Xác thực tên người dùng và mật khẩu đối với Active Directory?


526

Làm cách nào để xác thực tên người dùng và mật khẩu đối với Active Directory? Tôi chỉ đơn giản muốn kiểm tra xem tên người dùng và mật khẩu có đúng không.

Câu trả lời:


642

Nếu bạn làm việc trên .NET 3.5 trở lên, bạn có thể sử dụng System.DirectoryServices.AccountManagementkhông gian tên và dễ dàng xác minh thông tin đăng nhập của bạn:

// create a "principal context" - e.g. your domain (could be machine, too)
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
{
    // validate the credentials
    bool isValid = pc.ValidateCredentials("myuser", "mypassword");
}

Thật đơn giản, đáng tin cậy, đó là mã được quản lý 100% C # của bạn - bạn còn đòi hỏi gì hơn nữa? :-)

Đọc tất cả về nó ở đây:

Cập nhật:

Như đã nêu trong câu hỏi SO khác này (và câu trả lời của nó) , có một vấn đề với cuộc gọi này có thể trả về Truemật khẩu cũ của người dùng. Chỉ cần lưu ý về hành vi này và đừng quá ngạc nhiên nếu điều này xảy ra :-) (cảm ơn @MikeGledhill đã chỉ ra điều này!)


36
Trong miền của tôi, tôi phải chỉ định pc.ValidateCredentials ("myuser", "mypassword", ContextOptions.Negotiate) hoặc tôi sẽ nhận System.DirectoryService.Prot Protocol.DirectoryOperationException: Máy chủ không thể xử lý các yêu cầu thư mục.
Alex Peck

12
Nếu mật khẩu hết hạn hoặc tài khoản bị vô hiệu hóa, thì ValidateCredentials sẽ trả về sai. Thật đáng tiếc , nó không cho bạn biết lý do tại sao nó bị trả về sai (điều này thật đáng tiếc vì điều đó có nghĩa là tôi không thể làm điều gì đó hợp lý như chuyển hướng người dùng thay đổi mật khẩu của họ).
Chris J

64
Ngoài ra, hãy cẩn thận với tài khoản 'Khách' - nếu tài khoản Khách ở cấp tên miền được bật, ValidateCredentials trả về đúng nếu bạn cung cấp cho người dùng không tồn tại . Do đó, bạn có thể muốn gọi UserPrinciple.FindByIdentityđể xem liệu ID người dùng đã qua có tồn tại trước hay không.
Chris J

7
@AlexPeck: lý do tại sao bạn phải làm điều này (như tôi) là .NET sử dụng các công nghệ sau theo mặc định: LDAP + SSL, Kerberos, sau đó là RPC. Tôi nghi ngờ RPC bị tắt trong mạng của bạn (tốt!) Và Kerberos không thực sự được sử dụng bởi .NET trừ khi bạn nói rõ ràng bằng cách sử dụng nó ContextOptions.Negotiate.
Brett Veenstra

5
Xin lưu ý rằng nếu người dùng THAY ĐỔI mật khẩu Active Directory của họ, đoạn mã này sẽ tiếp tục vui vẻ xác thực người dùng bằng mật khẩu AD cũ của họ. Yup, thực sự. Hãy đọc tại đây: stackoverflow.com/questions/8949501/ Kẻ
Mike Gledhill

70

Chúng tôi làm điều này trên mạng nội bộ của chúng tôi

Bạn phải sử dụng System.DirectoryService;

Đây là can đảm của mã

using (DirectoryEntry adsEntry = new DirectoryEntry(path, strAccountId, strPassword))
{
    using (DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry))
    {
        //adsSearcher.Filter = "(&(objectClass=user)(objectCategory=person))";
        adsSearcher.Filter = "(sAMAccountName=" + strAccountId + ")";

        try
        {
            SearchResult adsSearchResult = adsSearcher.FindOne();
            bSucceeded = true;

            strAuthenticatedBy = "Active Directory";
            strError = "User has been authenticated by Active Directory.";
        }
        catch (Exception ex)
        {
            // Failed to authenticate. Most likely it is caused by unknown user
            // id or bad strPassword.
            strError = ex.Message;
        }
        finally
        {
            adsEntry.Close();
        }
    }
}

