活动目录访问和 IIS 身份验证方法问题

Active directory access and IIS Authetication method issue

我有一个 Intranet 应用程序可以根据 Active Directory 对用户进行身份验证。一般来说,它在本地测试时工作正常(即从我的开发机器使用 VS 2017)或 运行 在应用程序服务器("Browse *:80")中关闭 IIS,直到我尝试使用其 [=44= 访问它] 从我的本地机器。然后,无论我使用什么用户 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;
    }
}

WindowsIdentity.GetCurrent()会得到当前进程在运行下的用户账号。如果应用程序在 IIS 中是 运行,那通常是应用程序池所在的用户帐户 运行 - 而不是登录到您的应用程序的用户。

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

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

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

实际上有一种更简单的方法可以为用户获取 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 并请求 每个具有值 的属性。如果您只需要 3 个属性,那将是大量无用的数据通过网络传输。所以你可以告诉它在你使用它们之前只特别要求这 4 个属性:

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

我在一篇文章中谈到了这一点和其他要点:Active Directory: Better Performance

更新: 要验证 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 挑战。