LdapConnection 与 PrincipalContext
Posted
技术标签:
【中文标题】LdapConnection 与 PrincipalContext【英文标题】:LdapConnection vs. PrincipalContext 【发布时间】:2013-11-04 22:27:39 【问题描述】:我有以下两种使用 LDAP 和 LDAPS 对用户进行身份验证的实现,我想知道哪个更好/更正确。作为记录,这些都适用于 SSL 和非 SSL 连接。
我也很好奇,因为在Non-SSL PrincipalContext
版本上使用 Wireshark 观看时,我仍然看到端口 636 上的流量。在四种组合中(Non-SSL LdapConnection
、SSL LdapConnection
、Non-SSL PrincipalContext
、SSL PrincipalContext
)它是唯一一个在端口 389 和 636 上都有流量,而不仅仅是一个或另一个。这可能是什么原因造成的?
LDAP 连接方式:
bool userAuthenticated = false;
var domainName = DomainName;
if (useSSL)
domainName = domainName + ":636";
try
using (var ldap = new LdapConnection(domainName))
var networkCredential = new NetworkCredential(username, password, domainName);
ldap.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback((con, cer) => true);
ldap.SessionOptions.SecureSocketLayer = useSSL;
ldap.SessionOptions.ProtocolVersion = 3;
ldap.AuthType = AuthType.Negotiate;
ldap.Bind(networkCredential);
// If the bind succeeds, we have a valid user/pass.
userAuthenticated = true;
catch (LdapException ldapEx)
// Error Code 0x31 signifies invalid credentials, anything else will be caught outside.
if (!ldapEx.ErrorCode.Equals(0x31))
throw;
return userAuthenticated;
PrincipalContext 方法:
bool userAuthenticated = false;
var domainName = DomainName;
if (useSSL)
domainName = domainName + ":636";
ContextOptions options = ContextOptions.SimpleBind | ContextOptions.SecureSocketLayer;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domainName, null, options))
userAuthenticated = pc.ValidateCredentials(username, password, options);
else
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domainName))
userAuthenticated = pc.ValidateCredentials(username, password);
return userAuthenticated;
【问题讨论】:
您尝试过使用@sindilevich 答案吗?有什么解决办法吗? 【参考方案1】:@DTI-Matt,在上面的示例中,您使用了始终返回 true
的 VerifyServerCertificate
回调。这基本上违背了通过 SSL 连接到 LDAP 的目的,因为没有执行真正的证书检查。
虽然您可以使用 X509Chain
和/或 X509Certificate2
类实现真正的证书检查,但似乎 PrincipalContext
会为您处理检查。
总而言之,LdapConnection
和PrincipalContext
都提供了非常相似的功能,即通过普通或 SSL 连接连接到 LDAP 服务器。您必须提供LdapConnection
更多的手写代码才能使其正常工作。另一方面,PrincipalContext
为您提供了相同的功能,而您需要手动编写更少的代码。
请注意,通过非 SSL PrincipalContext
连接到端口 636(您的默认 LDAP over SSL 端口)可能是因为此类尝试尽可能安全地连接。
【讨论】:
【参考方案2】:这就是我们最终在 SSL/Non-SSL 上工作的结果。
public bool UserValid(string username, string password, bool useSSL)
bool userAuthenticated = false;
var domainName = DomainName;
if (useSSL)
domainName = domainName + ":636";
try
using (var ldap = new LdapConnection(domainName))
var networkCredential = new NetworkCredential(username, password, DomainName); // Uses DomainName without the ":636" at all times, SSL or not.
ldap.SessionOptions.VerifyServerCertificate += VerifyServerCertificate;
ldap.SessionOptions.SecureSocketLayer = useSSL;
ldap.AuthType = AuthType.Negotiate;
ldap.Bind(networkCredential);
// If the bind succeeds, we have a valid user/pass.
userAuthenticated = true;
catch (LdapException ldapEx)
// Error Code 0x31 signifies invalid credentials, so return userAuthenticated as false.
if (!ldapEx.ErrorCode.Equals(0x31))
throw;
return userAuthenticated;
private bool VerifyServerCertificate(LdapConnection connection, X509Certificate certificate)
X509Certificate2 cert = new X509Certificate2(certificate);
if (!cert.Verify())
// Could not validate potentially self-signed SSL certificate. Prompting user to install certificate themselves.
X509Certificate2UI.DisplayCertificate(cert);
// Try verifying again as the user may have allowed the certificate, and return the result.
if (!cert.Verify())
throw new SecurityException("Could not verify server certificate. Make sure this certificate comes from a trusted Certificate Authority.");
return true;
【讨论】:
以上是关于LdapConnection 与 PrincipalContext的主要内容,如果未能解决你的问题,请参考以下文章
在java Spring Boot中,如何在集成测试中将内存中的LDAPConnection对象传递给ldapService?