2012-01-11 110 views
0

中的域控制器驗證用戶憑據,我試圖通過用戶名和密碼對Windows用戶,本地用戶以及域用戶進行身份驗證。 I already tried this solution。我的代碼來獲取PrincipalContext看起來如下:在.NET應用程序中根據.net

protected static PrincipalContext TryCreatePrincipalContext(String domain) 
{ 
    var computerDomain = TryGetComputerDomain(); 

    if (String.IsNullOrEmpty(domain) && String.IsNullOrEmpty(computerDomain)) 
     return new PrincipalContext(ContextType.Machine); 
    else if (String.IsNullOrEmpty(domain)) 
     return new PrincipalContext(ContextType.Domain, computerDomain); 
    else 
     return new PrincipalContext(ContextType.Domain, domain); 
} 

protected static String TryGetComputerDomain() 
{ 
    try 
    { 
     var domain = Domain.GetComputerDomain(); 
     return domain.Name; 
    } catch 
    { 
     return null; 
    } 
} 

,對於本地Windows用戶的用戶和在ActiveDirectory的遠程用戶能正常工作。但是,如果我嘗試在計算機上運行身份驗證,則會將其連接到非ActiveDirectory Domain Master,例如。 Samba服務器,我得到以下異常:

System.DirectoryServices.AccountManagement.PrincipalServerDownException: Mit dem Server konnte keine Verbindung hergestellt werden. ---> 
System.DirectoryServices.Protocols.LdapException: Der LDAP-Server ist nicht verfügbar. 
bei System.DirectoryServices.Protocols.LdapConnection.Connect() 
bei System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(DirectoryRequest request, Int32& messageID) 
bei System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout) 
bei System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request) 
bei System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(String serverName, ServerProperties& properties) 
--- Ende der internen Ausnahmestapelüberwachung --- 
bei System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(String serverName, ServerProperties& properties) 
bei System.DirectoryServices.AccountManagement.PrincipalContext.DoServerVerifyAndPropRetrieval() 
bei System.DirectoryServices.AccountManagement.PrincipalContext..ctor(ContextType contextType, String name, String container, ContextOptions options, String userName, String password) 
bei System.DirectoryServices.AccountManagement.PrincipalContext..ctor(ContextType contextType, String name) 
bei DomainAuthTest.DomainAuthenticator.TryCreatePrincipalContext(String domain) 
bei DomainAuthTest.DomainAuthenticator.Authenticate(String domainUser, String password) 
bei DomainAuthTest.Program.Main(String[] args) 

如此看來,在PrincipalContext試圖在ContextType.Domain的情況下使用LDAP。如果我嘗試使用ContextType.Machine,我不能使用工作組/域名,因爲PrincipalContext嘗試直接連接到機器。如果在同一臺機器上已經有與該機器的連接,則該連接將失敗。

所以我的問題是:

  • 如何使用針對域主,這不一定是基於一個ActiveDirectory的憑據域,用戶名和密碼的用戶進行身份驗證?
  • 是否有託管API來完成上述任務?
  • 如果沒有託管基礎班,那麼做什麼是正確的方向?

謝謝你的回覆。

回答

3

爲了完整起見,在這裏我的解決方案,這似乎做的正是我想要的:

