[Security]Custom Role Provider without Member Provider

[Security]Custom Role Provider without Member Provider

  • 導覽

1. 登入

  1.1. 建立 LoginPage.aspx 處理登入

  1.2. 設定 web.config authentication section

2. 授權

  2.1 人員與角色的對應讀取於INI中

  2.2 建立 Role Persist for 存取人員對應角色於記憶體中.

  2.3 建立 Role Provider 實作 System.Web.Security.RoleProvider

  2.4 設定 web.config 的 roleManager section

  2.5 設定 web.config 的 authorization section (含: location)

  2.6 建立 AccessDenied.aspx 處理無權限, 並修改LoginPage.aspx 導頁至 AccessDenied.aspx

3. 內容

  3.1 建立Web.sitemap

  3.2 設定 web.config 的 siteMap section

  3.3 建立內容頁面

  • 實作

1. 登入

  1.1. 建立 LoginPage.aspx 處理登入

<asp:Login ID="Login1" runat="server" onauthenticate="Login1_Authenticate">
</asp:Login>
 
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
            e.Authenticated = true; // always login
}

  1.2. 設定 web.config authentication section

      <authentication mode="Forms">
        <forms name=".CA_BKSTG_AUTH" loginUrl="LoginPage.aspx" defaultUrl="Index.aspx"></forms>        
      </authentication>

2. 授權

  2.1 人員與角色的對應讀取於INI中

