我有一个MVC应用程序需要登录并验证用户对抗Active Directory。我使用的是PrincipalContext.ValidateCredentials
方法,但总是获得false
的身份验证。
连接服务器很好。问题似乎发生在ValidateCredentials
。
这是我的代码:
public static bool IsAuthenticated(string domain, string username, string pwd) {
bool IsAuthenticated = false;
try {
PrincipalContext insPrincipalContext =
new PrincipalContext(ContextType.Domain, domain, "DC=c1w,DC=com");
username = "c1w\\" + username;
IsAuthenticated = insPrincipalContext.ValidateCredentials(username, pwd);
}
catch (Exception ex)
{
// Rethrow this exception
ExceptionPolicy.HandleException(ex, "Exception Policy");
}
return IsAuthenticated;
}
任何人都知道为什么会发生这种情况?
答案 0 :(得分:13)
以下是ValidateCredentials(string, string)
的工作原理:首先,它尝试使用Negotiate
,Signing
和Sealing
上下文选项进行身份验证。如果失败,则会再次尝试使用SimpleBind
和SecureSocketLayer
。
问题是NT4(AKA“遗留”,AKA“低级别名称”)格式(DOMAIN\UserName
,或更正确,NetBiosName\SamAccountName
)不适用于谈判。但它确实适用于SimpleBind。
那么在调用2参数ValidateCredentials()
方法时可能发生的事情是它首先使用Negotiate失败,因为它不喜欢NT4格式,然后在使用简单绑定时再次失败。
在我自己的测试中,我发现即使在回归使用简单绑定之后它失败的原因是它不仅使用SimpleBind。它使用SimpleBind
加SecureSocketLayer
。这意味着如果未正确设置Active Directory服务器以使用SSL(测试环境的常见方案),它仍将失败。
正如其中一条评论中所提到的那样,您永远不要单独使用SimpleBind
(不使用SecureSocketLayer
),否则您的密码将以纯文本形式通过网络发送。
在野外,我已经看到一些Active Directory系统根本不允许使用简单的绑定,所以你必须让它与Negotiate一起使用。
我找到了两种方法来处理这个问题:
1)如果所有内容都发生在同一个域中,您应该只能使用用户名(SAM帐户名称)来呼叫ValidateCredentials
,而忽略“DOMAIN”部分。然后,第一次与Negotiate一起正常工作。
2)如果域部分很重要,因为可能涉及多个域(即Domain1\UserA
和Domain2\UserA
是不同的人),那么它会变得有点复杂。在这种情况下,我最终做的是将NT4名称(DOMAIN \ User)转换为“用户主要名称”格式(例如LogonName@domain.com
)。有几种不同的方法可以做到这一点。最简单的可能是使用UserPrincipal.FindByIdentity()
的3参数重载,然后在结果上获取UserPrincipalName
属性的值。另一种方法是使用DirectorySearcher
和查询LDAP://domain
来查找具有匹配userPrincipalName
值的用户的sAMAccountName
属性。注意:只有涉及的所有域都在同一个林中时,此解决方案才有效。
答案 1 :(得分:9)
我没看到你在哪里初始化“pwd”变量 也许您应该在此方法中使用ContextOption来准确指定所需的行为。很抱歉,答案过于广泛,但您的问题中没有太多详细信息
答案 2 :(得分:1)
您似乎正在使用domain \ userName格式验证用户。您可能希望从userName解析域名并使用ValidateCredential。