Подтвердить имя пользователя и пароль в Active Directory?

Как я могу проверить имя пользователя и пароль для Active Directory? Я просто хочу проверить правильность имени пользователя и пароля.

Ответ 1

Если вы работаете с .NET 3.5 или новее, вы можете использовать пространство имен System.DirectoryServices.AccountManagement и легко проверить свои учетные данные:

// create a "principal context" - e.g. your domain (could be machine, too)
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
{
    // validate the credentials
    bool isValid = pc.ValidateCredentials("myuser", "mypassword");
}

Это простой, надежный, он на 100% управляемый код на вашем конце - что еще вы можете попросить?: -)

Подробнее читайте здесь:

Update:

Как указано в этом другом вопросе SO (и его ответах), существует проблема с этим вызовом, возможно, возвращая True для старых паролей пользователя. Просто помните об этом поведении и не удивляйтесь, если это произойдет:-) (спасибо @MikeGledhill за то, что указали это!)

Ответ 2

Мы делаем это в нашей Интранет

Вы должны использовать System.DirectoryServices;

Вот кишки кода

using (DirectoryEntry adsEntry = new DirectoryEntry(path, strAccountId, strPassword))
{
    using (DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry))
    {
        //adsSearcher.Filter = "(&(objectClass=user)(objectCategory=person))";
        adsSearcher.Filter = "(sAMAccountName=" + strAccountId + ")";

        try
        {
            SearchResult adsSearchResult = adsSearcher.FindOne();
            bSucceeded = true;

            strAuthenticatedBy = "Active Directory";
            strError = "User has been authenticated by Active Directory.";
        }
        catch (Exception ex)
        {
            // Failed to authenticate. Most likely it is caused by unknown user
            // id or bad strPassword.
            strError = ex.Message;
        }
        finally
        {
            adsEntry.Close();
        }
    }
}

Ответ 3

В нескольких представленных здесь решениях нет возможности различать неправильный пользователь/пароль и пароль, который необходимо изменить. Это можно сделать следующим образом:

using System;
using System.DirectoryServices.Protocols;
using System.Net;

namespace ProtocolTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                LdapConnection connection = new LdapConnection("ldap.fabrikam.com");
                NetworkCredential credential = new NetworkCredential("user", "password");
                connection.Credential = credential;
                connection.Bind();
                Console.WriteLine("logged in");
            }
            catch (LdapException lexc)
            {
                String error = lexc.ServerErrorMessage;
                Console.WriteLine(lexc);
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc);
            }
        }
    }
}

Если пароль пользователя неверен или пользователь не существует, ошибка будет содержать

"8009030C: LdapErr: DSID-0C0904DC, комментарий: Ошибка AcceptSecurityContext, данные 52e, v1db1",

если пароль пользователя нужно изменить, он будет содержать

"8009030C: LdapErr: DSID-0C0904DC, комментарий: Ошибка AcceptSecurityContext, данные 773, v1db1"

Значение данных lexc.ServerErrorMessage представляет собой шестнадцатеричное представление кода ошибки Win32. Это те же коды ошибок, которые были бы возвращены путем вызова вызова Win32 LogonUser API. В приведенном ниже списке представлены общие значения с шестнадцатеричным и десятичным значениями:

525​ user not found ​(1317)
52e​ invalid credentials ​(1326)
530​ not permitted to logon at this time​ (1328)
531​ not permitted to logon at this workstation​ (1329)
532​ password expired ​(1330)
533​ account disabled ​(1331) 
701​ account expired ​(1793)
773​ user must reset password (1907)
775​ user account locked (1909)

Ответ 4

очень простое решение с использованием DirectoryServices:

using System.DirectoryServices;

//srvr = ldap server, e.g. LDAP://domain.com
//usr = user name
//pwd = user password
public bool IsAuthenticated(string srvr, string usr, string pwd)
{
    bool authenticated = false;

    try
    {
        DirectoryEntry entry = new DirectoryEntry(srvr, usr, pwd);
        object nativeObject = entry.NativeObject;
        authenticated = true;
    }
    catch (DirectoryServicesCOMException cex)
    {
        //not authenticated; reason why is in cex
    }
    catch (Exception ex)
    {
        //not authenticated due to some other exception [this is optional]
    }

    return authenticated;
}

