自訂 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:

http://stackoverflow.com/questions/12186078/call-custom-membershipprovider-without-casting-membership-provider

留言

這個網誌中的熱門文章

DB 資料庫呈現復原中

Outlook 刪除大量重覆信件

[VB.Net] If vs IIf ,兩者的差異