65.9K
CodeProject 正在变化。 阅读更多。
Home

Active Directory / Forms Authentication / User.IsInRole

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.91/5 (5投票s)

2009年5月22日

CPOL

2分钟阅读

viewsIcon

43360

在使用 Forms Authentication 时,让 User.IsInRole 正常工作(使用 Active Directory 提供程序)

引言

通常你只需要使用

  1. 集成 Windows 身份验证(使用 Active Directory)
  2. 或者 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 "";
    }

参考文献

  • 这个网站为我提供了关于自定义提供程序的见解,尽管我更改了访问方法以处理嵌套组。
  • 这个链接为你提供了使用 Active Directory 进行 Forms Authentication 的基础知识(MSDN)。
© . All rights reserved.