Valider un nom d'utilisateur et un mot de passe contre Active Directory?

comment valider un nom d'utilisateur et un mot de passe avec Active Directory? Je veux simplement vérifier si un nom d'utilisateur et mot de passe sont corrects.

466
demandé sur Marc 2008-11-14 18:59:42

12 réponses

si vous travaillez sur .NET 3.5 ou plus récent, vous pouvez utiliser l'espace de nom System.DirectoryServices.AccountManagement et vérifier facilement vos informations d'identification:

// 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");
}

c'est simple, c'est fiable, c'est du Code 100% C# managé de votre côté - que demander de plus? :- )

tout savoir ici:

mise à Jour:

comme indiqué dans cette autre question SO (et ses réponses) , Il ya un problème avec cet appel peut-être retourner True pour les anciens mots de passe d'un utilisateur. Soyez juste conscient de ce comportement et ne soyez pas trop surpris si cela arrive :-) (merci à @MikeGledhill de le souligner!)

585
répondu marc_s 2017-05-23 12:34:45

nous le faisons sur notre Intranet

vous devez utiliser le système.DirectoryServices;

Voici les boyaux du code

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();
        }
    }
}
61
répondu Dining Philanderer 2016-03-24 17:53:09

plusieurs solutions présentées ici ne permettent pas de faire la différence entre un utilisateur / mot de passe erroné et un mot de passe qui doit être modifié. Cela peut se faire de la manière suivante:

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

si le mot de passe de l'utilisateur est erroné, ou l'utilisateur n'existe pas, l'erreur contiendra

"8009030C: LdapErr: DSID-0C0904DC, comment: AcceptSecurityContext error, data 52e, v1db1",

si le mot de passe utilisateur doit être modifié, il contiendra

"8009030C: LdapErr: DSID-0C0904DC, comment: AcceptSecurityContext error, data 773, v1db1 "

la valeur de données lexc.ServerErrorMessage est une représentation hexadécimale du code D'erreur Win32. Ce sont les mêmes codes d'erreur qui seraient retournés en invoquant l'appel de L'API Win32 LogonUser. La liste ci-dessous résume une gamme de valeurs communes avec des valeurs HEX et décimales:

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)
46
répondu Søren Mors 2014-12-04 23:50:27

solution très simple utilisant 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;
}

l'accès NativeObject est nécessaire pour détecter un mauvais utilisateur / mot de passe

31
répondu Steven A. Lowe 2014-06-02 15:15:35

malheureusement, il n'y a pas de moyen" simple " de vérifier les informations d'identification d'un utilisateur sur AD.

avec chaque méthode présentée jusqu'à présent, vous pouvez obtenir un false-negative: les creds d'un utilisateur seront valides, cependant AD retournera false dans certaines circonstances:

L'utilisateur
  • est requis pour changer le mot de passe lors de la prochaine connexion.
  • Le mot de passe de L'utilisateur
  • est expiré.

ActiveDirectory ne vous permettra pas d'utiliser LDAP pour déterminer si un mot de passe est invalide en raison du fait qu'un utilisateur doit changer le mot de passe ou si son mot de passe est expiré.

pour déterminer le changement de mot de passe ou le mot de passe expiré, vous pouvez appeler Win32: LogonUser (), et vérifier le code d'erreur windows pour les 2 constantes suivantes:

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330
28
répondu Alan 2014-01-12 16:59:04

probablement la façon la plus facile est de PInvoke LogonUser Win32 API.par exemple

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

référence MSDN ici...

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

Je veux certainement utiliser le type de connexion

LOGON32_LOGON_NETWORK (3)

cela crée un token léger seulement - parfait pour les contrôles AuthN. (d'autres types peuvent être utilisés pour créer des sessions interactives, etc.)

21
répondu stephbu 2008-11-14 16:11:31

une solution .Net complète est d'utiliser les classes du système.Espace de nommage DirectoryServices. Ils permettent d'interroger directement un serveur publicitaire. Voici un petit échantillon:

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

ce code se connecte directement au serveur publicitaire, en utilisant les informations d'authentification fournies. Si les informations ne sont pas valides, chercheur.FindOne () va lancer une exception. L'ErrorCode est celui qui correspond à l'erreur de communication" nom d'utilisateur/mot de passe non valide".

Vous n'avez pas besoin d'exécuter le code comme un utilisateur de publicité. En fait, je l'utilise avec succès pour interroger des informations sur un serveur publicitaire, à partir d'un client en dehors du domaine !

17
répondu Mathieu Garstecki 2008-11-14 16:17:11

encore un autre appel. Net pour authentifier rapidement les justificatifs 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
    }
}
11
répondu palswim 2011-04-07 21:55:24

essayez ce code (NOTE: ne fonctionne pas sous 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

sauf que vous devrez créer votre propre exception personnalisée pour "LogonException"

10
répondu Charles Bretana 2013-11-29 16:43:18

si vous êtes bloqué avec .NET 2.0 et le code géré, voici une autre façon qui fonctionne avec les comptes locaux et de domaine:

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;
}   
5
répondu chauwel 2011-04-07 11:04:40

Ma Simple Fonction

 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;
        }
    }
1
répondu hossein andarkhora 2015-12-27 14:14:16

Windows authentification peut échouer pour diverses raisons: un nom d'utilisateur ou mot de passe incorrect, un compte verrouillé, un mot de passe expiré, et plus encore. Pour distinguer ces erreurs, appelez LogonUser fonction API via P / Invoke et vérifiez le code d'erreur si la fonction retourne 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()
    }
}

exemple d'utilisation:

try
{
    Win32Authentication.AuthenticateUser("EXAMPLE\user", "P@ssw0rd");
    // Or: Win32Authentication.AuthenticateUser("user@example.com", "P@ssw0rd");
}
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;
    }
}

Note: LogonUser nécessite une relation de confiance avec le domaine que vous êtes la validation de contre.

1
répondu Michael Liu 2016-12-27 21:05:07