[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,PeterROLE.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
<roleManagerdefaultProvider="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 建立內容頁面
…