public class WinApiDomainAuthenticator 
{ 
    [DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool LogonUser(string lpszUsername, 
             string lpszDomain, 
             string lpszPassword, 
             int dwLogonType, 
             int dwLogonProvider, 
             out IntPtr phToken); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
    public extern static bool CloseHandle(IntPtr handle); 

    public static IPrincipal Authenticate(String domainUser, String password) 
    { 
     var userToken = IntPtr.Zero; 
     var creds = new DomainAuthCredentials(domainUser, password); 

     if (! LogonUser(creds.Username, 
         creds.Domain, 
         creds.Password, 
         (int)LogonType.LOGON32_LOGON_BATCH, 
         (int)LogonProvider.LOGON32_PROVIDER_DEFAULT, out userToken)) 
     { 
      var error = new Win32Exception(Marshal.GetLastWin32Error()); 
      throw new SecurityException("Error while authenticating user", error); 
     } 

     var identity = new WindowsIdentity(userToken); 

     if (userToken != IntPtr.Zero) 
      CloseHandle(userToken); 

     return ConvertWindowsIdentityToGenericPrincipal(identity); 
    } 

    protected static IPrincipal ConvertWindowsIdentityToGenericPrincipal(WindowsIdentity windowsIdentity) 
    { 
     if (windowsIdentity == null) 
      return null; 

     // Identity in format DOMAIN\Username 
     var identity = new GenericIdentity(windowsIdentity.Name); 

     var groupNames = new string[0]; 
     if (windowsIdentity.Groups != null) 
     { 
      // Array of Group-Names in format DOMAIN\Group 
      groupNames = windowsIdentity.Groups 
             .Select(gId => gId.Translate(typeof(NTAccount))) 
             .Select(gNt => gNt.ToString()) 
             .ToArray(); 
     } 

     var genericPrincipal = new GenericPrincipal(identity, groupNames); 
     return genericPrincipal; 
    } 

    protected class DomainAuthCredentials 
    { 
     public DomainAuthCredentials(String domainUser, String password) 
     { 
      Username = domainUser; 
      Password = password; 
      Domain = "."; 

      if (!domainUser.Contains(@"\")) 
       return; 

      var tokens = domainUser.Split(new char[] { '\\' }, 2); 
      Domain = tokens[0]; 
      Username = tokens[1]; 
     } 

     public DomainAuthCredentials() 
     { 
      Domain = String.Empty; 
     } 

     #region Properties 

     public String Domain { get; set; } 
     public String Username { get; set; } 
     public String Password { get; set; } 

     #endregion 
    } 
} 

的登錄類型和LogonProvider枚舉反映「WINBASE.H」的定義。我使用LogonType.LOGON32_LOGON_BATCH而不是LogonType.LOGON32_LOGON_NETWORK解決了這個問題,因爲samba 3.4.X似乎遇到了這種類型的問題。

2

這是一個,我只是做了一個應用程序我工作的自己 - 需要框架V3.5或更高....

public static bool Authenticate(string user, string password) 
{ 
    // Split the user name in case a domain name was specified as DOMAIN\USER 
    string[] NamesArray = user.Split(new char[] { '\\' }, 2); 

    // Default vars for names & principal context type 
    string DomainName = string.Empty; 
    string UserName = string.Empty; 
    ContextType TypeValue = ContextType.Domain; 

    // Domain name was supplied 
    if (NamesArray.Length > 1) 
    { 
     DomainName = NamesArray[0]; 
     UserName = NamesArray[1]; 
    } 
    else 
    { 
     // Pull domain name from environment 
     DomainName = Environment.UserDomainName; 
     UserName = user; 

     // Check this against the machine name to pick up on a workgroup 
     if (string.Compare(DomainName, System.Environment.MachineName, StringComparison.InvariantCultureIgnoreCase) == 0) 
     { 
      // Use the domain name as machine name (local user) 
      TypeValue = ContextType.Machine; 
     } 
    } 

    // Create the temp context 
    using (PrincipalContext ContextObject = new PrincipalContext(TypeValue, DomainName)) 
    { 
     // Validate the credentials 
     return ContextObject.ValidateCredentials(UserName, password); 
    } 
} 
+0

謝謝天使的回覆。不幸的是,解決問題不是問題。您對當前的目錄服務器進行身份驗證。恕我直言,類System.DirectoryServices.AccountManagement.PrincipalContext不起作用,如果機器加入到沒有活動目錄的WIN2000/Samba域,或者即使它沒有加入任何域。 「advapi32.dll」中的「LogonUser」 - 方法對於每種情況都是透明的。 – 2013-01-16 07:52:38