C# 针对 Active Directory 验证用户名和密码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/290548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Validate a username and password against Active Directory?
提问by
How can I validate a username and password against Active Directory? I simply want to check if a username and password are correct.
如何根据 Active Directory 验证用户名和密码?我只想检查用户名和密码是否正确。
回答by stephbu
Probably easiest way is to PInvoke LogonUser Win32 API.e.g.
可能最简单的方法是 PInvoke LogonUser Win32 API.eg
MSDN Reference here...
MSDN 参考这里...
Definitely want to use logon type
肯定要使用登录类型
LOGON32_LOGON_NETWORK (3)
This creates a lightweight token only - perfect for AuthN checks. (other types can be used to build interactive sessions etc.)
这仅创建一个轻量级令牌 - 非常适合 AuthN 检查。(其他类型可用于构建交互式会话等)
回答by Charles Bretana
Try this code (NOTE: Reported to not work on windows server 2000)
试试这个代码(注意:报告在 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
except you'll need to create your own custom exception for "LogonException"
除非您需要为“LogonException”创建自己的自定义异常
回答by DiningPhilanderer
We do this on our Intranet
我们在我们的 Intranet 上执行此操作
You have to use System.DirectoryServices;
您必须使用 System.DirectoryServices;
Here are the guts of the code
这是代码的核心
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();
}
}
}
回答by Steven A. Lowe
very simple solution using DirectoryServices:
使用 DirectoryServices 的非常简单的解决方案:
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;
}
the NativeObject access is required to detect a bad user/password
需要 NativeObject 访问来检测错误的用户/密码
回答by Mathieu Garstecki
A full .Net solution is to use the classes from the System.DirectoryServices namespace. They allow to query an AD server directly. Here is a small sample that would do this:
完整的 .Net 解决方案是使用 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
This code directly connects to the AD server, using the credentials provided. If the credentials are invalid, searcher.FindOne() will throw an exception. The ErrorCode is the one corresponding to the "invalid username/password" COM error.
此代码使用提供的凭据直接连接到 AD 服务器。如果凭据无效,searcher.FindOne() 将抛出异常。ErrorCode 是与“无效的用户名/密码”COM 错误对应的代码。
You don't need to run the code as an AD user. In fact, I succesfully use it to query informations on an AD server, from a client outside the domain !
您不需要以 AD 用户身份运行代码。事实上,我成功地使用它从域外的客户端查询 AD 服务器上的信息!
回答by Alan
Unfortunately there is no "simple" way to check a users credentials on AD.
不幸的是,没有“简单”的方法来检查 AD 上的用户凭据。
With every method presented so far, you may get a false-negative: A user's creds will be valid, however AD will return false under certain circumstances:
使用到目前为止介绍的每种方法,您可能会得到一个假阴性:用户的凭据将是有效的,但是 AD 在某些情况下会返回 false:
- User is required to Change Password at Next Logon.
- User's password has expired.
- 用户需要在下次登录时更改密码。
- 用户的密码已过期。
ActiveDirectory will not allow you to use LDAP to determine if a password is invalid due to the fact that a user must change password or if their password has expired.
ActiveDirectory 不允许您使用 LDAP 来确定密码是否因用户必须更改密码或密码已过期而无效。
To determine password change or password expired, you may call Win32:LogonUser(), and check the windows error code for the following 2 constants:
要确定密码更改或密码过期,您可以调用 Win32:LogonUser(),并检查以下 2 个常量的 Windows 错误代码:
- ERROR_PASSWORD_MUST_CHANGE = 1907
- ERROR_PASSWORD_EXPIRED = 1330
- ERROR_PASSWORD_MUST_CHANGE = 1907
- ERROR_PASSWORD_EXPIRED = 1330
回答by marc_s
If you work on .NET 3.5 or newer, you can use the System.DirectoryServices.AccountManagement
namespace and easily verify your credentials:
如果您使用 .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");
}
It's simple, it's reliable, it's 100% C# managed code on your end - what more can you ask for? :-)
它很简单,很可靠,它是 100% C# 托管的代码——你还能要求什么?:-)
Read all about it here:
在这里阅读所有相关信息:
- Managing Directory Security Principals in the .NET Framework 3.5
- MSDN docs on System.DirectoryServices.AccountManagement
Update:
更新:
As outlined in this other SO question (and its answers), there is an issue with this call possibly returning True
for old passwords of a user. Just be aware of this behavior and don't be too surprised if this happens :-) (thanks to @MikeGledhill for pointing this out!)
正如this other SO question(及其答案)中所述,此调用存在问题,可能返回True
用户的旧密码。请注意这种行为,如果发生这种情况不要太惊讶:-)(感谢@MikeGledhill 指出这一点!)
回答by chauwel
If you are stuck with .NET 2.0 and managed code, here is another way that works whith local and domain accounts:
如果您坚持使用 .NET 2.0 和托管代码,这里是另一种适用于本地和域帐户的方法:
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;
}
回答by palswim
Yet another .NET call to quickly authenticate LDAP credentials:
另一个用于快速验证 LDAP 凭据的 .NET 调用:
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
}
}
回答by S?ren Mors
Several solutions presented here lack the ability to differentiate between a wrong user / password, and a password that needs to be changed. That can be done in the following way:
此处介绍的几种解决方案无法区分错误的用户/密码和需要更改的密码。这可以通过以下方式完成:
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);
}
}
}
}
If the users password is wrong, or the user doesn't exists, error will contain
如果用户密码错误,或者用户不存在,错误将包含
"8009030C: LdapErr: DSID-0C0904DC, comment: AcceptSecurityContext error, data 52e, v1db1",
“8009030C:LdapErr:DSID-0C0904DC,注释:AcceptSecurityContext 错误,数据 52e,v1db1”,
if the users password needs to be changed, it will contain
如果用户密码需要更改,它将包含
"8009030C: LdapErr: DSID-0C0904DC, comment: AcceptSecurityContext error, data 773, v1db1"
“8009030C:LdapErr:DSID-0C0904DC,注释:AcceptSecurityContext 错误,数据 773,v1db1”
The lexc.ServerErrorMessage
data value is a hex representation of the Win32 Error Code. These are the same error codes which would be returned by otherwise invoking the Win32 LogonUser API call. The list below summarizes a range of common values with hex and decimal values:
的lexc.ServerErrorMessage
数据值是Win32错误代码的十六进制表示。这些是通过调用 Win32 LogonUser API 调用返回的相同错误代码。下面的列表总结了一系列具有十六进制和十进制值的常见值:
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)