本文介绍了验证对域控制器的用户凭据.NET的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在.NET应用程序,我试图验证通过用户名和密码的用户对Windows用户来说,本地的和域用户。 。我的代码来获取PrincipalContext看起来如下:

 受保护的静态PrincipalContext TryCreatePrincipalContext(字符串域)
{
变种computerDomain = TryGetComputerDomain();

如果(String.IsNullOrEmpty(域)及和放大器; String.IsNullOrEmpty(computerDomain))
返回新PrincipalContext(ContextType.Machine);
,否则如果(String.IsNullOrEmpty(域))
返回新PrincipalContext(ContextType.Domain,computerDomain);
,否则
返回新PrincipalContext(ContextType.Domain,域);
}

受保护的静态字符串TryGetComputerDomain()
{

{
VAR域= Domain.GetComputerDomain();
返回domain.Name;
}赶上
{
返回NULL;
}
}

这对于本地Windows用户的用户和远程工作正常用户在ActiveDirectory中。但是,如果我尝试运行的机器上的认证,也加入到非的ActiveDirectory域主,例如。 Samba服务器出现以下异常:

  System.DirectoryServices.AccountManagement.PrincipalServerDownException:麻省理工学院的马克服务器konnte keine Verbindung hergestellt werden。 ---> 
System.DirectoryServices.Protocols.LdapException:明镜LDAP服务器IST nichtverfügbar。
贝System.DirectoryServices.Protocols.LdapConnection.Connect()
贝System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(DirectoryRequest请求的Int32&安培; MESSAGEID)
贝System.DirectoryServices.Protocols.LdapConnection .SendRequest(DirectoryRequest要求,时间跨度将requestTimeout)
贝System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest要求)
贝System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(字符串服务器名,ServerProperties和放大器;属性)
---电恩德德internenAusnahmestapelüberwachung---
贝System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(字符串服务器名,ServerProperties和放大器;属性)
贝System.DirectoryServices.AccountManagement.PrincipalContext.DoServerVerifyAndPropRetrieval( )
贝System.DirectoryServices.AccountManagement.PrincipalContext..ctor(contextType contextType,字符串名称,字符串容器,ContextOptions选项,用户名字符串,字符串密码)
贝System.DirectoryServices.AccountManagement.PrincipalContext..ctor (contextType contextType,字符串名称)
贝DomainAuthTest.DomainAuthenticator.TryCreatePrincipalContext(字符串域)
贝DomainAuthTest.DomainAuthenticator.Authenticate(字符串domainUser,字符串密码)
贝DomainAuthTest.Program.Main(字符串[]参数)

如此看来,在PrincipalContext试图在ContextType.Domain的情况下使用LDAP。如果我尝试使用ContextType.Machine我无法使用工作组/域名为PrincipalContext尝试直接连接到本机。 。那如果已经有一个来自同一台计算机的窗口到该计算机连接失败



