zoukankan      html  css  js  c++  java
  • 重写MembershipProvider用于事务处理(一)

        当使用MembershpProvider类创建新用户的时候,如果创建用户的同时还需要对数据库进行一些其他的操作,而又想把这些额外的操作同创建用户的动作融为一体,作为一个事务来处理的话,就要重写MembershipProvider了.还好M$提供了SqlMembershipProvider的源代码(http://download.microsoft.com/download/a/b/3/ab3c284b-dc9a-473d-b7e3-33bacfcc8e98/ProviderToolkitSamples.msi),在此基础上,我们就能更好的重写这个类了.

         需求说明:Employee表中存放员工信息,需要根据员工信息创建用户,每个员工对应一个或0个用户.只有已经存在的员工,才能为其创建用户.在创建用户的同时需要更新Employee表,将些员工的UserName字段置为刚刚创建的用户的UserName.需要把上述两步放在同一个Transaction中,如果有一个不成功,则全部RollBack.同理,删除用户时也应进行相应的操作.

         具体作法:

             新建一个Class Library类型的工程,工程改名为myProvider.MembershipProvider.添加一个类mySqlMembershipProvider,最好给他指定一个命名空间.我指定的是myProvider.这个类要继承于M$的SqlMembershipProvider.再添加两个类,这两个类可以从你下到的源代码中找到,分别是SecUtil.cs和SR.cs(如果没下到,我会把这两个类的代码放到后面,重写MembershipProvider用于事务处理(二)中),因为要用到这两个类中某些方法.

    ////////////////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////mySqlMembershipProvider.cs///////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////

    using System;
    using System.Web.Security;
    using System.Web;
    using System.Web.Configuration;
    using System.Security.Principal;
    using System.Security.Permissions;
    using System.Globalization;
    using System.Runtime.Serialization;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Data;
    using System.Data.SqlClient;
    using System.Data.SqlTypes;
    using System.Security.Cryptography;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Configuration.Provider;
    using System.Configuration;
    using System.Web.DataAccess;
    using System.Web.Management;
    using System.Web.Util;

    namespace  myProvider
    {
        /// <summary>
        /// Summary description for mySqlMembershipProvider
        /// </summary>
        public class mySqlMembershipProvider: SqlMembershipProvider
        {
            private string _sqlConnectionString;
            private int _SchemaVersionCheck;
            private int _CommandTimeout;
            private MembershipPasswordFormat _PasswordFormat;

            public CRM2006MembershipProvider()
            {
                //
                // TODO: Add constructor logic here
                //
            }
            public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
            {
                _CommandTimeout = SecUtility.GetIntValue(config, "commandTimeout", 30, true, 0);
                _SchemaVersionCheck = 0;
                string temp = config["connectionStringName"];
                if (temp == null || temp.Length < 1)
                    throw new ProviderException(SR.GetString(SR.Connection_name_not_specified));
                _sqlConnectionString = null;
                ConnectionStringSettings connObj = ConfigurationManager.ConnectionStrings[temp];
                if (connObj != null)
                    _sqlConnectionString = connObj.ConnectionString;
                string strTemp = config["passwordFormat"];
                if (strTemp == null)
                    strTemp = "Hashed";

                switch (strTemp)
                {
                    case "Clear":
                        _PasswordFormat = MembershipPasswordFormat.Clear;
                        break;
                    case "Encrypted":
                        _PasswordFormat = MembershipPasswordFormat.Encrypted;
                        break;
                    case "Hashed":
                        _PasswordFormat = MembershipPasswordFormat.Hashed;
                        break;
                    default:
                        throw new ProviderException(SR.GetString(SR.Provider_bad_password_format));
                }

                base.Initialize(name, config);
            }
            /// <summary>
            /// Adds a new user to the SQL Server membership database for a employee.
            /// </summary>
            /// <param name="username">The user name and employeId for the new user.Format="username|employeId"</param>
            /// <param name="password">The password for the new user.</param>
            /// <param name="email">The e-mail address for the new user.</param>
            /// <param name="passwordQuestion">The password question for the new user.</param>
            /// <param name="passwordAnswer">The password answer for the new user.</param>
            /// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
            /// <param name="providerUserKey">A System.Guid that uniquely identifies the membership user in the SQL Server database.</param>
            /// <param name="status">One of the System.Web.Security.MembershipCreateStatus values, indicating whether the user was created successfully.</param>
            /// <returns>A System.Web.Security.MembershipUser object for the newly created user. If  no user was created, this method returns null.</returns>
            public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
            {
                string[] strParms = username.Split('|');
                username = strParms[0];
                int employeId = 0;
                try
                {
                    employeId = int.Parse(strParms[1]);
                }
                catch
                {
                    employeId = 0;
                }

                if (!SecUtility.ValidateParameter(ref password, true, true, false, 128))
                {
                    status = MembershipCreateStatus.InvalidPassword;
                    return null;
                }

                string salt = GenerateSalt();
                string pass = EncodePassword(password, (int)_PasswordFormat, salt);
                if (pass.Length > 128)
                {
                    status = MembershipCreateStatus.InvalidPassword;
                    return null;
                }

                string encodedPasswordAnswer;
                if (passwordAnswer != null)
                {
                    passwordAnswer = passwordAnswer.Trim();
                }

                if (!string.IsNullOrEmpty(passwordAnswer))
                {
                    if (passwordAnswer.Length > 128)
                    {
                        status = MembershipCreateStatus.InvalidAnswer;
                        return null;
                    }
                    encodedPasswordAnswer = EncodePassword(passwordAnswer.ToLower(CultureInfo.InvariantCulture), (int)_PasswordFormat, salt);
                }
                else
                    encodedPasswordAnswer = passwordAnswer;
                if (!SecUtility.ValidateParameter(ref encodedPasswordAnswer, RequiresQuestionAndAnswer, true, false, 128))
                {
                    status = MembershipCreateStatus.InvalidAnswer;
                    return null;
                }

                if (!SecUtility.ValidateParameter(ref username, true, true, true, 256))
                {
                    status = MembershipCreateStatus.InvalidUserName;
                    return null;
                }

                if (!SecUtility.ValidateParameter(ref email,
                                                   RequiresUniqueEmail,
                                                   RequiresUniqueEmail,
                                                   false,
                                                   256))
                {
                    status = MembershipCreateStatus.InvalidEmail;
                    return null;
                }

                if (!SecUtility.ValidateParameter(ref passwordQuestion, RequiresQuestionAndAnswer, true, false, 256))
                {
                    status = MembershipCreateStatus.InvalidQuestion;
                    return null;
                }

                if (providerUserKey != null)
                {
                    if (!(providerUserKey is Guid))
                    {
                        status = MembershipCreateStatus.InvalidProviderUserKey;
                        return null;
                    }
                }

                if (password.Length < MinRequiredPasswordLength)
                {
                    status = MembershipCreateStatus.InvalidPassword;
                    return null;
                }

                int count = 0;

                for (int i = 0; i < password.Length; i++)
                {
                    if (!char.IsLetterOrDigit(password, i))
                    {
                        count++;
                    }
                }

                if (count < MinRequiredNonAlphanumericCharacters)
                {
                    status = MembershipCreateStatus.InvalidPassword;
                    return null;
                }

                if (PasswordStrengthRegularExpression.Length > 0)
                {
                    if (!Regex.IsMatch(password, PasswordStrengthRegularExpression))
                    {
                        status = MembershipCreateStatus.InvalidPassword;
                        return null;
                    }
                }

                ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, password, true);
                OnValidatingPassword(e);

                if (e.Cancel)
                {
                    status = MembershipCreateStatus.InvalidPassword;
                    return null;
                }

                SqlConnection Connection;
                try
                {
                    Connection = new SqlConnection(_sqlConnectionString);
                    Connection.Open();
                }
                catch (ArgumentException ex)
                {
                    throw new ArgumentException(SR.GetString(SR.SqlError_Connection_String), "_sqlConnectionString", ex);
                }

                CheckSchemaVersion(Connection);
                DateTime dt = RoundToSeconds(DateTime.UtcNow);
                //create user
                SqlCommand cmd = new SqlCommand("dbo.aspnet_Membership_CreateUser", Connection);

                cmd.CommandTimeout = CommandTimeout;
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add(CreateInputParam("@ApplicationName", SqlDbType.NVarChar, ApplicationName));
                cmd.Parameters.Add(CreateInputParam("@UserName", SqlDbType.NVarChar, username));
                cmd.Parameters.Add(CreateInputParam("@Password", SqlDbType.NVarChar, pass));
                cmd.Parameters.Add(CreateInputParam("@PasswordSalt", SqlDbType.NVarChar, salt));
                cmd.Parameters.Add(CreateInputParam("@Email", SqlDbType.NVarChar, email));
                cmd.Parameters.Add(CreateInputParam("@PasswordQuestion", SqlDbType.NVarChar, passwordQuestion));
                cmd.Parameters.Add(CreateInputParam("@PasswordAnswer", SqlDbType.NVarChar, encodedPasswordAnswer));
                cmd.Parameters.Add(CreateInputParam("@IsApproved", SqlDbType.Bit, isApproved));
                cmd.Parameters.Add(CreateInputParam("@UniqueEmail", SqlDbType.Int, RequiresUniqueEmail ? 1 : 0));
                cmd.Parameters.Add(CreateInputParam("@PasswordFormat", SqlDbType.Int, (int)PasswordFormat));
                cmd.Parameters.Add(CreateInputParam("@CurrentTimeUtc", SqlDbType.DateTime, dt));
                SqlParameter p = CreateInputParam("@UserId", SqlDbType.UniqueIdentifier, providerUserKey);
                p.Direction = ParameterDirection.InputOutput;
                cmd.Parameters.Add(p);

                p = new SqlParameter("@ReturnValue", SqlDbType.Int);
                p.Direction = ParameterDirection.ReturnValue;
                cmd.Parameters.Add(p);

                SqlTransaction trans = Connection.BeginTransaction();
                cmd.Transaction = trans;


                //create profile for the user
                string sqlCreateProfile = "INSERT INTO Profile (EmployeeID,Username, ApplicationName, LastActivityDate, LastUpdatedDate, IsAnonymous) Values(@EmployeeID,@Username, @ApplicationName, @LastActivityDate, @LastUpdatedDate, @IsAnonymous)";
                SqlCommand cmdCreateProfile = new SqlCommand(sqlCreateProfile, Connection);
                cmdCreateProfile.Parameters.Add(CreateInputParam("@EmployeeID", SqlDbType.Int, employeId));
                cmdCreateProfile.Parameters.Add(CreateInputParam("@Username", SqlDbType.NVarChar, username));
                cmdCreateProfile.Parameters.Add(CreateInputParam("@ApplicationName", SqlDbType.NVarChar, ApplicationName));
                cmdCreateProfile.Parameters.Add(CreateInputParam("@LastActivityDate", SqlDbType.DateTime, DateTime.Now));
                cmdCreateProfile.Parameters.Add(CreateInputParam("@LastUpdatedDate", SqlDbType.DateTime, DateTime.Now));
                cmdCreateProfile.Parameters.Add(CreateInputParam("@IsAnonymous", SqlDbType.Bit, false));
                cmdCreateProfile.Transaction = trans;

                //
                int iStatus = -1;
                try
                {
                    cmd.ExecuteNonQuery();
                    iStatus = ((p.Value != null) ? ((int)p.Value) : -1);
                    if (iStatus < 0 || iStatus > (int)MembershipCreateStatus.ProviderError)
                        iStatus = (int)MembershipCreateStatus.ProviderError;
                    status = (MembershipCreateStatus)iStatus;
                    if (iStatus != 0) // !success
                    {
                        trans.Rollback();
                        Connection.Close();
                        return null;
                    }
                    cmdCreateProfile.ExecuteNonQuery();
                    trans.Commit();
                    Connection.Close();
                }
                catch
                {
                    status = (MembershipCreateStatus)iStatus;
                    trans.Rollback();
                    Connection.Close();
                }
                providerUserKey = new Guid(cmd.Parameters["@UserId"].Value.ToString());
                dt = dt.ToLocalTime();
                return new MembershipUser(this.Name,
                                           username,
                                           providerUserKey,
                                           email,
                                           passwordQuestion,
                                           null,
                                           isApproved,
                                           false,
                                           dt,
                                           dt,
                                           dt,
                                           dt,
                                           new DateTime(1754, 1, 1));
            }


            private SqlParameter CreateInputParam(string paramName,
                                               SqlDbType dbType,
                                               object objValue)
            {

                SqlParameter param = new SqlParameter(paramName, dbType);

                if (objValue == null)
                {
                    param.IsNullable = true;
                    param.Value = DBNull.Value;
                }
                else
                {
                    param.Value = objValue;
                }

                return param;
            }
            private void CheckSchemaVersion(SqlConnection connection)
            {
                string[] features = { "Common", "Membership" };
                string version = "1";

                SecUtility.CheckSchemaVersion(this,
                                               connection,
                                               features,
                                               version,
                                               ref _SchemaVersionCheck);
            }
            private DateTime RoundToSeconds(DateTime dt)
            {
                return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second);
            }
            private int CommandTimeout
            {
                get { return _CommandTimeout; }
            }
            ///// <summary>
            ///// Removes a user's membership information from the SQL Server membership database.
            ///// </summary>
            ///// <param name="username">The name of the user to delete.</param>
            ///// <param name="deleteAllRelatedData">true to delete data related to the user from the database; false to leave data related to the user in the database.</param>
            ///// <returns>true if the user was deleted; otherwise, false. A value of false is also returned if the user does not exist in the database.</returns>
            //public override bool DeleteUser(string username, bool deleteAllRelatedData)
            //{
            //    SecUtility.CheckParameter(ref username, true, true, true, 256, "username");
            //    try
            //    {
            //        SqlConnection Connection;
            //        try
            //        {
            //            Connection = new SqlConnection(_sqlConnectionString);
            //            Connection.Open();
            //        }
            //        catch (ArgumentException ex)
            //        {
            //            throw new ArgumentException(SR.GetString(SR.SqlError_Connection_String), "_sqlConnectionString", ex);
            //        }
            //        CheckSchemaVersion(Connection);
            //        //delete user
            //        SqlCommand cmd = new SqlCommand("dbo.aspnet_Users_DeleteUser", Connection);

            //        cmd.CommandTimeout = CommandTimeout;
            //        cmd.CommandType = CommandType.StoredProcedure;
            //        cmd.Parameters.Add(CreateInputParam("@ApplicationName", SqlDbType.NVarChar, ApplicationName));
            //        cmd.Parameters.Add(CreateInputParam("@UserName", SqlDbType.NVarChar, username));

            //        if (deleteAllRelatedData)
            //        {
            //            cmd.Parameters.Add(CreateInputParam("@TablesToDeleteFrom", SqlDbType.Int, 0xF));
            //        }
            //        else
            //        {
            //            cmd.Parameters.Add(CreateInputParam("@TablesToDeleteFrom", SqlDbType.Int, 1));
            //        }

            //        SqlParameter p = new SqlParameter("@NumTablesDeletedFrom", SqlDbType.Int);
            //        p.Direction = ParameterDirection.Output;
            //        cmd.Parameters.Add(p);

            //        SqlTransaction trans = Connection.BeginTransaction();
            //        cmd.Transaction = trans;


            //        //update employee
            //        string sqlUpdateEmployee = "UPDATE Employee SET UserName=null WHERE UserName=@username";
            //        SqlCommand cmdUpdateEmployee = new SqlCommand(sqlUpdateEmployee, Connection);
            //        cmdUpdateEmployee.Parameters.Add(CreateInputParam("@Username", SqlDbType.NVarChar, username));
            //        cmdUpdateEmployee.Transaction = trans;

            //        try
            //        {
            //            cmd.ExecuteNonQuery();
            //            int status = ((p.Value != null) ? ((int)p.Value) : -1);
            //            if (status <= 0)
            //            {
            //                trans.Rollback();
            //                Connection.Close();
            //                return false;
            //            }
            //            cmdUpdateEmployee.ExecuteNonQuery();
            //            trans.Commit();
            //            Connection.Close();
            //        }
            //        catch
            //        {
            //            trans.Rollback();
            //            Connection.Close();
            //        }
            //        return true;
            //    }
            //    catch
            //    {
            //        return false;
            //    }
            //}
            internal string GenerateSalt()
            {
                byte[] buf = new byte[16];
                (new RNGCryptoServiceProvider()).GetBytes(buf);
                return Convert.ToBase64String(buf);
            }
            internal string EncodePassword(string pass, int passwordFormat, string salt)
            {
                if (passwordFormat == 0) // MembershipPasswordFormat.Clear
                    return pass;

                byte[] bIn = Encoding.Unicode.GetBytes(pass);
                byte[] bSalt = Convert.FromBase64String(salt);
                byte[] bAll = new byte[bSalt.Length + bIn.Length];
                byte[] bRet = null;

                Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
                Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
                if (passwordFormat == 1)
                { // MembershipPasswordFormat.Hashed
                    HashAlgorithm s = HashAlgorithm.Create(Membership.HashAlgorithmType);
                    bRet = s.ComputeHash(bAll);
                }
                else
                {
                    bRet = EncryptPassword(bAll);
                }

                return Convert.ToBase64String(bRet);
            }
        }
    }

    代码分析: 

    1.重写Initialize()方法,主要是为了获得某些配置信息.比如连接字符串,密码加密方式等.

    public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
            {
                _CommandTimeout = SecUtility.GetIntValue(config, "commandTimeout", 30, true, 0);
                _SchemaVersionCheck = 0;
                string temp = config["connectionStringName"];
                if (temp == null || temp.Length < 1)
                    throw new ProviderException(SR.GetString(SR.Connection_name_not_specified));
                _sqlConnectionString = null;
                ConnectionStringSettings connObj = ConfigurationManager.ConnectionStrings[temp];
                if (connObj != null)
                    _sqlConnectionString = connObj.ConnectionString;
                string strTemp = config["passwordFormat"];
                if (strTemp == null)
                    strTemp = "Hashed";

                switch (strTemp)
                {
                    case "Clear":
                        _PasswordFormat = MembershipPasswordFormat.Clear;
                        break;
                    case "Encrypted":
                        _PasswordFormat = MembershipPasswordFormat.Encrypted;
                        break;
                    case "Hashed":
                        _PasswordFormat = MembershipPasswordFormat.Hashed;
                        break;
                    default:
                        throw new ProviderException(SR.GetString(SR.Provider_bad_password_format));
                }

                base.Initialize(name, config);
            }

    2.重写CreateUser()方法.

     这个方法的前面半部分主要是验证输入信息的有效性,后半部分是用于事务处理.主要介绍一下事务处理这一块的吧.

                int iStatus = -1;
                try
                {
                    cmd.ExecuteNonQuery();
                    iStatus = ((p.Value != null) ? ((int)p.Value) : -1);
                    if (iStatus < 0 || iStatus > (int)MembershipCreateStatus.ProviderError)
                        iStatus = (int)MembershipCreateStatus.ProviderError;
                    status = (MembershipCreateStatus)iStatus;
                    if (iStatus != 0) // !success
                    {
                        trans.Rollback();
                        Connection.Close();
                        return null;
                    }
                    cmdCreateProfile.ExecuteNonQuery();
                    trans.Commit();
                    Connection.Close();
                }
                catch
                {
                    status = (MembershipCreateStatus)iStatus;
                    trans.Rollback();
                    Connection.Close();
                }
                providerUserKey = new Guid(cmd.Parameters["@UserId"].Value.ToString());
                dt = dt.ToLocalTime();
                return new MembershipUser(this.Name,
                                           username,
                                           providerUserKey,
                                           email,
                                           passwordQuestion,
                                           null,
                                           isApproved,
                                           false,
                                           dt,
                                           dt,
                                           dt,
                                           dt,
                                           new DateTime(1754, 1, 1));

     cmd这个变量用于执行一个存储过程"aspnet_Membership_CreateUser",这个存储过程是M$提供的,它内部已经有了事务处理,也就是说执行这个存储过程时是不会引发异常的.所以我们只能根据这个存储过程的返回值(@ReturnValue)来判断创建新用户是否成功.

                    if (iStatus != 0) // !success
                    {
                        trans.Rollback();
                        Connection.Close();
                        return null;
                    }

    如何使用:

            写好了这个Class Library以后,编译成功后,怎么使用呢.在你的主项目中添加已存在的Project.把这个Class Library添加进去.然后在主项目中引用(Add Reference)这个Project.  然后在web.config中进行配置.如下:

      <membership defaultProvider="mySqlMembershipProvider">
       <providers>
            <remove name="AspNetSqlProvider"/>
        <clear/>
        <add  name="mySqlMembershipProvider" type="myProvider.mySqlMembershipProvider,myProvider.MembershipProvider"  applicationName="appName" connectionStringName="LocalSqlServer" maxInvalidPasswordAttempts="5" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="6" passwordFormat="Hashed" requiresQuestionAndAnswer="true" requiresUniqueEmail="true" enablePasswordRetrieval="false"/>
       </providers>
      </membership>

    需要说明的是type中的内容.type内容分两段,逗号前面是你的provider类的命名空间+类名,逗号后面是你的Class Library的工程名.也就是Assmbly的名字.

    因为爱上你,我才懂得珍惜,每一天日记,都写满了甜蜜
    因为想念你,我每天都可以,对着镜子说我多爱你,有多想见到你。
  • 相关阅读:
    负载均衡机制
    测试先行
    MVC模式在Java Web应用程序中的实例
    MVC模式学习
    Java反射机制
    软件开发火狐自动填写用户名和密码
    23种设计模式概述
    站立会议总结02
    站立会议总结01
    买书最低价问题
  • 原文地址:https://www.cnblogs.com/jackzhang/p/570246.html
Copyright © 2011-2022 走看看