9
Những gì bạn đặt trong "con đường"? Tên miền? Tên của máy chủ? Đường dẫn LDAP đến miền? Đường dẫn LDAP đến máy chủ?
Ian Boyd

3
Trả lời1: Không, chúng tôi chạy nó dưới dạng dịch vụ web để có thể gọi nó từ nhiều vị trí trong ứng dụng web chính. Trả lời2: Đường dẫn chứa thông tin LDAP ... LDAP: // DC = domainname1, DC = domainname2, DC = com
ĂnPhilanderer

3
Dường như điều này có thể cho phép tiêm LDAP. Bạn có thể muốn đảm bảo thoát hoặc xóa bất kỳ dấu ngoặc đơn nào trong strAccountId
Brain2000

Điều này có nghĩa strPasswordlà được lưu trữ trong LDAP trong văn bản thuần túy?
Matt Kocaj

15
Không bao giờ cần phải gọi một cách rõ ràng Close()về một usingbiến.
Nyerguds

62

Một số giải pháp được trình bày ở đây thiếu khả năng phân biệt giữa người dùng / mật khẩu sai và mật khẩu cần được thay đổi. Điều đó có thể được thực hiện theo cách sau:

using System;
using System.DirectoryServices.Protocols;
using System.Net;

namespace ProtocolTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                LdapConnection connection = new LdapConnection("ldap.fabrikam.com");
                NetworkCredential credential = new NetworkCredential("user", "password");
                connection.Credential = credential;
                connection.Bind();
                Console.WriteLine("logged in");
            }
            catch (LdapException lexc)
            {
                String error = lexc.ServerErrorMessage;
                Console.WriteLine(lexc);
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc);
            }
        }
    }
}

Nếu mật khẩu người dùng sai hoặc người dùng không tồn tại, lỗi sẽ chứa

"8009030C: LdapErr: DSID-0C0904DC, nhận xét: AcceptSecurityContext lỗi, dữ liệu 52e, v1db1",

nếu mật khẩu người dùng cần thay đổi, nó sẽ chứa

"8009030C: LdapErr: DSID-0C0904DC, nhận xét: AcceptSecurityContext lỗi, dữ liệu 773, v1db1"

Các lexc.ServerErrorMessage giá trị dữ liệu là một đại diện hex của Error Code Win32. Đây là các mã lỗi tương tự sẽ được trả về bằng cách gọi lệnh gọi Win32 LogonUser API. Danh sách dưới đây tóm tắt một loạt các giá trị phổ biến với các giá trị thập phân và thập phân:

525 user not found ​(1317)
52e invalid credentials ​(1326)
530 not permitted to logon at this time (1328)
531 not permitted to logon at this workstation (1329)
532 password expired ​(1330)
533 account disabled ​(1331) 
701 account expired ​(1793)
773 user must reset password (1907)
775 user account locked (1909)

2
Thật không may, một số cài đặt AD không trả về mã lỗi phụ LDAP, điều đó có nghĩa là giải pháp này sẽ không hoạt động.
Søren Mors

4
Đừng quên thêm một số tài liệu tham khảo cho dự án: System.DirectoryServicesSystem.DirectoryServices.Protocols
TomXP411

3
Tuy nhiên, câu hỏi của tôi là: làm thế nào để bạn có được tên máy chủ LDAP? Nếu bạn đang viết một ứng dụng di động, bạn không thể mong muốn người dùng biết hoặc cần cung cấp tên của các máy chủ AD trên mỗi mạng.
TomXP411

