如何对活动目录验证用户名和密码?我只是想检查用户名和密码是否正确。


当前回答

我的简单功能

 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;
        }
    }

其他回答

不幸的是,没有“简单”的方法来检查AD上的用户凭据。

使用到目前为止所介绍的每一种方法,你可能会得到一个假阴性:用户的信用将是有效的,但AD在某些情况下将返回false:

用户需要在下次登录时更改密码。 用户密码已过期。

ActiveDirectory不允许您使用LDAP来确定密码是否由于用户必须更改密码或密码是否过期而无效。

要确定密码更改或密码过期,您可以调用Win32:LogonUser(),并检查以下2个常量的windows错误码:

Error_password_must_change = 1907 Error_password_expired = 1330

可能最简单的方法是PInvoke LogonUser Win32 api。

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

MSDN参考在这里…

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

绝对要使用登录类型

LOGON32_LOGON_NETWORK (3)

这只创建了一个轻量级的令牌-完美的认证检查。(其他类型可用于构建交互式会话等)

Windows身份验证失败的原因有很多:用户名或密码不正确、帐户被锁定、密码过期等等。为了区分这些错误,通过P/Invoke调用LogonUser API函数,如果函数返回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()
    }
}

示例用法:

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;
    }
}

注意:LogonUser需要与您验证的域建立信任关系。

我使用这个过程作为DLL登录到我们开发的其他应用程序… (我们目前正在使用OpenEdge Progress)

public static string AzureLogin(string user, string password) {

    string status;

    try {
        new DirectorySearcher(new DirectoryEntry("LDAP://yourdomain.com", user, password) {
            AuthenticationType = AuthenticationTypes.Secure,
            Username = user,
            Password = password
        })  {
            Filter = "(objectclass=user)"
        }.FindOne().Properties["displayname"][0].ToString();

        status = $"SUCCESS - User {user} has logged in.";

    } catch(System.Exception e) {
        status = $"ERROR - While logging in: {e}";
    }

    return status;
}

如果你使用的是。net 3.5或更新版本,你可以使用System.DirectoryServices.AccountManagement命名空间轻松验证你的凭证:

// 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");
}

它简单,可靠,在你的端是100%的c#托管代码——你还能要求什么呢?: -)

在这里阅读所有内容:

在.NET Framework 3.5中管理目录安全主体 System.DirectoryServices.AccountManagement上的MSDN文档

更新:

正如在另一个SO问题(及其答案)中概述的那样,对于用户的旧密码,这个调用可能会返回True。只是要注意这种行为,如果发生这种情况不要太惊讶:-)(感谢@MikeGledhill指出这一点!)