Question Valider un nom d'utilisateur et un mot de passe par rapport à Active Directory?


Comment puis-je valider un nom d'utilisateur et un mot de passe dans Active Directory? Je veux simplement vérifier si un nom d'utilisateur et un mot de passe sont corrects.


459
2017-11-14 15:59


origine


Réponses:


Si vous travaillez sur .NET 3.5 ou plus récent, vous pouvez utiliser le System.DirectoryServices.AccountManagement espace de noms et vérifiez 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 C # géré à 100% de votre côté - que demander de plus? :-)

Lisez tout a propos de ça ici:

Mettre à jour:

Comme indiqué dans cette autre question SO (et ses réponses), il y a un problème avec cet appel qui pourrait revenir True pour les anciens mots de passe d'un utilisateur. Soyez conscient de ce comportement et ne soyez pas trop surpris si cela se produit :-) (merci à @MikeGledhill de l'avoir signalé!)


577
2018-01-31 22:35



Nous le faisons sur notre Intranet

Vous devez utiliser System.DirectoryServices;

Voici les tripes 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();
        }
    }
}

59
2017-11-14 16:10



Plusieurs solutions présentées ici ne permettent pas de différencier un utilisateur / mot de passe incorrect et un mot de passe à modifier. Cela peut être fait 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 faux ou si l'utilisateur n'existe pas, l'erreur contiendra

"8009030C: LdapErr: DSID-0C0904DC, commentaire: erreur AcceptSecurityContext, données 52e, v1db1",

Si le mot de passe des utilisateurs doit être modifié, il contiendra

"8009030C: LdapErr: DSID-0C0904DC, commentaire: erreur AcceptSecurityContext, données 773, v1db1"

le lexc.ServerErrorMessage valeur de données est une représentation hexadécimale du code d'erreur Win32. Ce sont les mêmes codes d'erreur qui seraient renvoyés en invoquant autrement l'appel de l'API Win32 LogonUser. La liste ci-dessous résume une plage de valeurs communes avec des valeurs hexadécimales 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)

42
2018-06-14 12:55



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 requis pour détecter un mauvais utilisateur / mot de passe


31
2017-11-14 16:15



Malheureusement, il n'existe aucun moyen "simple" de vérifier les informations d'identification d'un utilisateur sur AD.

Avec toutes les méthodes présentées jusqu'à présent, vous pouvez obtenir un faux négatif: les droits d'accès d'un utilisateur seront valides, mais AD retournera faux dans certaines circonstances:

  • L'utilisateur est tenu de modifier le mot de passe lors de la prochaine ouverture de session.
  • Le mot de passe de l'utilisateur a expiré.

ActiveDirectory ne vous permettra pas d'utiliser LDAP pour déterminer si un mot de passe n'est pas valide car un utilisateur doit changer de mot de passe ou si son mot de passe a 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 deux constantes suivantes:

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330

28
2017-11-14 23:51



Probablement le moyen le plus facile est de PInvoke LogonUser Win32 API.e.g.

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

Référence MSDN ici ...

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

Vraiment vouloir utiliser le type de connexion

LOGON32_LOGON_NETWORK (3)

Cela crée un jeton léger seulement - parfait pour les vérifications d'authentification. (d'autres types peuvent être utilisés pour créer des sessions interactives, etc.)


20
2017-11-14 16:01



Une solution complète. Net consiste à utiliser les classes de l'espace de noms System.DirectoryServices. Ils permettent d'interroger directement un serveur AD. Voici un petit échantillon qui ferait ceci:

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 AD en utilisant les informations d'identification fournies. Si les informations d'identification ne sont pas valides, searcher.FindOne () lancera une exception. Le ErrorCode est celui correspondant à l'erreur COM "nom d'utilisateur / mot de passe invalide".

Vous n'avez pas besoin d'exécuter le code en tant qu'utilisateur AD. En fait, je l’utilise avec succès pour interroger des informations sur un serveur AD, à partir d’un client hors du domaine!


17
2017-11-14 16:17



Encore un autre appel .NET pour authentifier rapidement les informations d'identification 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
2018-04-07 21:55



Essayez ce code (REMARQUE: signalé pour ne pas fonctionner sur 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
2017-11-14 16:06



Si vous êtes bloqué avec .NET 2.0 et le code managé, voici une autre façon de travailler 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
2018-04-07 11:04



Ma fonction simple

 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
2017-12-27 14:14