簡體   English   中英

將 System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials 與 SAM 一起使用時出現奇怪的錯誤

[英]Strange Error When Using System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials with SAM

我正在使用 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()
{
    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
}

...在另一個 class 中:


//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);
}

(注意:我使用公共 static PrincipalContext (vpc) 僅用於調用 ValidateCredentials 方法;我創建了一個不同的臨時 PrincipalContext 來創建、刪除和查找用戶和組,因為我在嘗試使用時遇到了各種與 COM 相關的錯誤所有事物的全局 PrincipalContext )。

所以,大多數時候,這段代碼運行得非常好。 但是,間歇性地,我收到以下錯誤:

不允許同一用戶使用多個用戶名與服務器或共享資源建立多個連接。 斷開與服務器或共享資源的所有先前連接,然后重試。 (來自 HRESULT 的異常:0x800704C3)

應用程序:System.DirectoryServices.AccountManagement

堆棧跟蹤:在 System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password) 在 System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials 的 System.DirectoryServices.AccountManagement.CredentialValidator.BindSam(String target, String userName, String password) (字符串用戶名,字符串密碼)在 MyNamespace.User..ctor(字符串用戶名,字符串密碼)

一旦發生錯誤,我會繼續得到它,直到我重新啟動我的整個服務器(不僅僅是 IIS)。 我已經嘗試重新啟動我的應用程序池和/或 IIS,但在我重新啟動機器之前,錯誤不會消失 go。 我還嘗試為每次調用 ValidateCredentials (我不應該這樣做)實例化(通過 using 塊)一個新的 PrincipalContext,但我最終仍然得到相同的錯誤。 根據我在 System.DirectoryServices.AccountManagement (msdn docs,articles)上閱讀的內容,我相信我使用它是正確的,但是這個錯誤正在削弱我的應用程序。 我需要(並且應該能夠)驗證來自多個客戶端的 web 服務請求的本地用戶憑據? 難道我做錯了什么。 任何有關解決此問題的幫助將不勝感激...

  1. 任何這種類型的公共 static(在 Visual Basic 中共享)成員都是線程安全的”這個樣板文本讓很多人感到困惑。 這意味着該類型公開的任何 static 成員都是線程安全的。 這並不意味着存儲在 static 成員中的類型的任何實例都是線程安全的。

  2. 您的validateAdminCredentials方法會創建一個新的PrincipalContext object,然后繼續使用static vpc實例來驗證憑據。 由於您對 static 實例的訪問沒有鎖定,並且實例方法不是線程安全的,因此您最終將有兩個線程嘗試同時訪問同一個實例,這將不起作用。

嘗試刪除vpc字段並使用principalContext實例來驗證憑據。 您需要在每次調用時創建和處置PrincipalContext

此外,您可以使用IsMemberOf方法測試組中的成員資格,而不是手動迭代用戶的組。

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

我的建議是不要將 ValidateCredentials 與 SAM 提供程序一起使用。 我相信它應該可以工作,但我對它遇到問題也不感到驚訝。 我認為通過使用網絡登錄類型對 LogonUser 進行 ap/invoke 以進行編程憑據驗證,您會做得更好。

我想看看微軟是否有一個已知問題和解決這個問題的方法,但同時我建議只是圍繞它進行編碼。

如果您需要一個解決方案,即使使用 Windows 2000 並且沒有運行代碼作為系統,您也可以使用它:

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

我通過使用域上下文解決了同樣的問題。 可以使用機器上下文委托給域,但看起來您最終將被迫遇到這個問題。

請注意,您應該為每個調用創建新的域上下文,因為它必須使用您要驗證的用戶的域來創建。

我的應用程序也需要支持機器用戶。 我發現只要用戶是機器用戶,域上下文就會拋出異常。 如果我捕捉到異常,我會針對機器上下文工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM