Active Directory / Forms Authentication / User.IsInRole






3.91/5 (5投票s)
在使用 Forms Authentication 时,让 User.IsInRole 正常工作(使用 Active Directory 提供程序)
引言
通常你只需要使用
- 集成 Windows 身份验证(使用 Active Directory)
- 或者 Forms 身份验证
这适用于**罕见**的情况,即你想在 Forms Authentication 中使用 Active Directory 提供程序。
现在,使用 AD 提供程序实现 FormsAuthentication
并不难,但 .NET 最终并没有为当前用户填充 Roles,因此你无法使用 User.IsInRole
。
解决办法是创建你自己的自定义角色提供程序。
*********************************************************************
这是针对 ASP.NET 2.0 的。不确定是否在 3+ 版本中需要,并且肯定在 1.0 版本中无法工作。 *********************************************************************
背景
你需要对目录服务有扎实的理解才能充分利用它,因为代码中没有任何注释。
代码
Web.Config
好的,这里自定义的部分是角色管理器。type 属性应该是你类的完全限定名。我只是把我的类放在了 app_code 目录中,所以只需要类名。管理员用户/密码应该是具有在 Active Directory 上进行搜索权限的 Active Directory 用户。
你想要查看的是 roleManager
。如果你还没有使用 AD 进行 Forms Auth,你可能还需要查看连接字符串和成员资格提供程序区域。
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="ADConnectionString"
connectionString="LDAP://yourhost.com/DC=host,DC=com" />
</connectionStrings>
<system.web>
<roleManager enabled="true" cacheRolesInCookie="true"
defaultProvider="MyADRoleProvider"
cookieName=".ASPXROLES" cookiePath="/" cookieTimeout="30"
cookieRequireSSL="false"
cookieSlidingExpiration="true" createPersistentCookie="false"
cookieProtection="All">
<providers>
<add name="MyADRoleProvider"
type="CustomActiveDirectoryRoleProvider"
connectionStringName="ADConnectionString"
applicationName="/"
connectionUsername="adminUser"
connectionPassword="adminPassword" />
</providers>
</roleManager>
<compilation debug="true">
<assemblies>
<add assembly="System.DirectoryServices,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
<membership defaultProvider="MembershipADProvider">
<providers>
<add name="MembershipADProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ADConnectionString"
connectionUsername="adminUsername"
connectionPassword="adminPassword"
attributeMapUsername="sAMAccountName" />
</providers>
</membership>
<authentication mode="Forms">
<forms name=".ASPNET"/>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</configuration>
这是“令人印象深刻”的 Login.aspx 页面
<asp:Login ID="Login1" runat="server" DisplayRememberMe="false">
</asp:Login>
或者,如果你正在尝试创建一个直通登录页面,你可以以这种方式进行验证
string username, password;
if (Membership.ValidateUser(username, password)) {
FormsAuthentication.RedirectFromLoginPage(username, false);
}
接下来我们将看到提供程序。你只需要提供几个方法就可以让 User.IsInRole
函数正常工作。如果你想实现更多,那也可以。这是 CustomActiveDirectoryRoleProvider.cs 的基本所需信息
注意,这处理嵌套成员资格,并且必须跟踪它已经完成的组,以避免重复。
using System;
using System.Collections;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Web.Configuration;
using System.Web.Security;
/// <summary>
/// Summary description for CustomActiveDirectoryRoleProvider
/// </summary>
public class CustomActiveDirectoryRoleProvider : RoleProvider
{
string _connectionString = string.Empty;
string _applicationName = string.Empty;
string _userName = string.Empty;
string _userPassword = string.Empty;
public override void Initialize
(string name, System.Collections.Specialized.NameValueCollection config) {
_connectionString = config["connectionStringName"];
if (!string.IsNullOrEmpty(config["applicationName"]))
_applicationName = config["applicationName"];
if (!string.IsNullOrEmpty(config["connectionUsername"]))
_userName = config["connectionUsername"];
if (!string.IsNullOrEmpty(config["connectionPassword"]))
_userPassword = config["connectionPassword"];
base.Initialize(name, config);
}
public override string ApplicationName {
get {
return _applicationName;
}
set {
_applicationName = value;
}
}
public override string[] GetRolesForUser(string userName) {
userName = RemoveADGroup(userName);
return GetUserRoles(userName);
}
string RemoveADGroup(string name) {
string[] ary = name.Split(new char[] { '\\' });
return ary[ary.Length - 1];
}
string[] GetUserRoles(string userName) {
DirectoryEntry obEntry = new DirectoryEntry(
WebConfigurationManager.ConnectionStrings
[_connectionString].ConnectionString,
_userName, _userPassword);
DirectorySearcher srch = new DirectorySearcher
(obEntry, "(sAMAccountName=" + userName + ")");
SearchResult res = srch.FindOne();
Dictionary<string, string> dictionary = new Dictionary<string, string>();
if (null != res) {
DirectoryEntry obUser = new DirectoryEntry
(res.Path, _userName, _userPassword);
string rootPath = WebConfigurationManager.ConnectionStrings
[_connectionString].ConnectionString;
rootPath = rootPath.Substring(0,rootPath.LastIndexOf(@"/") + 1);
GetMemberships(obUser, dictionary, rootPath);
}
string[] ary = new string[dictionary.Count];
dictionary.Values.CopyTo(ary, 0);
return ary;
}
void GetMemberships(DirectoryEntry entry,
Dictionary<string, string> dictionary, string rootPath) {
List<DirectoryEntry> childrenToCheck = new List<DirectoryEntry>();
PropertyValueCollection children = entry.Properties["memberOf"];
foreach (string childDN in children) {
if (!dictionary.ContainsKey(childDN)) {
DirectoryEntry obGpEntry =
new DirectoryEntry(rootPath + childDN, _userName, _userPassword);
string groupName =
obGpEntry.Properties["sAMAccountName"].Value.ToString();
dictionary.Add(childDN, groupName);
childrenToCheck.Add(obGpEntry);
}
}
foreach (DirectoryEntry child in childrenToCheck) {
GetMemberships(child, dictionary, rootPath);
}
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames) {
throw new Exception("The method or operation is not implemented.");
}
public override void CreateRole(string roleName) {
throw new Exception("The method or operation is not implemented.");
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) {
throw new Exception("The method or operation is not implemented.");
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch) {
throw new Exception("The method or operation is not implemented.");
}
public override string[] GetAllRoles() {
throw new Exception("The method or operation is not implemented.");
}
public override string[] GetUsersInRole(string roleName) {
throw new Exception("The method or operation is not implemented.");
}
public override bool IsUserInRole(string username, string roleName) {
throw new Exception("The method or operation is not implemented.");
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) {
throw new Exception("The method or operation is not implemented.");
}
public override bool RoleExists(string roleName) {
throw new Exception("The method or operation is not implemented.");
}
}
关注点
这是我实现的其他一些方法,在我意识到除非我要使用 Roles
方法(例如:Roles.GetUsersInRole("blah\somerole")
)否则不需要它们之前。它们确实有效……你只需要它们来执行正常的 Page.User.IsInRole("blah\basdfasf");
public override string[] GetUsersInRole(string roleName) {
return GetGroupMembers(RemoveADGroup(roleName));
}
public override bool IsUserInRole(string username, string roleName) {
string[] ary = GetRolesForUser(username);
foreach (string s in ary) {
if (roleName.ToLower() == s.ToLower())
return true;
}
return false;
}
string[] GetGroupMembers(string groupName) {
DirectoryEntry obEntry = new DirectoryEntry(
WebConfigurationManager.ConnectionStrings[_connectionString].ConnectionString,
_userName, _userPassword);
DirectorySearcher srch = new DirectorySearcher
(obEntry, "(sAMAccountName=" + groupName + ")");
SearchResult res = srch.FindOne();
Dictionary<string, string> groups = new Dictionary<string, string>();
Dictionary<string, string> members = new Dictionary<string, string>();
if (null != res) {
DirectoryEntry entry = new DirectoryEntry(res.Path, _userName, _userPassword);
GetMembers(entry, groups, members);
}
string[] ary = new string[members.Count];
members.Values.CopyTo(ary, 0);
return ary;
}
void GetMembers(DirectoryEntry entry,
Dictionary<string, string> groups, Dictionary<string, string> members) {
List<DirectoryEntry> childrenToCheck = new List<DirectoryEntry>();
object children = entry.Invoke("Members", null);
foreach (object childObject in (IEnumerable)children) {
DirectoryEntry child = new DirectoryEntry(childObject);
string type = getEntryType(child);
if (type == "G" && !groups.ContainsKey(child.Path)) {
childrenToCheck.Add(child);
} else if (type == "P" && !members.ContainsKey(child.Path)) {
members.Add(child.Path, child.Properties
["sAMAccountName"].Value.ToString());
}
}
foreach (DirectoryEntry child in childrenToCheck) {
GetMembers(child, groups, members);
}
}
string getEntryType(DirectoryEntry inEntry) {
if (inEntry.Properties.Contains("objectCategory")) {
string fullValue = inEntry.Properties["objectCategory"].Value.ToString();
if (fullValue.StartsWith("CN=Group"))
return "G";
else if (fullValue.StartsWith("CN=Person"))
return "P";
}
return "";
}