# ~/App_Data/Role.ini
ROLE.A=John,Kevin,Peter
ROLE.B=Amy,Stacy

  2.2 建立 Role Persist for 存取人員對應角色於記憶體中.

    class MacroRolePersist
    {
        Dictionary<string, List<string>> persist = new Dictionary<string, List<string>>();
 
        public void Clear()
        {
            foreach (string role in persist.Keys)
            {
                persist[role].Clear();    
            }
            persist.Clear();
        }
 
        public void Load(string ini)
        {
            if (!File.Exists(ini))
            {
                throw new ArgumentException(ini + " not found");
            }
            using (StreamReader reader = new StreamReader(ini))
            {
                Properties prpts = new Properties();
                prpts.Load(reader);
                foreach (string name in prpts.PropertyNames())
                {
                    string[] names = Regex.Split(name, @"^ROLE\.", RegexOptions.IgnoreCase);
                    if (names != null && name.Length > 0)
                    {
                        string[] emps = Regex.Split(prpts[name], "[,;]");
                        AddEmployeesToRole(names[1], emps);
                    }
                }
            }            
        }
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="role"></param>
        /// <param name="emps"></param>
        public void AddEmployeesToRole(string role, params string[] emps)
        {
            // validation
            if (string.IsNullOrEmpty(role))
                return;
            if (emps == null || emps.Length == 0)
                return;
 
            // normalize users
            IEnumerable<string> noNullEmps = extractNoNull(emps);
            if (noNullEmps == null || noNullEmps.Count() == 0)
                return;
 
            if (!persist.ContainsKey(role))
            {
                // initial first role
                persist.Add(role, new List<string>(noNullEmps));
                return;
            }
 
            // add users in role
            addNoDuplicateEmp(role, noNullEmps);
        }
 
        /// <summary>
        /// Return no null and after trim strings.
        /// </summary>
        /// <param name="emps"></param>
        /// <returns></returns>
        private static IEnumerable<string> extractNoNull(string[] emps)
        {
            IEnumerable<string> noNullUsers = emps.Where(x => !string.IsNullOrEmpty(x)).Select(x => x.Trim());
            return noNullUsers;
        }
 
        /// <summary>
        /// Add users in role
        /// </summary>
        /// <param name="role"></param>
        /// <param name="noNullEmps"></param>
        private void addNoDuplicateEmp(string role, IEnumerable<string> noNullEmps)
        {
            List<string> employees = persist[role];
            IEnumerable<string> fulfillEmps = noNullEmps.Where(x => !employees.Contains(x));
            if (fulfillEmps == null || !fulfillEmps.Any())
                return;
            employees.AddRange(fulfillEmps);
        }
 
        /// <summary>
        /// Remove employees in role
        /// </summary>
        /// <param name="role"></param>
        /// <param name="emps"></param>
        public void RemoveEmployeesInRole(string role, params string[] emps)
        {
            if (string.IsNullOrEmpty(role))
                return;
            if (emps == null || emps.Length == 0)
                return;
            if (!persist.ContainsKey(role))
                return;
            
            // Remove employees in role (clear and bulk-insert)
            List<string> employees = persist[role];
            IEnumerable<string> noNullEmps = extractNoNull(emps);
            IEnumerable<string> susurvivor = employees.Where(x => !noNullEmps.Contains(x));
            employees.Clear();
            if (susurvivor == null || !susurvivor.Any())
                return;
            employees.AddRange(susurvivor);
        }
 
        /// <summary>
        /// Remove role
        /// </summary>
        /// <param name="role"></param>
        public void RemoveRole(string role)
        {
            if (string.IsNullOrEmpty(role))
                return;
            if (!persist.ContainsKey(role))
                return;
            // Clear values (employees)
            persist[role].Clear();
            // Remove key (role)
            persist.Remove(role);
        }
 
        public string[] GetAllRoles()
        {
            return persist.Keys.ToArray();
        }
 
        public string[] GetEmpsInRole(string role)
        {
            if (string.IsNullOrEmpty(role))
                return new string[0];
            if(!persist.ContainsKey(role))
                return new string[0];
            return persist[role].ToArray();
        }
        class IgnoreCaseEquality : IEqualityComparer<string>
        {
            public bool Equals(string x, string y)
            {
                if (x == null)
                    return y == null;
                return x.Equals(y, StringComparison.OrdinalIgnoreCase);
            }
 
            public int GetHashCode(string obj)
            {
                return obj.GetHashCode();
            }
        }
 
        public string[] GetRolesForUser(string emp)
        {
            if (string.IsNullOrEmpty(emp))
                return new string[0];
 
            string tEmp = emp.Trim();
            List<string> roles = new List<string>();
            
            foreach (string role in persist.Keys)
            {
                if (persist[role].Contains(tEmp,new IgnoreCaseEquality()))
                {
                    roles.Add(role);
                }
            }
            return roles.ToArray();
        }
 
    }

  2.3 建立 Role Provider 實作 System.Web.Security.RoleProvider

    public class MacroRoleProvider : System.Web.Security.RoleProvider
    {
        static MacroRolePersist persist = new MacroRolePersist();
 
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            foreach (string role in roleNames)
            {
                persist.AddEmployeesToRole(role, usernames);
            }
        }
 
        //
        // System.Web.Security.RoleProvider properties.
        //
        private string pApplicationName;
 
        public override string ApplicationName
        {
            get
            {
                return pApplicationName;
            }
            set
            {
                pApplicationName = value;
            }
        }
 
        //
        // System.Web.Security.RoleProvider properties.
        //
        private string pIniName;
 
        public string IniName
        {
            get
            {
                return pIniName;
            }
            set
            {
                pIniName = value;
            }
        }
 
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);
            if (config["applicationName"] == null || config["applicationName"].Trim() == "")
            {
                pApplicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
            }
            else
            {
                pApplicationName = config["applicationName"];
            }
 
            if (config["iniPath"] == null || config["iniPath"].Trim() == "")
            {
                pIniName = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "/App_Data/MacroRole.ini";
            }
            else
            {
                pIniName = config["iniPath"];
            }
            persist.Load(pIniName);
        }
 
        public override void CreateRole(string roleName)
        {
            persist.AddEmployeesToRole(roleName, null);
        }
 
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            persist.RemoveRole(roleName);
            return true;
        }
 
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            return GetUsersInRole(roleName).Where(x => x.IndexOf(usernameToMatch, StringComparison.OrdinalIgnoreCase) > -1).ToArray();
        }
 
        public override string[] GetAllRoles()
        {
            return persist.GetAllRoles();
        }
 
        public override string[] GetRolesForUser(string username)
        {
            return persist.GetRolesForUser(username);
        }
 
        public override string[] GetUsersInRole(string roleName)
        {
            return persist.GetEmpsInRole(roleName);
        }
 
        public override bool IsUserInRole(string username, string roleName)
        {
            return GetUsersInRole(roleName).Contains(username);
        }
 
        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            foreach (string role in roleNames)
            {
                persist.RemoveEmployeesInRole(role, usernames);
            }
        }
 
        public override bool RoleExists(string roleName)
        {
            return GetAllRoles().Any();
        }
    }

  2.4 設定 web.config 的 roleManager section

      <roleManager 
        defaultProvider="MacroRoleProvider"
        enabled="true" 
        cacheRolesInCookie="true"
        cookieName=".ROLES"
        cookieTimeout="30"
        cookiePath="/"
        cookieRequireSSL="false" 
        cookieSlidingExpiration="true" 
        cookieProtection="All" >
        <providers>
          <clear />
          <add name="MacroRoleProvider" type="MacroRoleProvider"/>
        </providers>
      </roleManager>

  2.5 設定 web.config 的 authorization section (含: location)

    <location path="Index.aspx">
      <system.web>
        <authorization>
          <allow roles="BZ,ADM" />
          <deny users="*" />
        </authorization>        
      </system.web>    
    </location>
 
    <system.web> 
      <authorization>
        <deny users="?" />        
      </authorization> 
    </system.web>

  2.6 建立 AccessDenied.aspx 處理無權限, 並修改LoginPage.aspx 導頁至 AccessDenied.aspx

ASPX:
您沒有權限執行此功能,請洽系統管理人員或<asp:LinkButton ID="login" 
            runat="server" onclick="login_Click">重新登入</asp:LinkButton>。
 
CS:
        protected void login_Click(object sender, EventArgs e)
        {
            System.Web.Security.FormsAuthentication.SignOut();
            Response.Redirect("~/LoginPage.aspx");
        }
---
    public partial class LoginPage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (User != null && User.Identity.IsAuthenticated)
            {
                Response.Redirect("~/AccessDenied.aspx");
            }
        }
 

3. 內容

  3.1 建立Web.sitemap

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode url="/" title="管理後台"  description="管理後台">
        <siteMapNode url="/ContentManagement.aspx" title="內容管理"  description="內容管理" />
        <siteMapNode url="/SystemManagement.aspx" title="系統管理"  description="系統管理" />
    </siteMapNode>
</siteMap>

  3.2 設定 web.config 的 siteMap section

      <siteMap defaultProvider="XmlSiteMapProvider" enabled="true">
        <providers>
          <add name="XmlSiteMapProvider" type="System.Web.XmlSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true"/>
        </providers>        
      </siteMap>

  3.3 建立內容頁面