Windows – Win32:如何验证Active Directory的凭据?

前端之家收集整理的这篇文章主要介绍了Windows – Win32:如何验证Active Directory的凭据?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
它已经是 askedanswered for .NET,但现在是时候得到本机Win32代码的答案:

如何验证Windows用户名和密码?

asked this question before for managed code.现在是时候为本地解决方案.

需要指出一些更常见的解决方案的陷阱:

方法无效1.使用模拟查询Active Directory

很多人suggest querying the Active Directory的东西.如果抛出异常,那么您知道凭据无效 – 如this stackoverflow question所述.

但是有some serious drawbacks to this approach

  • You are not only authenticating a domain account,but you are also doing an implicit authorization check. That is,you are reading properties from the AD using an impersonation token. What if the otherwise valid account has no rights to read from the AD? By default all users have read access,but domain policies can be set to disable access permissions for restricted accounts (and or groups).

  • Binding against the AD has a serIoUs overhead,the AD schema cache has to be loaded at the client (ADSI cache in the ADSI provider used by DirectoryServices). This is both network,and AD server,resource consuming – and is too expensive for a simple operation like authenticating a user account.

  • You’re relying on an exception failure for a non-exceptional case,and assuming that means invalid username and password. Other problems (e.g. network failure,AD connectivity failure,memory allocation error,etc) are then mis-intrepreted as authentication failure.

使用DirectoryEntry类是.NET是验证凭据的错误方法的示例:

无效方法1a – .NET

DirectoryEntry entry = new DirectoryEntry("persuis","iboyd","Tr0ub4dor&3");
object nativeObject = entry.NativeObject;

无效方法1b – .NET#2

public static Boolean CheckADUserCredentials(String accountName,String password,String domain)
{
    Boolean result;

    using (DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain,accountName,password))
    {
        using (DirectorySearcher searcher = new DirectorySearcher(entry))
        {
            String filter = String.Format("(&(objectCategory=user)(sAMAccountName={0}))",accountName);
            searcher.Filter = filter;
            try
            {
                SearchResult adsSearchResult = searcher.FindOne();
                result = true;
            }
            catch (DirectoryServicesCOMException ex)
            {
                const int SEC_E_logoN_DENIED = -2146893044; //0x8009030C;
                if (ex.ExtendedError == SEC_E_logoN_DENIED)
                {
                    // Failed to authenticate. 
                    result = false;
                }
                else
                {
                    throw;
                }
            }
        }
    }

以及通过ADO连接查询Active Directory:

方法1c无效 – 本地查询

connectionString = "Provider=ADsDSOObject;
       User ID=iboyd;Password=Tr0ub4dor&3;
       Encrypt Password=True;Mode=Read;
       Bind Flags=0;ADSI Flag=-2147483648';"

SELECT userAccountControl 
FROM 'LDAP://persuis/DC=stackoverflow,DC=com'
WHERE objectClass='user' and sAMAccountName = 'iboyd'

即使您的凭据有效,但您没有权限查看您的目录条目,这些都会失败:

方法无效2. logonUser Win32 API

Others建议使用LogonUser() API函数.这听起来不错,但不幸的是,主叫用户有时需要通过授权给操作系统本身:

The process calling logonUser requires
the SE_TCB_NAME privilege. If the
calling process does not have this
privilege,logonUser fails and
GetLastError returns
ERROR_PRIVILEGE_NOT_HELD.

In some
cases,the process that calls
logonUser must also have the
SE_CHANGE_NOTIFY_NAME privilege
enabled; otherwise,logonUser fails
and GetLastError returns
ERROR_ACCESS_DENIED. This privilege is
not required for the local system
account or accounts that are members
of the administrators group. By
default,SE_CHANGE_NOTIFY_NAME is
enabled for all users,but some
administrators may disable it for
everyone.

作为微软在knowledge base article中指出的那样,将“作为操作系统的一部分”作为优势并不是你想做的事情 –

…the process that is calling
logonUser must have the SE_TCB_NAME
privilege (in User Manager,this is
the “Act as part of the Operating
System
” right). The SE_TCB_NAME
privilege is very powerful and
should not be granted to any arbitrary user just so that they can
run an application
that needs to
validate credentials.

另外,如果指定了空白密码,对logonUser()的调用将失败.

有效的.NET 3.5方法 – PrincipalContext

有一种验证方法,仅在.NET 3.5和更高版本中可用,允许用户进行身份验证,而无需执行授权检查:

// create a "principal context" - e.g. your domain (could be machine,too)
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain,"stackoverflow.com"))
{
    // validate the credentials
    bool isValid = pc.ValidateCredentials("iboyd","Tr0ub4dor&3")
}

不幸的是,此代码仅在.NET 3.5及更高版本中可用.

现在是找到本机等价物的时候了.

Here is Microsoft’s recommendation.

对于其他答案,我不太确定你为什么要拍摄他们.您尝试验证凭据时抱怨(相对边缘的情况)失败,但是如果要使用这些凭据实际执行某些操作,那么该操作将会失败.如果你不会真正用这些凭证做某事,那么为什么要首先验证它们呢?这似乎是一个有点设计的情况,但显然我不知道你想要完成什么.

猜你在找的Windows相关文章