在.NET应用程序中,我正在尝试通过用户名和密码对Windows用户,本地用户以及域用户进行身份验证。 I already tried this solution 。获取PrincipalContext的代码如下所示:
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域主服务器,例如。 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,我无法使用workgroup / domain-name,因为PrincipalContext尝试直接连接到计算机。如果已经有来自同一台机器的那台机器连接到那台机器,那就失败了。
所以我的问题是:
感谢您的回复。
答案 0 :(得分:4)
为了完整起见,我的解决方案似乎完全符合我的要求:
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
}
}
LogonType和LogonProvider枚举反映了“Winbase.h”中的定义。我决定使用LogonType.LOGON32_LOGON_BATCH而不是LogonType.LOGON32_LOGON_NETWORK,因为samba 3.4.X似乎遇到了这种类型的问题。
答案 1 :(得分:2)
以下是我刚为自己正在开发的应用所做的一项 - 需要Framework 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);
}
}