Проверить имя пользователя и пароль в Active Directory? - PullRequest
497 голосов
/ 14 ноября 2008

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

Ответы [ 12 ]

611 голосов
/ 01 февраля 2009

Если вы работаете в .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% C # -управляемый код на вашем конце - что еще вы можете попросить? : -)

Прочитайте все об этом здесь:

Обновление:

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

68 голосов
/ 14 ноября 2008

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

Вы должны использовать 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();
        }
    }
}
54 голосов
/ 14 июня 2012

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

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)
33 голосов
/ 14 ноября 2008

очень простое решение с использованием 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 для обнаружения неверного пользователя / пароля

28 голосов
/ 15 ноября 2008

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

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

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

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

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

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330
21 голосов
/ 14 ноября 2008

Вероятно, самый простой способ - это 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. (другие типы могут использоваться для создания интерактивных сессий и т. д.)

18 голосов
/ 14 ноября 2008

Полное решение .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, используя предоставленные учетные данные. Если учетные данные недействительны, searcher.FindOne () сгенерирует исключение. Код ошибки - это код ошибки «Недопустимое имя пользователя / пароль».

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

11 голосов
/ 08 апреля 2011

Еще один вызов .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
    }
}
10 голосов
/ 14 ноября 2008

Попробуйте этот код (ПРИМЕЧАНИЕ: сообщается, что не работает на Windows Server 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"

5 голосов
/ 07 апреля 2011

Если вы застряли с .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;
}   
...