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的名字.

    因为爱上你,我才懂得珍惜,每一天日记,都写满了甜蜜
    因为想念你,我每天都可以,对着镜子说我多爱你,有多想见到你。
  • 相关阅读:
    poj 2411 Mondriaan's Dream 骨牌铺放 状压dp
    zoj 3471 Most Powerful (有向图)最大生成树 状压dp
    poj 2280 Islands and Bridges 哈密尔顿路 状压dp
    hdu 3001 Travelling 经过所有点(最多两次)的最短路径 三进制状压dp
    poj 3311 Hie with the Pie 经过所有点(可重)的最短路径 floyd + 状压dp
    poj 1185 炮兵阵地 状压dp
    poj 3254 Corn Fields 状压dp入门
    loj 6278 6279 数列分块入门 2 3
    VIM记事——大小写转换
    DKIM支持样本上传做检测的网站
  • 原文地址:https://www.cnblogs.com/jackzhang/p/570246.html
Copyright © 2011-2022 走看看