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

