【问题标题】:How can I check a user/password combination on an ActiveDirectory without putting the password in a String?如何在不将密码放入字符串的情况下检查 ActiveDirectory 上的用户/密码组合?
【发布时间】:2011-04-05 06:51:39
【问题描述】:

我想检查 Windows 域上的用户/密码组合。现在我用下面的代码来做:

bool Login(String username, String password) {
    var principalContext = new PrincipalContext(ContextType.Domain);
    principalContext.ValidateCredentials(username, password);
}

虽然它有效,但让我烦恼的是,我必须将密码放入 String 才能使用该 API;由于我使用SecureString 将密码存储在其他任何地方,我真的很想使用某种方式检查用户名/密码组合,而不必将密码作为托管System.String 传递。

实现这一目标的最佳方法是什么?

【问题讨论】:

    标签: .net active-directory directoryservices


    【解决方案1】:

    您可以尝试的一种方法可能是:

    通过使用 P/Invoke 调用 LoginUser 来模拟用户,将密码作为 SecureString as described in MSDN 传递。

    使用模拟用户连接到 ActiveDirectory,无需传递用户名和密码:

    AuthenticationTypes authenticationTypes = AuthenticationTypes.Secure;
    
    using (var entry = new DirectoryEntry("LDAP://example.com", "", "", authenticationTypes))
    {
        ...
    }
    

    我还没有尝试过,但在我看来它应该可以工作。

    【讨论】:

    【解决方案2】:

    使用DsBindWithCred。请注意,即使凭据在技术上有效(例如帐户被锁定),此功能也会因访问被拒绝而失败。如果您想要该级别的详细信息,则必须使用 LogonUser 函数,但每次调用都将计为一次登录尝试。

    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Text;
    
    public class PInvoke
    {
        public static bool TestCreds(string usernamePossiblyWithDomain, 
                                     SecureString password, 
                                     string dnsDomainName)
        {
            string username, usernameDomain;
            ParseUserName(usernamePossiblyWithDomain, out username, out usernameDomain);
    
            IntPtr pPass = Marshal.SecureStringToGlobalAllocUnicode(password);
    
            try
            {
                IntPtr hDS = IntPtr.Zero;
                IntPtr authID = MakePassCreds(username, usernameDomain, pPass);
                //if you're really paranoid, you can uncomment the next two lines
                //to zero out the memory as soon as possible
                //Marshal.ZeroFreeGlobalAllocUnicode(pPass);
                //pPass = IntPtr.Zero;
    
                try
                {
                    int lastErr = DsBindWithCred(null, dnsDomainName, authID, ref hDS);
                    switch(lastErr)
                    {
                        case 0: return true;  //ERROR_SUCCESS
                        case 5: return false; //ERROR_ACCESS_DENIED
                        default: throw new Win32Exception(lastErr);
                    }
                }
                finally
                {
                    if(hDS != IntPtr.Zero) DsUnBind(ref hDS);
                    if(authID != IntPtr.Zero) DsFreePasswordCredentials(authID);
                }
            }
            finally
            {
                if(pPass != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(pPass);
            }
        }
    
        [DllImport("credui.dll", CharSet = CharSet.Unicode)]
        protected static extern int CredUIParseUserName(string pszUserName,
                                        StringBuilder pszUser,   int ulUserMaxChars, 
                                        StringBuilder pszDomain, int ulDomainMaxChars);
    
        public static void ParseUserName(string usernamePossiblyWithDomain, 
                                         out string username, out string domain)
        {
            int MaxUserChars = 256, maxDomainChars = 256;
            StringBuilder sbUser = new StringBuilder(maxUserChars);
            StringBuilder sbDomain = new StringBuilder(maxDomainChars);
            int lastErr = CredUIParseUserName(usernamePossiblyWithDomain, sbUser,
                                     maxUserChars - 1, sbDomain, maxDomainChars - 1);
            if(lastErr != 0) throw new Win32Exception(lastErr);
            username = sbUser.ToString();
            domain = sbDomain.ToString();
        }
    
        [DllImport("ntdsapi.dll", CharSet = CharSet.Unicode)]
        protected static extern int DsMakePasswordCredentials(
            string User, string Domain, IntPtr Password, ref IntPtr pAuthIdentity);
    
        [DllImport("ntdsapi.dll")]
        public static extern int DsFreePasswordCredentials(IntPtr AuthIdentity);
    
        //caller is responsible for calling DsFreePasswordCredentials on the return val
        public static IntPtr MakePassCreds(string username, string domain, IntPtr pPass)
        {
            IntPtr auth = IntPtr.Zero;
            int lastErr = DsMakePasswordCredentials(username, domain, pPass, ref auth);
            if(lastErr != 0) throw new Win32Exception(lastErr);
            return auth;
        }
    
        [DllImport("ntdsapi.dll", CharSet = CharSet.Unicode)]
        protected static extern int DsBindWithCred(string DomainControllerName, 
                            string DnsDomainName, IntPtr AuthIdentity, ref IntPtr phDS);
    
        [DllImport("ntdsapi.dll")]
        public static extern int DsUnBind(ref IntPtr phDS);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-06
      • 1970-01-01
      相关资源
      最近更新 更多