для обнаружения плохих пользователей/паролей требуется доступ к NativeObject

Ответ 5

К сожалению, нет простого способа проверить учетные данные пользователей в AD.

С каждым представленным до сих пор способом вы можете получить ложно-отрицательный: пользовательские creds будут действительны, однако AD при возврате false будет возвращать false:

  • Пользователь должен сменить пароль при следующем входе в систему.
  • Срок действия пароля пользователя истек.

ActiveDirectory не позволит вам использовать LDAP, чтобы определить, недействителен ли пароль из-за того, что пользователь должен сменить пароль или срок их пароля истек.

Чтобы определить изменение пароля или срок действия пароля истек, вы можете вызвать Win32: LogonUser() и проверить код ошибки Windows для следующих 2 констант:

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330

Ответ 6

Вероятно, самым простым способом является PInvoke LogonUser Win32 API.e.g.

http://www.pinvoke.net/default.aspx/advapi32/LogonUser.html

Ссылка MSDN здесь...

http://msdn.microsoft.com/en-us/library/aa378184.aspx

Определенно хотите использовать тип входа

LOGON32_LOGON_NETWORK (3)

Это создает только легкий токен - идеально подходит для проверок AuthN. (другие типы могут использоваться для создания интерактивных сеансов и т.д.)

Ответ 7

Полное решение .Net - использовать классы из пространства имен System.DirectoryServices. Они позволяют напрямую запрашивать сервер AD. Вот небольшой пример, который бы сделал это:

using (DirectoryEntry entry = new DirectoryEntry())
{
    entry.Username = "here goes the username you want to validate";
    entry.Password = "here goes the password";

    DirectorySearcher searcher = new DirectorySearcher(entry);

    searcher.Filter = "(objectclass=user)";

    try
    {
        searcher.FindOne();
    }
    catch (COMException ex)
    {
        if (ex.ErrorCode == -2147023570)
        {
            // Login or password is incorrect
        }
    }
}

// FindOne() didn't throw, the credentials are correct

Этот код напрямую подключается к серверу AD, используя предоставленные учетные данные. Если учетные данные недействительны, искатель .FindOne() выдаст исключение. ErrorCode соответствует ошибке COM недопустимого имени пользователя/пароля.

Вам не нужно запускать код как пользователь AD. Фактически, я успешно использую его для запроса информации на сервере AD, от клиента за пределами домена!

Ответ 8

Еще один вызов .NET для быстрой аутентификации учетных данных LDAP:

using System.DirectoryServices;

using(var DE = new DirectoryEntry(path, username, password)
{
    try
    {
        DE.RefreshCache(); // This will force credentials validation
    }
    catch (COMException ex)
    {
        // Validation failed - handle how you want
    }
}

Ответ 9

Попробуйте этот код (ПРИМЕЧАНИЕ: Сообщается, что он не работает на сервере Windows 2000)

#region NTLogonUser
#region Direct OS LogonUser Code
[DllImport( "advapi32.dll")]
private static extern bool LogonUser(String lpszUsername, 
    String lpszDomain, String lpszPassword, int dwLogonType, 
    int dwLogonProvider, out int phToken);

[DllImport("Kernel32.dll")]
private static extern int GetLastError();

public static bool LogOnXP(String sDomain, String sUser, String sPassword)
{
   int token1, ret;
   int attmpts = 0;

   bool LoggedOn = false;

   while (!LoggedOn && attmpts < 2)
   {
      LoggedOn= LogonUser(sUser, sDomain, sPassword, 3, 0, out token1);
      if (LoggedOn) return (true);
      else
      {
         switch (ret = GetLastError())
         {
            case (126): ; 
               if (attmpts++ > 2)
                  throw new LogonException(
                      "Specified module could not be found. error code: " + 
                      ret.ToString());
               break;

            case (1314): 
               throw new LogonException(
                  "Specified module could not be found. error code: " + 
                      ret.ToString());

            case (1326): 
               // edited out based on comment
               //  throw new LogonException(
               //   "Unknown user name or bad password.");
            return false;

            default: 
               throw new LogonException(
                  "Unexpected Logon Failure. Contact Administrator");
              }
          }
       }
   return(false);
}
#endregion Direct Logon Code
#endregion NTLogonUser

за исключением того, что вам нужно создать собственное собственное исключение для "LogonException"

Ответ 10

Если вы застряли с .NET 2.0 и управляемым кодом, вот еще один способ, который работает с локальными и доменными учетными записями:

using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Diagnostics;

static public bool Validate(string domain, string username, string password)
{
    try
    {
        Process proc = new Process();
        proc.StartInfo = new ProcessStartInfo()
        {
            FileName = "no_matter.xyz",
            CreateNoWindow = true,
            WindowStyle = ProcessWindowStyle.Hidden,
            WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            RedirectStandardInput = true,
            LoadUserProfile = true,
            Domain = String.IsNullOrEmpty(domain) ? "" : domain,
            UserName = username,
            Password = Credentials.ToSecureString(password)
        };
        proc.Start();
        proc.WaitForExit();
    }
    catch (System.ComponentModel.Win32Exception ex)
    {
        switch (ex.NativeErrorCode)
        {
            case 1326: return false;
            case 2: return true;
            default: throw ex;
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }

    return false;
}   

Ответ 11

Моя простая функция

 private bool IsValidActiveDirectoryUser(string activeDirectoryServerDomain, string username, string password)
    {
        try
        {
            DirectoryEntry de = new DirectoryEntry("LDAP://" + activeDirectoryServerDomain, username + "@" + activeDirectoryServerDomain, password, AuthenticationTypes.Secure);
            DirectorySearcher ds = new DirectorySearcher(de);
            ds.FindOne();
            return true;
        }
        catch //(Exception ex)
        {
            return false;
        }
    }

Ответ 12

Проверка подлинности Windows может быть выполнена по различным причинам: неправильное имя пользователя или пароль, заблокированная учетная запись, истекший пароль и т.д. Чтобы различать эти ошибки, вызовите функцию API LogonUser через P/Invoke и проверьте код ошибки, если функция возвращает false:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

public static class Win32Authentication
{
    private class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle() // called by P/Invoke
            : base(true)
        {
        }

        protected override bool ReleaseHandle()
        {
            return CloseHandle(this.handle);
        }
    }

    private enum LogonType : uint
    {
        Network = 3, // LOGON32_LOGON_NETWORK
    }

    private enum LogonProvider : uint
    {
        WinNT50 = 3, // LOGON32_PROVIDER_WINNT50
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(
        string userName, string domain, string password,
        LogonType logonType, LogonProvider logonProvider,
        out SafeTokenHandle token);

    public static void AuthenticateUser(string userName, string password)
    {
        string domain = null;
        string[] parts = userName.Split('\\');
        if (parts.Length == 2)
        {
            domain = parts[0];
            userName = parts[1];
        }

        SafeTokenHandle token;
        if (LogonUser(userName, domain, password, LogonType.Network, LogonProvider.WinNT50, out token))
            token.Dispose();
        else
            throw new Win32Exception(); // calls Marshal.GetLastWin32Error()
    }
}

Использование образца:

try
{
    Win32Authentication.AuthenticateUser("EXAMPLE\\user", "[email protected]");
    // Or: Win32Authentication.AuthenticateUser("[email protected]", "[email protected]");
}
catch (Win32Exception ex)
{
    switch (ex.NativeErrorCode)
    {
        case 1326: // ERROR_LOGON_FAILURE (incorrect user name or password)
            // ...
        case 1327: // ERROR_ACCOUNT_RESTRICTION
            // ...
        case 1330: // ERROR_PASSWORD_EXPIRED
            // ...
        case 1331: // ERROR_ACCOUNT_DISABLED
            // ...
        case 1907: // ERROR_PASSWORD_MUST_CHANGE
            // ...
        case 1909: // ERROR_ACCOUNT_LOCKED_OUT
            // ...
        default: // Other
            break;
    }
}

Примечание. LogonUser требует отношения доверия с доменом, который вы проверяете.