Проверьте учетные данные пользователя на контроллере домена в .net - PullRequest
0 голосов
/ 11 января 2012

в приложении .NET я пытаюсь аутентифицировать пользователей по имени пользователя и паролю против пользователей Windows, как локальных, так и пользователей домена. Я уже пробовал это решение .Мой код для получения PrincipalContext выглядит следующим образом:

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;
    }
}

Это прекрасно работает для пользователей локальных окон и удаленных пользователей в ActiveDirectory.Но если я попытаюсь запустить проверку подлинности на компьютере, то он присоединится к не-ActiveDirectory Domain Master, например.Сервер Samba Я получаю следующее исключение:

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)

Таким образом, кажется, что PrincipalContext пытается использовать LDAP в случае ContextType.Domain.Если я пытаюсь использовать ContextType.Machine, я не могу использовать имя рабочей группы / домена, так как PrincipalContext пытается подключиться напрямую к машине.Это не удастся, если уже есть подключение к этой машине с этими окнами с той же машины.

Так что мой вопрос:

  • Как аутентифицировать пользователя с доменом учетных данных, имя пользователяи пароль к хозяину домена, который не обязательно основан на ActiveDirectory?
  • Существуют ли управляемые API для выполнения описанной выше задачи?
  • Если не существует управляемых базовых классов, что такоеправильное направление, чтобы сделать это с?

Спасибо за ваши ответы.

Ответы [ 2 ]

4 голосов
/ 19 января 2012

Ради полноты, вот мое решение, которое, кажется, делает именно то, что я хочу:

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
    }
}

Перечисления LogonType и LogonProvider отражают определения в "Winbase.h".Я решил использовать LogonType.LOGON32_LOGON_BATCH вместо LogonType.LOGON32_LOGON_NETWORK, потому что samba 3.4.X, похоже, имеет проблемы с этим типом.

2 голосов
/ 16 января 2013

Вот то, что я только что сделал для приложения, над которым я работаю - требует Framework v3.5 или выше ....

public static bool Authenticate(string user, string password)
{
    // Split the user name in case a domain name was specified as DOMAIN\USER
    string[] NamesArray = user.Split(new char[] { '\\' }, 2);

    // Default vars for names & principal context type
    string DomainName = string.Empty;
    string UserName = string.Empty;
    ContextType TypeValue = ContextType.Domain;

    // Domain name was supplied
    if (NamesArray.Length > 1)
    {
        DomainName = NamesArray[0];
        UserName = NamesArray[1];
    }
    else
    {
        // Pull domain name from environment
        DomainName = Environment.UserDomainName;
        UserName = user;

        // Check this against the machine name to pick up on a workgroup
        if (string.Compare(DomainName, System.Environment.MachineName, StringComparison.InvariantCultureIgnoreCase) == 0)
        {
            // Use the domain name as machine name (local user)
            TypeValue = ContextType.Machine;
        }
    }

    // Create the temp context
    using (PrincipalContext ContextObject = new PrincipalContext(TypeValue, DomainName))
    {
        // Validate the credentials
        return ContextObject.ValidateCredentials(UserName, password);
    }
}
...