所以我的问题是:




  • 如何使用针对域主凭据域,用户名和密码,这不一定是基于一个ActiveDirectory的用户进行身份验证?

  • 是有管理的API来完成上述任务?

  • 如果没有管理基础类,什么是做与?



  • 感谢您的答复。


    解决方案

    有关完整起见,在这里我的解决方案,它似乎做正是我想要的:

     公共类WinApiDomainAuthenticator 
    {
    函数[DllImport (ADVAPI32.DLL,SetLastError = TRUE)]
    公共静态的extern BOOL LogonUser的(字符串lpszUsername,
    串lpszDomain,
    串lpszPassword,
    INT dwLogonType,
    INT dwLogonProvider,
    OUT的IntPtr phToken);

    函数[DllImport(KERNEL32.DLL,字符集= CharSet.Auto)]
    公众的extern静态布尔CloseHandle的(IntPtr的手柄);

    公共静态的IPrincipal进行身份验证(字符串domainUser,字符串密码)
    {
    VAR userToken = IntPtr.Zero;
    变种creds =新DomainAuthCredentials(domainUser,密码);

    如果(!LogonUser的(creds.Username,
    creds.Domain,
    creds.Password,
    (INT)LogonType.LOGON32_LOGON_BATCH,
    (INT )LogonProvider.LOGON32_PROVIDER_DEFAULT,出userToken))
    {
    VAR误差=新Win32Exception(Marshal.GetLastWin32Error());
    抛出新SecurityException异常(错误而认证用户,错误);
    }

    变种标识=新的WindowsIdentity(userToken);

    如果(userToken = IntPtr.Zero!)
    CloseHandle的(userToken);

    返回ConvertWindowsIdentityToGenericPrincipal(身份);
    }

    保护静态的IPrincipal ConvertWindowsIdentityToGenericPrincipal(的WindowsIdentity的WindowsIdentity)
    {
    如果(的WindowsIdentity == NULL)
    返回NULL;

    //认同格式DOMAIN\Username
    变种标识=新的GenericIdentity(windowsIdentity.Name);

    VAR组名=新的字符串[0];
    如果(windowsIdentity.Groups!= NULL)
    格式DOMAIN\Group $ B $ {
    //阵列组,名称乙组名= windowsIdentity.Groups
    。选择(GID => gId.Translate(typeof运算(NTACCOUNT)))
    。选择(GNT => gNt.ToString())
    .ToArray();
    }

    变种的GenericPrincipal =新的GenericPrincipal(标识,组名);
    回报率的GenericPrincipal;
    }

    类保护DomainAuthCredentials
    {
    公共DomainAuthCredentials(字符串domainUser,字符串密码)
    {
    =用户名domainUser;
    密码=密码;
    结构域=;

    如果
    回报率(domainUser.Contains(@\)!);

    VAR令牌= domainUser.Split(新的char [] {'\\'},2);
    结构域=令牌[0];
    用户名=令牌[1];
    }

    公共DomainAuthCredentials()
    {
    区=的String.Empty;
    }

    #区域属性

    公共字符串域{搞定;组; }
    公共字符串用户名{搞定;组; }
    公共字符串密码{搞定;组; }

    #endregion
    }
    }



    的登录类型和LogonProvider枚举反映WINBASE.H的定义。我LogonType.LOGON32_LOGON_BATCH代替LogonType.LOGON32_LOGON_NETWORK定居,因为桑巴3.4.X似乎与这种类型的麻烦。


    in an .NET application, I'm trying to authenticate users by username and password a against windows users, local ones as well as domain users. I already tried this solution . My code to get the PrincipalContext looks the following:

    protected static PrincipalContext TryCreatePrincipalContext(String domain)
    {
        var computerDomain = TryGetComputerDomain();
    
        if (String.IsNullOrEmpty(domain) && String.IsNullOrEmpty(computerDomain))
            return new PrincipalContext(ContextType.Machine);
        else if (String.IsNullOrEmpty(domain))
            return new PrincipalContext(ContextType.Domain, computerDomain);
        else
            return new PrincipalContext(ContextType.Domain, domain);
    }
    
    protected static String TryGetComputerDomain()
    {
        try
        {
            var domain = Domain.GetComputerDomain();
            return domain.Name;
        } catch
        {
           return null;
        }
    }
    

    That works fine for local windows users users and for remote users in an ActiveDirectory. But if I try to run the authentication on a machine, that is joined to a non-ActiveDirectory Domain Master, eg. a Samba Server I get the following Exception:

    System.DirectoryServices.AccountManagement.PrincipalServerDownException: Mit dem Server konnte keine Verbindung hergestellt werden. --->
    System.DirectoryServices.Protocols.LdapException: Der LDAP-Server ist nicht verfügbar.
    bei System.DirectoryServices.Protocols.LdapConnection.Connect()
    bei System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(DirectoryRequest request, Int32& messageID)
    bei System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout)
    bei System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request)
    bei System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(String serverName, ServerProperties& properties)
    --- Ende der internen Ausnahmestapelüberwachung ---
    bei System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(String serverName, ServerProperties& properties)
    bei System.DirectoryServices.AccountManagement.PrincipalContext.DoServerVerifyAndPropRetrieval()
    bei System.DirectoryServices.AccountManagement.PrincipalContext..ctor(ContextType contextType, String name, String container, ContextOptions options, String userName, String password)
    bei System.DirectoryServices.AccountManagement.PrincipalContext..ctor(ContextType contextType, String name)
    bei DomainAuthTest.DomainAuthenticator.TryCreatePrincipalContext(String domain)
    bei DomainAuthTest.DomainAuthenticator.Authenticate(String domainUser, String  password)
    bei DomainAuthTest.Program.Main(String[] args)
    

    So it seems that the PrincipalContext tries to use LDAP in case of ContextType.Domain. If I try to use ContextType.Machine I have cannot use the workgroup/domain-name as PrincipalContext tries to connect directly to the machine. That fails if there is already a connection to that machine with that windows from the same machine.

    So my question is:

    • How to authenticate a user with the credentials domain, username and password against a domain master, which is not necessarily based on an ActiveDirectory?
    • Are there managed APIs to accomplish the above described task?
    • If there are no managed foundation-classes, what is the right direction to do that with?

    Thank you for your replies.

    解决方案

    For the sake of completeness, here my solution which seems to do exactly what I want:

    public class WinApiDomainAuthenticator
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(string lpszUsername,
                                            string lpszDomain,
                                            string lpszPassword,
                                            int dwLogonType,
                                            int dwLogonProvider,
                                            out IntPtr phToken);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);
    
        public static IPrincipal Authenticate(String domainUser, String password)
        {
            var userToken = IntPtr.Zero;
            var creds = new DomainAuthCredentials(domainUser, password);
    
            if (! LogonUser(creds.Username,
                            creds.Domain,
                            creds.Password,
                           (int)LogonType.LOGON32_LOGON_BATCH,
                           (int)LogonProvider.LOGON32_PROVIDER_DEFAULT, out userToken))
            {
                var error = new Win32Exception(Marshal.GetLastWin32Error());
                throw new SecurityException("Error while authenticating user", error);
            }
    
            var identity = new WindowsIdentity(userToken);
    
            if (userToken != IntPtr.Zero)
                CloseHandle(userToken);
    
            return ConvertWindowsIdentityToGenericPrincipal(identity);
        }
    
        protected static IPrincipal ConvertWindowsIdentityToGenericPrincipal(WindowsIdentity windowsIdentity)
        {
            if (windowsIdentity == null)
                return null;
    
            // Identity in format DOMAIN\Username
            var identity = new GenericIdentity(windowsIdentity.Name);
    
            var groupNames = new string[0];
            if (windowsIdentity.Groups != null)
            {
                // Array of Group-Names in format DOMAIN\Group
                groupNames = windowsIdentity.Groups
                                            .Select(gId => gId.Translate(typeof(NTAccount)))
                                            .Select(gNt => gNt.ToString())
                                            .ToArray();
            }
    
            var genericPrincipal = new GenericPrincipal(identity, groupNames);
            return genericPrincipal;
        }
    
        protected class DomainAuthCredentials
        {
            public DomainAuthCredentials(String domainUser, String password)
            {
                Username = domainUser;
                Password = password;
                Domain = ".";
    
                if (!domainUser.Contains(@"\"))
                    return;
    
                var tokens = domainUser.Split(new char[] { '\\' }, 2);
                Domain = tokens[0];
                Username = tokens[1];
            }
    
            public DomainAuthCredentials()
            {
                Domain = String.Empty;
            }
    
            #region Properties
    
            public String Domain { get; set; }
            public String Username { get; set; }
            public String Password { get; set; }
    
            #endregion
        }
    }
    

    The LogonType and LogonProvider enums reflect the definitions in "Winbase.h". I settled with LogonType.LOGON32_LOGON_BATCH instead of LogonType.LOGON32_LOGON_NETWORK because samba 3.4.X seems to have trouble with this type.

    这篇关于验证对域控制器的用户凭据.NET的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 02:24