1
Tôi có người dùng bị hạn chế đăng nhập vào các máy trạm cụ thể; Làm cách nào để chỉ định máy trạm mà tôi đang thử đăng nhập? (máy
trạm1

1
Tôi cảm thấy kỳ lạ vì tôi không nghĩ rằng bạn đang nhận đủ tín dụng. Đây là một phương pháp được quản lý hoàn toàn mà không gặp sự cố với lệnh gọi API Win32 để xác định xem "người dùng phải đặt lại mật khẩu" mà rõ ràng không có câu trả lời nào khác đạt được. Có bất kỳ kẽ hở trong phương pháp này gây ra tỷ lệ đánh giá thấp của nó? hmm ...
Lionet Chen

34

giải pháp rất đơn giản bằng cách sử dụng DirectoryService:

using System.DirectoryServices;

//srvr = ldap server, e.g. LDAP://domain.com
//usr = user name
//pwd = user password
public bool IsAuthenticated(string srvr, string usr, string pwd)
{
    bool authenticated = false;

    try
    {
        DirectoryEntry entry = new DirectoryEntry(srvr, usr, pwd);
        object nativeObject = entry.NativeObject;
        authenticated = true;
    }
    catch (DirectoryServicesCOMException cex)
    {
        //not authenticated; reason why is in cex
    }
    catch (Exception ex)
    {
        //not authenticated due to some other exception [this is optional]
    }

    return authenticated;
}

cần có quyền truy cập NativeObject để phát hiện người dùng / mật khẩu xấu


4
Mã này là xấu vì nó cũng đang thực hiện kiểm tra ủy quyền (kiểm tra xem người dùng có được phép đọc thông tin thư mục hoạt động không). Tên người dùng và mật khẩu có thể hợp lệ, nhưng người dùng không được phép đọc thông tin - và có một ngoại lệ. Nói cách khác, bạn có thể có tên người dùng và mật khẩu hợp lệ, nhưng vẫn có ngoại lệ.
Ian Boyd

2
tôi thực sự đang trong quá trình yêu cầu tương đương với nguồn gốcPrincipleContext - chỉ tồn tại trong .NET 3.5. Nhưng nếu bạn đang sử dụng .NET 3.5 hoặc mới hơn, bạn nên sử dụngPrincipleContext
Ian Boyd

28

Thật không may, không có cách "đơn giản" nào để kiểm tra thông tin đăng nhập của người dùng trên AD.

Với mọi phương thức được trình bày cho đến nay, bạn có thể nhận được âm tính giả: Tín dụng của người dùng sẽ hợp lệ, tuy nhiên AD sẽ trả về sai trong một số trường hợp:

  • Người dùng được yêu cầu thay đổi mật khẩu tại lần đăng nhập tiếp theo.
  • Mật khẩu của người dùng đã hết hạn.

ActiveDirectory sẽ không cho phép bạn sử dụng LDAP để xác định xem mật khẩu không hợp lệ do người dùng phải thay đổi mật khẩu hoặc nếu mật khẩu của họ đã hết hạn.

Để xác định thay đổi mật khẩu hoặc mật khẩu đã hết hạn, bạn có thể gọi Win32: LogonUser () và kiểm tra mã lỗi của windows cho 2 hằng số sau:

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330

1
Tôi có thể hỏi nơi bạn có các định nghĩa cho Hết hạn và Must_Change ... Tìm thấy chúng ở đâu ngoài đây :)
mabstrei

1
Từ một bài viết của MSDN: msdn.microsoft.com/en-us/l Library
Alan

Cảm ơn. Tôi đã cố gắng tìm ra cách xác nhận của tôi đã trở lại sai mọi lúc. Đó là bởi vì người dùng cần thay đổi mật khẩu của mình.
Deise Vicentin

22

Có lẽ cách dễ nhất là PInvoke LogonUser Win32 API.eg

http://www.pinvoke.net/default.aspx/advapi32/LogonUser.html

Tham khảo MSDN tại đây ...

http://msdn.microsoft.com/en-us/l Library / aa378184.aspx

Chắc chắn muốn sử dụng loại đăng nhập

LOGON32_LOGON_NETWORK (3)

Điều này chỉ tạo ra một mã thông báo nhẹ - hoàn hảo cho kiểm tra AuthN. (các loại khác có thể được sử dụng để xây dựng các phiên tương tác, v.v.)


