2008-11-14 3 views

ответ

19

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

+0

Как указывает @Alan, API LogonUser имеет много полезных черт помимо вызова System.DirectoryServices. – stephbu 2008-12-01 17:48:17

+3

@cciotti: Нет, это неправильно. ЛУЧШИЙ способ правильно аутентифицировать кого-то - использовать LogonUserAPI как @stephbu write. Все другие методы, описанные в этом сообщении, НЕ РАБОТАЮТ на 100%. Просто заметьте, однако, я считаю, что вы должны быть присоединены к домену, чтобы вызвать LogonUser. – Alan 2009-04-20 18:28:04

+0

@Alan, чтобы сгенерировать учетные данные, вы должны иметь возможность подключиться к домену, передав действительную учетную запись домена. Однако я уверен, что ваша машина не обязательно должна быть членом домена. – stephbu 2009-04-21 02:15:30

9

Попробуйте этот код (Примечание: Сообщил, чтобы не работать на 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»

+0

Не используйте обработку исключений для возврата информации из метода. «Неизвестное имя пользователя или неверный пароль» не является исключительным, это стандартное поведение для LogonUser. Просто верните false. – Treb 2008-11-14 16:20:33

+0

Да ... это был порт из старой библиотеки VB6 ... написан 2003 или около того ... (когда .Net впервые вышел) – 2008-11-17 15:18:07

+0

Если в Windows 2000 этот код не будет работать (http: // support. microsoft.com/kb/180548) – 2008-12-01 14:58:56

55

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

Вы должны использовать 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(); 
     } 
    } 
} 
+1

Этот код не нужно запускать как пользователь AD? – bzlm 2008-11-14 16:13:40

+6

Что вы вкладываете в «путь»? Имя домена? Имя сервера? Путь LDAP к домену? Путь LDAP к серверу? – 2008-12-01 15:00:59

+2

Answer1: Нет, мы запускаем его как веб-службу, чтобы его можно было вызывать из нескольких мест в основном веб-приложении. Ответ2: Путь содержит информацию LDAP ... LDAP: // DC = domainname1, DC = domainname2, DC = com – 2008-12-01 18:21:30

30

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

16

полный. 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, от клиента за пределами домена!

27

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

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

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

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

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

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330
559

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

Читайте об этом здесь:

Update:

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

+4

Вы уверены? ??? Я получил исключение PrincipalServerdownException, когда я это пробовал ... – 2009-05-29 04:59:09

+1

Я попробовал: PrincipalContext pc = new PrincipalContext (ContextType.Domain, "yourdomain"); Ваш пробег может отличаться. – 2009-05-29 05:03:02

5

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

Еще один вызов .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 
    } 
} 
37

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

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 ошибка, 52е данных, 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) 
1

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

аутентификация Windows, может потерпеть неудачу по различным причинам: неправильное имя пользователя или пароль, заблокированная учетной запись , истекший пароль и т. д. Для того, чтобы различать эти ошибки, вызовите функцию LogonUser API через 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 требует доверительных отношений с доменом, на который вы проверяете.

Смежные вопросы