c# - 活动目录访问和 IIS 身份验证方法问题
问题描述
我有一个针对活动目录对用户进行身份验证的 Intranet 应用程序。通常,在本地测试(即使用 VS 2017 从我的开发机器)或在应用服务器中从 IIS 运行它(“浏览 *:80”)时它工作正常,直到我尝试使用本地机器上的 URL 访问它。然后,无论我使用什么用户 ID 来获取用户的详细信息,都不会显示任何内容。
此外,某些组中的用户可以访问此应用程序,因此应用程序检查登录用户的组成员身份。
以下是我在 IIS 中设置它的方式以及我测试过的不同场景:
- 我将身份验证设置为“Windows 身份验证”,禁用匿名身份验证并启用 AS.NET 模拟。这在使用 localhost 从应用服务器运行时工作正常,但是当尝试从我的本地计算机访问并提供用户的用户 ID 以获取他/她的详细信息时,不显示任何详细信息(无法从 AD 获取任何信息)。
- 如果我启用匿名身份验证并将其设置为“应用程序池标识”(即网络服务),它会显示我的自定义“拒绝访问”页面,可能是因为该用户不是允许组的一部分。
- 如果我启用匿名身份验证并选择“特定用户”,提供我的凭据,那么一切正常,从应用程序服务器或我的本地计算机,有一个警告:无论谁访问该站点,它都会显示我的名字作为登录用户.
我很难过广告将不胜感激一些提示。
更新 - 添加了获取用户信息的代码
获取用户身份的代码:
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()
应该返回登录的用户 - 因为模拟意味着您的应用现在假装是那个人。所以这可能意味着模拟设置不正确。但也许你甚至不需要它。我个人从未发现需要使用模拟。
要获取登录到您的应用程序的用户,您应该使用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 挑战。
推荐阅读
- laravel - Laravel 传递参数时返回 404 错误
- excel - 将循环结果复制并粘贴到另一张工作表的列中
- ios - 获取 MPMediaPlaylist 上次修改日期
- c# - 有没有办法从具有特定属性的列表中获取所有项目
- javascript - How i can create a new string with two reversed last letters of another string?
- javascript - 使用 Node.js 以编程方式删除 Firebase 用户
- button - font-awesome 字体阻止可点击下拉菜单在点击按钮外边缘以外的任何地方时工作?
- c# - 返回特定字符串 Any() 正在查找
- algorithm - 具有多种元素的霍夫曼代码
- powershell - 如何以管理员身份在安全模式下在启动时运行 powershell 脚本