C# 与SAM一起使用System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials时出现奇怪错误
我正在使用IIS 6.0托管一个WCF Web服务。我的应用程序池在本地管理员帐户下运行,我已定义了其他本地用户以访问Web服务。我编写了以下代码来验证用户:C# 与SAM一起使用System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials时出现奇怪错误,c#,.net-3.5,C#,.net 3.5,我正在使用IIS 6.0托管一个WCF Web服务。我的应用程序池在本地管理员帐户下运行,我已定义了其他本地用户以访问Web服务。我编写了以下代码来验证用户: //Any public static (Shared in Visual Basic) members of this type are thread safe public static PrincipalContext vpc; //static initializer static UserManagement() {
//Any public static (Shared in Visual Basic) members of this type are thread safe
public static PrincipalContext vpc;
//static initializer
static UserManagement()
{
vpc = new PrincipalContext(ContextType.Machine);
}
//Determines whether the given credentials are for a valid Administrator
public static bool validateAdminCredentials(string username, string password)
{
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Machine))
{
if (vpc.ValidateCredentials(username, password))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, username))
{
foreach (GroupPrincipal gp in user.GetGroups())
{
try
{
if (gp.Name.Equals("Administrators"))
{
return true;
}
}
finally
{
gp.Dispose();
}
}
}
}
return false;
} //end using PrincipalContext
}
…在另一类中:
//verify the user's password
if (!UserManagement.vpc.ValidateCredentials(username, password))
{
string errorMsg = string.Format("Invalid credentials received for user '{0}'", username);
throw new Exception(errorMsg);
}
(注意:我使用公共静态PrincipalContext(vpc)仅用于调用ValidateCredentials方法;我创建了一个不同的临时PrincipalContext用于创建、删除和查找用户和组,因为我在尝试使用全局PrincipalContext进行所有操作时遇到了各种与COM相关的错误)
因此,大多数情况下,这段代码工作得非常好。但是,我偶尔会遇到以下错误:
不允许同一用户使用多个用户名多次连接到服务器或共享资源。断开以前与服务器或共享资源的所有连接,然后重试。(来自HRESULT的异常:0x800704C3)
应用程序:
System.DirectoryServices.AccountManagement
堆栈跟踪:
位于System.DirectoryServices.AccountManagement.CredentialValidator.BindSam(字符串目标、字符串用户名、字符串密码)
位于System.DirectoryServices.AccountManagement.CredentialValidator.Validate(字符串用户名、字符串密码)
位于System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(字符串用户名、字符串密码)
在MyNamespace.User..ctor(字符串用户名、字符串密码)
一旦发生错误,我将继续获取它,直到重新启动整个服务器(不仅仅是IIS)。我已尝试重新启动应用程序池和/或IIS,但在重新启动计算机之前,错误不会消失。我还尝试(通过使用块)为每个ValidateCredentials调用实例化一个新的PrincipalContext(我不应该这么做),但最终还是得到了相同的错误。从我在System.DirectoryServices.AccountManagement(msdn文档,文章)上读到的内容来看,我相信我正确地使用了它,但这个错误正在削弱我的应用程序!我需要(并且应该能够)验证来自多个客户端的web服务请求的本地用户凭据。我做错什么了吗?如果您能帮助解决此问题,我们将不胜感激……我的建议是不要与SAM提供商一起使用ValidateCredentials。我相信它应该会起作用,但我也不奇怪它会遇到问题。我认为通过使用网络登录类型对LogonUser进行p/invoke以进行编程凭证验证,您会做得更好
我想知道微软是否有一个已知的问题和解决方案,但同时我建议只是围绕这个问题进行编码。如果您需要解决方案,即使Windows 2000和w/o作为系统运行代码,您也可以使用:
public static bool ValidateUser(
string userName,
string domain,
string password)
{
var tcpListener = new TcpListener(IPAddress.Loopback, 0);
tcpListener.Start();
var isLoggedOn = false;
tcpListener.BeginAcceptTcpClient(
delegate(IAsyncResult asyncResult)
{
using (var serverSide =
new NegotiateStream(
tcpListener.EndAcceptTcpClient(
asyncResult).GetStream()))
{
try
{
serverSide.AuthenticateAsServer(
CredentialCache.DefaultNetworkCredentials,
ProtectionLevel.None,
TokenImpersonationLevel.Impersonation);
var id = (WindowsIdentity)serverSide.RemoteIdentity;
isLoggedOn = id != null;
}
catch (InvalidCredentialException) { }
}
}, null);
var ipEndpoint = (IPEndPoint) tcpListener.LocalEndpoint;
using (var clientSide =
new NegotiateStream(
new TcpClient(
ipEndpoint.Address.ToString(),
ipEndpoint.Port).GetStream()))
{
try
{
clientSide.AuthenticateAsClient(
new NetworkCredential(
userName,
password,
domain),
"",
ProtectionLevel.None,
TokenImpersonationLevel.Impersonation);
}
catch(InvalidCredentialException){}
}
tcpListener.Stop();
return isLoggedOn;
}
public static bool ValidateCredentials(string username, string password)
{
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Machine))
{
return principalContext.ValidateCredentials(username, password);
}
}
public static bool validateAdminCredentials(string username, string password)
{
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Machine))
{
if (principalContext.ValidateCredentials(username, password))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, username))
using (GroupPrincipal group = GroupPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, "Administrators"))
{
if (null != group && user.IsMemberOf(group))
{
return true;
}
}
}
return false;
}
}
我通过使用域上下文解决了同样的问题。可以使用机器上下文来委托给域,但似乎最终将被迫遇到此问题 请注意,您应该为每个调用创建新的域上下文,因为它必须使用您要验证的用户的域来创建 我的应用程序也需要支持机器用户。我发现,只要用户是机器用户,域上下文就会抛出异常。如果捕获到异常,我将针对机器上下文工作