根据Active Directoryvalidation用户名和密码?
我如何根据Active Directoryvalidation用户名和密码? 我只是想检查用户名和密码是否正确。
如果您使用.NET 3.5或更高版本,则可以使用System.DirectoryServices.AccountManagement
命名空间并轻松validation您的凭据:
// create a "principal context" - eg 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#托pipe代码在你的最终 – 你还能要求什么? 🙂
在这里阅读所有信息:
- pipe理.NET Framework 3.5中的目录安全主体
- System.DirectoryServices.AccountManagement上的MSDN文档
更新:
正如在这个其他SO问题(及其答案)中概述的, 这个调用可能返回一个用户的旧密码为True
的问题。 只要知道这种行为,不要太惊讶,如果发生这种情况:-)(感谢@MikeGledhill指出这一点!)
我们在我们的内联网上这样做
你必须使用System.DirectoryServices;
这里是代码的胆量
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(); } } }
这里提出的几个解决scheme缺乏区分错误的用户/密码和需要改变的密码的能力。 这可以通过以下方式完成:
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); } } } }
如果用户密码错误,或者用户不存在,则会包含错误
“8009030C:LdapErr:DSID-0C0904DC,注释:AcceptSecurityContext错误,数据52e,v1db1”,
如果用户密码需要更改,则会包含该密码
“8009030C:LdapErr:DSID-0C0904DC,注释:AcceptSecurityContext错误,数据773,v1db1”
lexc.ServerErrorMessage
数据值是Win32错误代码的hex表示forms。 这些错误代码将通过调用Win32 LogonUser API调用返回。 下面的列表总结了hex和十进制值的一系列常用值:
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)
使用DirectoryServices非常简单的解决scheme:
using System.DirectoryServices; //srvr = ldap server, eg 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; }
需要NativeObject访问来检测不良的用户/密码
不幸的是,没有“简单”的方式来检查AD上的用户凭证。
到目前为止所提出的每一种方法,你都可能得到一个假阴性:一个用户的信用将是有效的,但是AD在某些情况下将返回false:
- 用户需要在下次login时更改密码。
- 用户的密码已过期。
ActiveDirectory将不允许您使用LDAP来确定密码是否无效,因为用户必须更改密码或密码已过期。
要确定密码更改或密码已过期,您可以调用Win32:LogonUser(),并检查以下2个常量的Windows错误代码:
- ERROR_PASSWORD_MUST_CHANGE = 1907
- ERROR_PASSWORD_EXPIRED = 1330
可能最简单的方法是PInvoke LogonUser Win32 API.eg
MSDN参考这里…
绝对要使用logintypes
LOGON32_LOGON_NETWORK (3)
这只会创build一个轻量级令牌 – 非常适合AuthN检查。 (其他types可用于构build交互式会话等)
完整的.Net解决scheme是使用System.DirectoryServices名称空间中的类。 它们允许直接查询AD服务器。 这是一个小样本,可以做到这一点:
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
此代码使用提供的凭据直接连接到AD服务器。 如果证书无效,searcher.FindOne()将抛出一个exception。 ErrorCode是对应于“无效的用户名/密码”COM错误。
您不需要以AD用户身份运行代码。 事实上,我成功地使用它来查询AD服务器上的信息,从域外的客户端!
又一个.NET调用来快速validationLDAP凭证:
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 } }
试试这个代码(注:报告不能在Windows Server 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
除非您需要为“LogonException”创build您自己的自定义exception
如果你被困在.NET 2.0和托pipe代码中,这是另一种可以运行本地和域帐户的方式:
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; }
我的简单function
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; } }
Windows身份validation可能因各种原因失败:用户名或密码不正确,帐户被locking,密码过期等等。 要区分这些错误,请通过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需要与要validation的域名build立信任关系。