自訂 MembershipProvider
在 ASP.Net 開發中,會員的註冊、登入、登出、修改帳密等動作都可以透過 Membership 達成,是非常方便的元件
但是因為他非常的制式化,所以在功能上自然就是以滿足大眾的需求為主,這樣的話自然就會有種似乎不太夠的感覺
但是 asp.net 有提供 MembershipProvider 可以讓使用者繼承並實作他,增加各種自訂的擴展空間
而因為我主要仍是透過 regsql 這程式建構出來的資料庫,所以我直接透過繼承 SqlMembershipProvider 來寫作我自訂的 Membership
透過自訂的 MembershipProvider 你可以撰寫自訂的 Membership ,這樣的好處是像你的會員資料表已有資料,而一一轉成 regsql 訂出來的格式需較大的成本時,你就可以撰寫自訂的 MembershipProvider ,透過自訂的方法進行註冊使用者、驗證登入、取得使用者資訊 等動作
而我的系統因為是從新開始,所以可以配合 regsql 的格式,但我又希望能有些不一樣的衍伸性,所以就去找尋相關的文章撰寫自己的 SqlMembershipProvider
public class MyMembershipProvider : System.Web.Security.SqlMembershipProvider { private string m_applicationName; private int m_maxInvalidPasswordAttempts; private int m_passwordAttemptWindow; private int m_minRequiredNonAlphaNumericCharacters; private int m_minRequiredPasswordLength; private string m_passwordStregthRegularExpression; private bool m_enablePasswordReset; private bool m_enablePasswordRetrieval; private bool m_requiredQuestionAndAnswer; private bool m_requiredUniqueEmail; private MembershipPasswordFormat m_passwordFormat; private string m_connectionString; private MachineKeySection m_machinekey; public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (config == null) throw new ArgumentNullException("config"); if (string.IsNullOrEmpty(name)) name = "ApparatusMembershipProvider"; if (string.IsNullOrEmpty(config["description"])) { config.Remove("description"); config.Add("description", "Apparatus Membership(write by WCT)"); } m_applicationName = Functions.GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath); m_maxInvalidPasswordAttempts = Functions.GetConfigValue(config["maxInvalidPasswordAttempts"], 5); m_passwordAttemptWindow = Functions.GetConfigValue(config["passwordAttemptWindow"], 10); m_minRequiredNonAlphaNumericCharacters = Functions.GetConfigValue(config["minRequiredAlphaNumericCharacters"], 1); m_minRequiredPasswordLength = Functions.GetConfigValue(config["minRequiredPasswordLength"], 7); m_passwordStregthRegularExpression = Functions.GetConfigValue(config["passwordStrengthRegularExpression"], String.Empty); m_enablePasswordReset = Functions.GetConfigValue(config["enablePasswordReset"], true); m_enablePasswordRetrieval = Functions.GetConfigValue(config["enablePasswordRetrieval"], true); m_requiredQuestionAndAnswer = Functions.GetConfigValue(config["requiresQuestionAndAnswer"], false); m_requiredUniqueEmail = Functions.GetConfigValue(config["requiresUniqueEmail"], true); string temp_format = config["passwordFormat"]; if (temp_format == null) { temp_format = "Hashed"; } switch (temp_format) { case "Hashed": m_passwordFormat = MembershipPasswordFormat.Hashed; break; case "Encrypted": m_passwordFormat = MembershipPasswordFormat.Encrypted; break; case "Clear": m_passwordFormat = MembershipPasswordFormat.Clear; break; default: throw new ProviderException("Password format not supported."); } ConnectionStringSettings _connectionStringSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]]; if (_connectionStringSettings == null || _connectionStringSettings.ConnectionString.Length == 0) { throw new ProviderException("Connection String Cannot Be Blank."); } m_connectionString = _connectionStringSettings.ConnectionString; System.Configuration.Configuration cfg = WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath); m_machinekey = cfg.GetSection("system.web/machineKey") as MachineKeySection; if (m_machinekey.ValidationKey.Contains("AutoGenerate")) { if (PasswordFormat != MembershipPasswordFormat.Clear) { throw new ProviderException("Hashed or Encrypted passwords are not supported with auto-generated keys."); } } base.Initialize(name, config); } }
透過上面的程式碼就得到一個自訂的 MyMembershipProvider ,那目前雖然什麼沒有變更他其他的類別,不過已經取出 web.config 上的設定了,就可以透過那些東西直接連到指定的資料庫去存取資料表了
而另一個擴展則是自行撰寫相關的 Extensions ,我透過一個自訂的 UserInfo 類別來存取使用者的資訊,而透過自訂的 MembershipProviderExtensions 來取得使用者的帳號資料
public static class MembershipProviderExtensions { const string ItemsKey = "Membership_UserInfo"; public static UserInfo GetUserInfo(this MembershipProvider provider) { if (System.Web.HttpContext.Current == null) throw new NullReferenceException("本函數需在 Web 介面下方能使用!"); var UserInfo = HttpContext.Current.Items[ItemsKey] as UserInfo; if (UserInfo == null) { if (HttpContext.Current.User != null && HttpContext.Current.User.Identity != null && HttpContext.Current.User.Identity.IsAuthenticated) { var user = Membership.GetUser(HttpContext.Current.User.Identity.Name); var role = Roles.GetRolesForUser(HttpContext.Current.User.Identity.Name); UserInfo = new UserInfo() { name = HttpContext.Current.User.Identity.Name, isLogin = true, isAdmin = role.Contains("Administrators"), ProviderUserKey = (Guid)user.ProviderUserKey }; } else { UserInfo = new UserInfo() { isLogin = false }; } HttpContext.Current.Items[ItemsKey] = UserInfo; } return UserInfo; } }
當第一次被呼叫時,因為 UserInfo 是空的,所以透過 Membership.GetUser 取得使用者相關的資訊,而透過 Roles.GetRolesForUser 也能取得使用者相關的規則(群組)
而取得的資訊亦透過 HttpContext.Current.Items 存在這次的連線紀錄中,這次連線中的之後索取都不需重新取得使用者資訊及規則
以上是我的隨手筆記,可能很凌亂就是了... 因為這是避免我這健忘的人一下子又忘了所以作的整理XXXD
Reference:
留言
張貼留言