Jab*_*ezz 18 c# ldap active-directory
目前,我使用以下代码针对某些AD对用户进行身份验证:
DirectoryEntry entry = new DirectoryEntry(_path, username, pwd);
try
{
// Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry) { Filter = "(sAMAccountName=" + username + ")" };
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (result == null)
{
return false;
}
// Update the new path to the user in the directory
_path = result.Path;
_filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
Run Code Online (Sandbox Code Playgroud)
这非常适合验证用户名的密码.
问题在于总是返回一般错误"登录失败:未知用户名或密码错误".验证失败时
但是,当帐户被锁定时,身份验证也可能失败.
我怎么知道它是否因为被锁定而失败?
我遇到过你可以使用的文章:
Convert.ToBoolean(entry.InvokeGet("IsAccountLocked"))
Run Code Online (Sandbox Code Playgroud)
问题是,每当您尝试访问DirectoryEntry上的任何属性时,都会抛出相同的错误.
关于如何找到认证失败的实际原因的任何其他建议?(帐户被锁定/密码已过期/等)
我连接到的AD可能不一定是Windows服务器.
Sco*_*Bai 16
有点晚了,但我会把它扔出去.
如果您真的想确定帐户验证失败的具体原因(除了错误的密码,过期,锁定等等,还有更多原因),您可以使用Windows API LogonUser.不要被它吓倒 - 它比看起来容易.您只需调用LogonUser,如果失败,则查看Marshal.GetLastWin32Error(),它将为您提供返回代码,指示登录失败的(非常)具体原因.
但是,您无法在要进行身份验证的用户的上下文中调用此方法; 您将需要一个priveleged帐户 - 我认为该要求是SE_TCB_NAME(又名SeTcbPrivilege) - 一个有权"作为操作系统的一部分"的用户帐户.
//Your new authenticate code snippet:
try
{
if (!LogonUser(user, domain, pass, LogonTypes.Network, LogonProviders.Default, out token))
{
errorCode = Marshal.GetLastWin32Error();
success = false;
}
}
catch (Exception)
{
throw;
}
finally
{
CloseHandle(token);
}
success = true;
Run Code Online (Sandbox Code Playgroud)
如果它失败了,你会得到一个返回代码(还有更多你可以查找,但这些是重要的:
//See http://support.microsoft.com/kb/155012
const int ERROR_PASSWORD_MUST_CHANGE = 1907;
const int ERROR_LOGON_FAILURE = 1326;
const int ERROR_ACCOUNT_RESTRICTION = 1327;
const int ERROR_ACCOUNT_DISABLED = 1331;
const int ERROR_INVALID_LOGON_HOURS = 1328;
const int ERROR_NO_LOGON_SERVERS = 1311;
const int ERROR_INVALID_WORKSTATION = 1329;
const int ERROR_ACCOUNT_LOCKED_OUT = 1909; //It gives this error if the account is locked, REGARDLESS OF WHETHER VALID CREDENTIALS WERE PROVIDED!!!
const int ERROR_ACCOUNT_EXPIRED = 1793;
const int ERROR_PASSWORD_EXPIRED = 1330;
Run Code Online (Sandbox Code Playgroud)
其余的只是复制/粘贴以获取要传入的DLLImports和值
//here are enums
enum LogonTypes : uint
{
Interactive = 2,
Network =3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkCleartext = 8,
NewCredentials = 9
}
enum LogonProviders : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
//Paste these DLLImports
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonTypes logonType,
LogonProviders logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
Run Code Online (Sandbox Code Playgroud)
我知道这个答案已经晚了几年,但我们遇到了与原始海报相同的情况.不幸的是,在我们的环境中,我们无法使用LogonUser - 我们需要一个纯LDAP解决方案.事实证明,有一种方法可以从绑定操作中获取扩展错误代码.这有点难看,但它有效:
catch(DirectoryServicesCOMException exc)
{
if((uint)exc.ExtendedError == 0x80090308)
{
LDAPErrors errCode = 0;
try
{
// Unfortunately, the only place to get the LDAP bind error code is in the "data" field of the
// extended error message, which is in this format:
// 80090308: LdapErr: DSID-0C09030B, comment: AcceptSecurityContext error, data 52e, v893
if(!string.IsNullOrEmpty(exc.ExtendedErrorMessage))
{
Match match = Regex.Match(exc.ExtendedErrorMessage, @" data (?<errCode>[0-9A-Fa-f]+),");
if(match.Success)
{
string errCodeHex = match.Groups["errCode"].Value;
errCode = (LDAPErrors)Convert.ToInt32(errCodeHex, fromBase: 16);
}
}
}
catch { }
switch(errCode)
{
case LDAPErrors.ERROR_PASSWORD_EXPIRED:
case LDAPErrors.ERROR_PASSWORD_MUST_CHANGE:
throw new Exception("Your password has expired and must be changed.");
// Add any other special error handling here (account disabled, locked out, etc...).
}
}
// If the extended error handling doesn't work out, just throw the original exception.
throw;
}
Run Code Online (Sandbox Code Playgroud)
并且您需要错误代码的定义(http://www.lifeasbob.com/code/errorcodes.aspx中有更多这些定义):
private enum LDAPErrors
{
ERROR_INVALID_PASSWORD = 0x56,
ERROR_PASSWORD_RESTRICTION = 0x52D,
ERROR_LOGON_FAILURE = 0x52e,
ERROR_ACCOUNT_RESTRICTION = 0x52f,
ERROR_INVALID_LOGON_HOURS = 0x530,
ERROR_INVALID_WORKSTATION = 0x531,
ERROR_PASSWORD_EXPIRED = 0x532,
ERROR_ACCOUNT_DISABLED = 0x533,
ERROR_ACCOUNT_EXPIRED = 0x701,
ERROR_PASSWORD_MUST_CHANGE = 0x773,
ERROR_ACCOUNT_LOCKED_OUT = 0x775,
ERROR_ENTRY_EXISTS = 0x2071,
}
Run Code Online (Sandbox Code Playgroud)
我无法在其他地方找到这些信息 - 每个人都说你应该使用LogonUser.如果有更好的解决方案,我很乐意听到.如果没有,我希望这可以帮助其他无法调用LogonUser的人.