我有一个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;
}
}
答案 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挑战。