首页 > 解决方案 > 活动目录访问和 IIS 身份验证方法问题

问题描述

我有一个针对活动目录对用户进行身份验证的 Intranet 应用程序。通常,在本地测试(即使用 VS 2017 从我的开发机器)或在应用服务器中从 IIS 运行它(“浏览 *:80”)时它工作正常,直到我尝试使用本地机器上的 URL 访问它。然后,无论我使用什么用户 ID 来获取用户的详细信息,都不会显示任何内容。

此外,某些组中的用户可以访问此应用程序,因此应用程序检查登录用户的组成员身份。

以下是我在 IIS 中设置它的方式以及我测试过的不同场景:

我很难过广告将不胜感激一些提示。

更新 - 添加了获取用户信息的代码

获取用户身份的代码:

WindowsIdentity wiUser = WindowsIdentity.GetCurrent();
string sID = wiUser.Name.ToUpper();

获取用户广告信息的代码:

static string adDomain = "www.xxx.yyy.zzz";
static string adContainer = "DC=www,DC=xxx,DC=yyy,DC=zzz";

public static DataTable getUserADInfoDT(string sSearchStr)
{
    DataTable dtResults = new DataTable();
    dtResults.Columns.Add("ID");
    dtResults.Columns.Add("FirstName");
    ...
    dtResults.Columns.Add("Zip");

    string adDomain = string.Empty;
    string adContainer = string.Empty;

    // create domain context
    PrincipalContext adPrincipalContext = new PrincipalContext(ContextType.Domain, adDomain, adContainer);

    using (adPrincipalContext)
    {
        // define a "query-by-example" principal 
        UserPrincipal qbeUser = new UserPrincipal(adPrincipalContext);
        qbeUser.SamAccountName = sSearchStr.Trim().ToUpper();

        // create principal searcher passing in the QBE principal    
        PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
        PrincipalSearchResult<Principal> psr = srch.FindAll();

        // find all matches
        foreach (var found in psr)
        {
            DataRow dr = dtResults.NewRow();
            UserPrincipal up = (UserPrincipal)found;
            DirectoryEntry de = (DirectoryEntry)up.GetUnderlyingObject();

            dr["ID"] = de.Properties["SAMAccountName"].Value.ToString().ToUpper(); 

            if (de.Properties["givenName"].Value != null) 
                dr["FirstName"] = de.Properties["givenName"].Value.ToString();

            ...

            if (de.Properties["postalCode"].Value != null)
                dr["Zip"] = de.Properties["postalCode"].Value.ToString();

            dtResults.Rows.Add(dr);
            //de.Close();
        }
        return dtResults;
    }
}

标签: c#active-directoryiis-7.5

解决方案


WindowsIdentity.GetCurrent()将获取当前进程正在运行的用户帐户。如果应用程序在 IIS 中运行,那通常是运行应用程序池的用户帐户,而不是登录到您的应用程序的用户。

如果您使用 IIS Express 在本地进行调试,那么 IIS Express 是在您的用户帐户下运行的,因此您不会看到差异。但是在服务器上你会的。

也就是说,如果您打开了模拟并且工作正常,那么WindowsIdentity.GetCurrent()应该返回登录的用户 - 因为模拟意味着您的应用现在假装是那个人。所以这可能意味着模拟设置不正确。但也许你甚至不需要它。我个人从未发现需要使用模拟。

要获取登录到您的应用程序的用户,您应该使用HttpRequest.Current.User或者可能只是HttpContext.User您的应用程序是 ASP.NET MVC。

实际上有一种更简单的方法可以DirectoryEntry为用户获取对象,而无需搜索用户名。您可以直接绑定到用户的 SID,这是您已经拥有的信息:

var user = new DirectoryEntry(
    $"LDAP://<SID={((WindowsIdentity) HttpContext.User.Identity).User.Value}>");

如果您的服务器未与您的用户加入同一个域,那么您可能需要包含域名:

var user = new DirectoryEntry(
    $"LDAP://www.xxx.yyy.zzz/<SID={((WindowsIdentity) HttpContext.User.Identity).User.Value}>");

还有一点要记住:DirectoryEntry保留所有Properties. 当您访问缓存中没有的属性时,它会发送到 AD 并请求每个具有 value 的属性。如果您只需要 3 个属性,那将是通过网络传输的大量无用数据。因此,您可以告诉它在使用它们之前只专门询问这 4 个属性:

user.RefreshCache(new[] {"SAMAccountName", "givenName", "postalCode"});

我在我写的一篇文章中谈到了这一点和其他要点:Active Directory:更好的性能

更新:要验证 IIS 是否实际上对页面强制执行身份验证,您可以在 PowerShell 中执行此操作(将 URL 更新为您需要的内容 - 这至少需要 PowerShell 3):

try { (Invoke-WebRequest "http://example.com/the/page").StatusCode }
    catch { $_.Exception.Response.StatusCode.Value__}

这将输出回复的状态码。您应该看到401,因为这是 IIS 挑战浏览器发送凭据的方式。如果您看到200,则 Windows 身份验证不适用于该页面。

我在 PowerShell 中执行此操作,因为 Chrome 的开发工具甚至不会向您展示 401 挑战。


推荐阅读