Như @Alan chỉ ra, API LogonUser có nhiều đặc điểm hữu ích ngoài lệnh gọi System.DirectoryService.
stephbu

3
@cciotti: Không, điều đó sai. Cách TỐT NHẤT để xác thực chính xác một ai đó là sử dụng LogonUserAPI như @stephbu viết. Tất cả các phương pháp khác được mô tả trong bài viết này sẽ KHÔNG LÀM VIỆC 100%. Tuy nhiên, chỉ là một lưu ý, tôi tin rằng bạn phải tham gia inorder để gọi LogonUser.
Alan

@Alan để tạo thông tin đăng nhập bạn phải có khả năng kết nối với tên miền bằng cách gửi tài khoản miền hợp lệ. Tuy nhiên tôi khá chắc chắn rằng máy của bạn không nhất thiết phải là thành viên của miền.
stephbu

2
Các LogonUserAPI yêu cầu người dùng phải có luật như là một phần của hệ điều hành privelage; đó không phải là thứ mà người dùng nhận được - và không phải thứ bạn muốn cấp cho mọi người dùng trong tổ chức. ( msdn.microsoft.com/en-us/l Library / aa378184 ( v = vs85 ) .aspx )
Ian Boyd

1
LogonUser chỉ cần đóng vai trò là một phần của hệ điều hành cho Windows 2000 trở xuống theo support.microsoft.com/kb/180548 ... Nó có vẻ sạch cho Server 2003 trở lên.
Chris J

18

Một giải pháp .Net đầy đủ là sử dụng các lớp từ không gian tên System.DirectoryService. Họ cho phép truy vấn trực tiếp một máy chủ AD. Đây là một mẫu nhỏ sẽ làm điều này:

using (DirectoryEntry entry = new DirectoryEntry())
{
    entry.Username = "here goes the username you want to validate";
    entry.Password = "here goes the password";

    DirectorySearcher searcher = new DirectorySearcher(entry);

    searcher.Filter = "(objectclass=user)";

    try
    {
        searcher.FindOne();
    }
    catch (COMException ex)
    {
        if (ex.ErrorCode == -2147023570)
        {
            // Login or password is incorrect
        }
    }
}

// FindOne() didn't throw, the credentials are correct

Mã này kết nối trực tiếp với máy chủ AD, sử dụng thông tin đăng nhập được cung cấp. Nếu thông tin đăng nhập không hợp lệ, searcher.FindOne () sẽ đưa ra một ngoại lệ. ErrorCode là một lỗi tương ứng với lỗi COM "tên người dùng / mật khẩu không hợp lệ".

Bạn không cần phải chạy mã với tư cách là người dùng AD. Trên thực tế, tôi thành công sử dụng nó để truy vấn thông tin trên máy chủ AD, từ một khách hàng bên ngoài miền!


Làm thế nào về các loại xác thực? Tôi nghĩ rằng bạn đã quên nó trong mã của bạn ở trên. :-) theo mặc định DirectoryEntry.AuthenticationType được đặt thành Bảo mật phải không? mã đó sẽ không hoạt động trên các LDAP không được bảo mật (Có thể ẩn danh hoặc Không có gì). tôi có đúng với điều này không?
jerbersoft

Mặt trái của việc truy vấn máy chủ AD là bạn có quyền truy vấn máy chủ AD. Thông tin đăng nhập của bạn có thể hợp lệ, nhưng nếu bạn không có quyền truy vấn AD, thì bạn sẽ gặp lỗi. Đó là lý do tại sao cái gọi là Fast Bind được tạo ra; bạn xác nhận thông tin đăng nhập mà không cho phép khả năng của người dùng để làm điều gì đó.
Ian Boyd

2
điều này sẽ không cho phép bất cứ ai vượt qua trong trường hợp COMException bị ném vì bất kỳ lý do nào khác trước khi thông tin đăng nhập được kiểm tra?
Stefan Paul Noack

11

Một cuộc gọi .NET khác để nhanh chóng xác thực thông tin đăng nhập LDAP:

using System.DirectoryServices;

using(var DE = new DirectoryEntry(path, username, password)
{
    try
    {
        DE.RefreshCache(); // This will force credentials validation
    }
    catch (COMException ex)
    {
        // Validation failed - handle how you want
    }
}

Đây là giải pháp duy nhất hiệu quả với tôi, sử dụng PrincipalContext không hiệu quả với tôi.
Daniel

PrincipalContext không hợp lệ cho kết nối LDAP an toàn (còn gọi là LDAPS, sử dụng cổng 636
Kiquenet

10

Hãy thử mã này (LƯU Ý: Được báo cáo là không hoạt động trên máy chủ windows 2000)

#region NTLogonUser
#region Direct OS LogonUser Code
[DllImport( "advapi32.dll")]
private static extern bool LogonUser(String lpszUsername, 
    String lpszDomain, String lpszPassword, int dwLogonType, 
    int dwLogonProvider, out int phToken);

[DllImport("Kernel32.dll")]
private static extern int GetLastError();

public static bool LogOnXP(String sDomain, String sUser, String sPassword)
{
   int token1, ret;
   int attmpts = 0;

   bool LoggedOn = false;

   while (!LoggedOn && attmpts < 2)
   {
      LoggedOn= LogonUser(sUser, sDomain, sPassword, 3, 0, out token1);
      if (LoggedOn) return (true);
      else
      {
         switch (ret = GetLastError())
         {
            case (126): ; 
               if (attmpts++ > 2)
                  throw new LogonException(
                      "Specified module could not be found. error code: " + 
                      ret.ToString());
               break;

            case (1314): 
               throw new LogonException(
                  "Specified module could not be found. error code: " + 
                      ret.ToString());

            case (1326): 
               // edited out based on comment
               //  throw new LogonException(
               //   "Unknown user name or bad password.");
            return false;

            default: 
               throw new LogonException(
                  "Unexpected Logon Failure. Contact Administrator");
              }
          }
       }
   return(false);
}
#endregion Direct Logon Code
#endregion NTLogonUser

ngoại trừ bạn sẽ cần tạo ngoại lệ tùy chỉnh của riêng mình cho "LogonException"


Không sử dụng xử lý ngoại lệ để trả về thông tin từ một phương thức. "Tên người dùng không xác định hoặc mật khẩu xấu" không phải là ngoại lệ, đó là hành vi tiêu chuẩn cho LogonUser. Chỉ cần trả lại sai.
Treb

vâng ... đây là một cổng từ thư viện VB6 cũ ... được viết năm 2003 hoặc lâu hơn ... (khi .Net lần đầu tiên xuất hiện)
Charles Bretana

Nếu chạy trên Windows 2000, mã này sẽ không hoạt động ( support.microsoft.com/kb/180548 )
Ian Boyd

1
Suy nghĩ lại về điều này. Đăng nhập hành vi dự kiến ​​của người dùng, mục đích của nó là đăng nhập người dùng . Nếu nó không thực hiện được nhiệm vụ đó, thì đó một ngoại lệ. Trong thực tế, phương thức sẽ trả về void, không phải là Boolean. Ngoài ra, nếu bạn vừa trả lại Boolean, người tiêu dùng của phương thức không có cách nào để thông báo cho người dùng lý do thất bại là gì.
Charles Bretana

5

Nếu bạn bị mắc kẹt với .NET 2.0 và mã được quản lý, đây là một cách khác hoạt động với tài khoản miền và tài khoản miền:

using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Diagnostics;

static public bool Validate(string domain, string username, string password)
{
    try
    {
        Process proc = new Process();
        proc.StartInfo = new ProcessStartInfo()
        {
            FileName = "no_matter.xyz",
            CreateNoWindow = true,
            WindowStyle = ProcessWindowStyle.Hidden,
            WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            RedirectStandardInput = true,
            LoadUserProfile = true,
            Domain = String.IsNullOrEmpty(domain) ? "" : domain,
            UserName = username,
            Password = Credentials.ToSecureString(password)
        };
        proc.Start();
        proc.WaitForExit();
    }
    catch (System.ComponentModel.Win32Exception ex)
    {
        switch (ex.NativeErrorCode)
        {
            case 1326: return false;
            case 2: return true;
            default: throw ex;
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }

    return false;
}   

Hoạt động tốt với các tài khoản cục bộ của máy, anh ta khởi chạy tập lệnh
eka808

BTW, phương pháp này là cần thiết để làm cho công trình này hoạt động tĩnh SecureString ToSecureString (chuỗi PwString) {char [] PasswordChars = PwString.ToCharArray (); Mật khẩu SecureString = new SecureString (); foreach (char c trong PasswordChars) Mật khẩu.AppendChar (c); ProcessStartInfo foo = new ProcessStartInfo (); foo.Password = Mật khẩu; trả về foo.Password; }
eka808

Ngược lại, dù sao đi nữa, bạn nên sử dụng SecureString cho mật khẩu. Mật khẩu WPF hỗ trợ nó.
Stephen Drew

5

Xác thực Windows có thể không thành công vì nhiều lý do: tên người dùng hoặc mật khẩu không chính xác, tài khoản bị khóa, mật khẩu đã hết hạn và hơn thế nữa. Để phân biệt giữa các lỗi này, hãy gọi hàm API LogonUser qua P / Gọi và kiểm tra mã lỗi nếu hàm trả về false:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

public static class Win32Authentication
{
    private class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle() // called by P/Invoke
            : base(true)
        {
        }

        protected override bool ReleaseHandle()
        {
            return CloseHandle(this.handle);
        }
    }

    private enum LogonType : uint
    {
        Network = 3, // LOGON32_LOGON_NETWORK
    }

    private enum LogonProvider : uint
    {
        WinNT50 = 3, // LOGON32_PROVIDER_WINNT50
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(
        string userName, string domain, string password,
        LogonType logonType, LogonProvider logonProvider,
        out SafeTokenHandle token);

    public static void AuthenticateUser(string userName, string password)
    {
        string domain = null;
        string[] parts = userName.Split('\\');
        if (parts.Length == 2)
        {
            domain = parts[0];
            userName = parts[1];
        }

        SafeTokenHandle token;
        if (LogonUser(userName, domain, password, LogonType.Network, LogonProvider.WinNT50, out token))
            token.Dispose();
        else
            throw new Win32Exception(); // calls Marshal.GetLastWin32Error()
    }
}

Sử dụng mẫu:

try
{
    Win32Authentication.AuthenticateUser("EXAMPLE\\user", "P@ssw0rd");
    // Or: Win32Authentication.AuthenticateUser("user@example.com", "P@ssw0rd");
}
catch (Win32Exception ex)
{
    switch (ex.NativeErrorCode)
    {
        case 1326: // ERROR_LOGON_FAILURE (incorrect user name or password)
            // ...
        case 1327: // ERROR_ACCOUNT_RESTRICTION
            // ...
        case 1330: // ERROR_PASSWORD_EXPIRED
            // ...
        case 1331: // ERROR_ACCOUNT_DISABLED
            // ...
        case 1907: // ERROR_PASSWORD_MUST_CHANGE
            // ...
        case 1909: // ERROR_ACCOUNT_LOCKED_OUT
            // ...
        default: // Other
            break;
    }
}

Lưu ý: LogonUser yêu cầu mối quan hệ tin cậy với tên miền bạn đang xác thực.


bạn có thể giải thích tại sao câu trả lời của bạn tốt hơn câu trả lời được bình chọn cao nhất không?
Mohammad Ali

1
@MohammadAli: Nếu bạn cần biết lý do tại sao xác thực thông tin không thành công (thông tin không chính xác, tài khoản bị khóa, mật khẩu hết hạn, v.v.), chức năng API LogonUser sẽ cho bạn biết. Ngược lại, phương thức PrincipalContext.ValidateCredentials (theo nhận xét về câu trả lời của marc_s) sẽ không; nó trả về false trong tất cả các trường hợp này Mặt khác, LogonUser yêu cầu mối quan hệ tin cậy với tên miền, nhưng PrincipalContext.ValidateCredentials (tôi nghĩ) thì không.
Michael Liu

2

Chức năng đơn giản của tôi

 private bool IsValidActiveDirectoryUser(string activeDirectoryServerDomain, string username, string password)
    {
        try
        {
            DirectoryEntry de = new DirectoryEntry("LDAP://" + activeDirectoryServerDomain, username + "@" + activeDirectoryServerDomain, password, AuthenticationTypes.Secure);
            DirectorySearcher ds = new DirectorySearcher(de);
            ds.FindOne();
            return true;
        }
        catch //(Exception ex)
        {
            return false;
        }
    }

1

Đây là giải pháp xác thực hoàn chỉnh của tôi để bạn tham khảo.

Đầu tiên, thêm bốn tài liệu tham khảo sau

 using System.DirectoryServices;
 using System.DirectoryServices.Protocols;
 using System.DirectoryServices.AccountManagement;
 using System.Net; 

private void AuthUser() { 


      try{
            string Uid = "USER_NAME";
            string Pass = "PASSWORD";
            if (Uid == "")
            {
                MessageBox.Show("Username cannot be null");
            }
            else if (Pass == "")
            {
                MessageBox.Show("Password cannot be null");
            }
            else
            {
                LdapConnection connection = new LdapConnection("YOUR DOMAIN");
                NetworkCredential credential = new NetworkCredential(Uid, Pass);
                connection.Credential = credential;
                connection.Bind();

                // after authenticate Loading user details to data table
                PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
                UserPrincipal user = UserPrincipal.FindByIdentity(ctx, Uid);
                DirectoryEntry up_User = (DirectoryEntry)user.GetUnderlyingObject();
                DirectorySearcher deSearch = new DirectorySearcher(up_User);
                SearchResultCollection results = deSearch.FindAll();
                ResultPropertyCollection rpc = results[0].Properties;
                DataTable dt = new DataTable();
                DataRow toInsert = dt.NewRow();
                dt.Rows.InsertAt(toInsert, 0);

                foreach (string rp in rpc.PropertyNames)
                {
                    if (rpc[rp][0].ToString() != "System.Byte[]")
                    {
                        dt.Columns.Add(rp.ToString(), typeof(System.String));

                        foreach (DataRow row in dt.Rows)
                        {
                            row[rp.ToString()] = rpc[rp][0].ToString();
                        }

                    }  
                }
             //You can load data to grid view and see for reference only
                 dataGridView1.DataSource = dt;


            }
        } //Error Handling part
        catch (LdapException lexc)
        {
            String error = lexc.ServerErrorMessage;
            string pp = error.Substring(76, 4);
            string ppp = pp.Trim();

            if ("52e" == ppp)
            {
                MessageBox.Show("Invalid Username or password, contact ADA Team");
            }
            if ("775​" == ppp)
            {
                MessageBox.Show("User account locked, contact ADA Team");
            }
            if ("525​" == ppp)
            {
                MessageBox.Show("User not found, contact ADA Team");
            }
            if ("530" == ppp)
            {
                MessageBox.Show("Not permitted to logon at this time, contact ADA Team");
            }
            if ("531" == ppp)
            {
                MessageBox.Show("Not permitted to logon at this workstation, contact ADA Team");
            }
            if ("532" == ppp)
            {
                MessageBox.Show("Password expired, contact ADA Team");
            }
            if ("533​" == ppp)
            {
                MessageBox.Show("Account disabled, contact ADA Team");
            }
            if ("533​" == ppp)
            {
                MessageBox.Show("Account disabled, contact ADA Team");
            }



        } //common error handling
        catch (Exception exc)
        {
            MessageBox.Show("Invalid Username or password, contact ADA Team");

        }

        finally {
            tbUID.Text = "";
            tbPass.Text = "";

        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.