Active Directory访问和IIS身份验证方法问题

时间:2019-07-16 15:55:16

标签: c# active-directory iis-7.5

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

此外,某些组中的用户也可以使用此应用,因此应用会检查登录的用户组成员身份。

这是我在IIS和我测试过的不同方案中进行设置的方式:

  • 我将身份验证设置为“ Windows身份验证”,已禁用匿名 身份验证并启用了AS.NET模拟。当工作正常 使用本地主机从应用服务器运行但尝试访问时 从我的本地计算机上并提供用户的用户ID以获取他/她 详细信息,不显示任何详细信息(无法从中获取任何信息 广告)。
  • 如果我启用了匿名身份验证并将其设置为“应用程序池身份”(即网络服务),它将显示我的自定义 “访问被拒绝”页面,大概是因为该用户不属于 允许的组。
  • 如果我启用匿名身份验证并选择“特定用户”并提供我的凭据,则从应用程序服务器或本地计算机运行一切正常,但请注意:无论谁访问该网站,它都将我的名字显示为登录用户。

我陷入困境的广告不胜枚举。

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

获取用户身份的代码:

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

1 个答案:

答案 0 :(得分:0)

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

如果您使用IIS Express在本地进行调试,则IIS Express将以您的用户帐户运行,因此不会有任何区别。但是,您将在服务器上。

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

要获得登录您应用程序的用户,您应该使用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挑战。