您的位置:首页 > 数据库 > MySQL

实现成员资格提供程序(MySqlMembershipProvider)

2010-12-13 01:06 323 查看
一共有1700多行,所以浏览器会提示慢,如果有提示,请点击取消!

ASP.NET 成员资格专为使您可以轻松地将多个不同的成员资格提供程序用于您的 ASP.NET 应用程序而设计。 可以使用 .NET Framework 提供的成员资格提供程序,也可以实现自己的提供程序。

创建自定义成员资格提供程序主要有两个原因。

需要将成员资格信息存储在一个 .NET Framework 内附的成员资格提供程序不支持的数据源中,如 MySql 数据库、Oracle 数据库或其他数据源。

需要使用不同于 .NET Framework 附带的提供程序所使用的数据库架构来管理成员资格信息。 一个常见的示例是公司或网站的 SQL Server 数据库中已有的成员资格数据。

注:以上信息摘自MSDN说明

下面这个MySqlMembershipProvider是我参加MSDN的OdbcMembershipProvider写的,方便以后查询,也希望能为正在使用MySql,同时又和我一样有需要的提供帮助。



MySqlMembershipProvider.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Security;
using System.Configuration.Provider;
using System.Configuration;
using System.Web.Configuration;
using MySql.Data.MySqlClient;
using System.Diagnostics;

/*
请创建数据表
 
CREATE TABLE `users` (
  `PKID` varchar(36) NOT NULL DEFAULT '',
  `Username` varchar(255) NOT NULL DEFAULT '',
  `ApplicationName` varchar(100) NOT NULL DEFAULT '',
  `Email` varchar(100) NOT NULL DEFAULT '',
  `Comment` varchar(255) DEFAULT NULL,
  `Password` varchar(128) NOT NULL DEFAULT '',
  `PasswordQuestion` varchar(255) NOT NULL DEFAULT '',
  `PasswordAnswer` varchar(255) DEFAULT NULL,
  `IsApproved` tinyint(1) DEFAULT NULL,
  `LastActivityDate` datetime DEFAULT NULL,
  `LastLoginDate` datetime DEFAULT NULL,
  `LastPasswordChangedDate` datetime DEFAULT NULL,
  `CreationDate` datetime DEFAULT NULL,
  `IsOnLine` tinyint(1) DEFAULT NULL,
  `IsLockedOut` tinyint(1) DEFAULT NULL,
  `LastLockedOutDate` datetime DEFAULT NULL,
  `FailedPasswordAttemptCount` int(11) DEFAULT NULL,
  `FailedPasswordAttemptWindowStart` datetime DEFAULT NULL,
  `FailedPasswordAnswerAttemptCount` int(11) DEFAULT NULL,
  `FailedPasswordAnswerAttemptWindowStart` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
*/
namespace Membership
{
    public class MySqlMembershipProvider : System.Web.Security.MembershipProvider
    {
        #region System.Web.Security.MembershipProvider properties

        private int m_newPasswordLength = 8;
        private string m_tableName = "Users";
        private string m_eventLog = "Application";
        private string m_eventSource = "MySqlMembershipProvider";
        private string m_exceptionMessage = "An exception occurred. Please check the Event Log.";

        private string m_connectionString;
        private MachineKeySection m_machineKey;

        private string m_applicationName;
        private bool m_enablePasswordRest;
        private bool m_enablePasswordRetrieval;
        private bool m_requiresQuestionAndAnswer;
        private bool m_requiresUniqueEmail;
        private int m_maxInvalidPasswordAttempts;
        private int m_passwordAttemptWindow;
        private int m_minRequiredPasswordLength;
        private int m_minRequiredNonAlphanumericCharacters;
        private string m_passwordStrengthRegularExpression;
        private bool m_writeExceptionsToEventLog;

        private MembershipPasswordFormat m_passwordFormat;
        
        /// <summary>
        /// 使用配置文件 (Web.config) 中指定的成员资格信息的应用程序的名称。 ApplicationName 与相关用户信息一同存储在数据源中,并在查询这些信息时使用。 
        /// 如果未明确指定,则此属性为读/写,并且默认值为 ApplicationPath。 
        /// </summary>
        public override string ApplicationName
        {
            get { return m_applicationName; }
            set { m_applicationName = value; }
        }

        /// <summary>
        /// 指示用户是否可以使用 ResetPassword 方法将其当前密码覆盖为随机生成的新密码。 此属性为只读。 
        /// </summary>
        public override bool EnablePasswordReset
        {
            get { return m_enablePasswordRest; }
        }

        /// <summary>
        /// 在配置文件 (Web.config) 中指定的 Boolean 值。 
        /// EnablePasswordRetrieval 属性指示用户是否可以使用 GetPassword 方法取回其密码。 
        /// 此属性为只读。
        /// </summary>
        public override bool EnablePasswordRetrieval
        {
            get { return m_enablePasswordRetrieval; }
        }

        /// <summary>
        /// 在配置文件 (Web.config) 中指定的 Boolean 值。 
        /// RequiresQuestionAndAnswer 属性指示用户是否必须提供一个密码提示问题答案,才能使用 GetPassword 方法取回其密码或使用 ResetPassword 方法重置其密码。 
        /// 此属性为只读。 
        /// </summary>
        public override bool RequiresQuestionAndAnswer
        {
            get { return m_requiresQuestionAndAnswer; }
        }

        /// <summary>
        /// 在配置文件 (Web.config) 中指定的 Boolean 值。 
        /// RequiresUniqueEmail 属性指示用户在创建新用户时是否必须提供唯一的电子邮件地址。 如果当前 ApplicationName 应用程序的数据源中已经存在某个用户,则 CreateUser 方法返回 null 和一个 DuplicateEmail 状态值。 
        /// 此属性为只读。 
        /// </summary>
        public override bool RequiresUniqueEmail
        {
            get { return m_requiresUniqueEmail; }
        }

        /// <summary>
        /// 在配置文件 (Web.config) 中指定的 Integer 值。
        /// MaxInvalidPasswordAttempts 与 PasswordAttemptWindow 一起工作,防止不明来源反复尝试猜测成员资格用户的密码或密码提示问题答案。 
        /// 在 PasswordAttemptWindow 指定的分钟数内,如果为某一成员资格用户所输入的无效密码或密码提示问题答案的次数超过 MaxInvalidPasswordAttempts,
        /// 则通过将 IsLockedOut 属性设置为 true 锁定该成员资格用户,直到使用 UnlockUser 方法解除对该用户的锁定。 
        /// 如果在达到 MaxInvalidPasswordAttempts 之前输入了有效密码或密码提示问题答案,则跟踪无效尝试次数的计数器将被重置为零。
        /// 如果 RequiresQuestionAndAnswer 属性设置为 false,则不跟踪输入无效密码提示问题答案的尝试次数。 
        /// 输入无效密码和密码提示问题答案的尝试次数通过下面的方法进行跟踪:ValidateUser、ChangePassword、ChangePasswordQuestionAndAnswer、GetPassword 和 ResetPassword 方法。 
        /// 此属性为只读。
        /// </summary>
        public override int MaxInvalidPasswordAttempts
        {
            get { return m_maxInvalidPasswordAttempts; }
        }

        /// <summary>
        /// 在配置文件 (Web.config) 中指定的 Integer 值。 
        /// 有关说明,请参见 MaxInvalidPasswordAttempts 属性的说明。 
        /// 此属性为只读。 
        /// </summary>
        public override int PasswordAttemptWindow
        {
            get { return m_passwordAttemptWindow; }
        }

        /// <summary>
        /// 在配置文件 (Web.config) 中指定的 MembershipPasswordFormat 值。 
        /// PasswordFormat 属性指示存储密码的格式。 密码可以采用 Clear、Encrypted 和 Hashed 密码格式存储。 
        /// Clear 密码以纯文本格式存储,这可以提高存储和检索密码的性能,但安全性较差,当数据源安全性受到威胁时此类密码很容易被读取。 
        /// Encrypted 密码在存储时进行了加密,可以在比较或检索密码时进行解密。 此类密码在存储和检索时需要进行额外的处理,但比较安全,在数据源的安全性受到威胁时不容易被获取。 
        /// Hashed 密码在存储到数据库时使用单向哈希算法和随机生成的 salt 值对其进行哈希处理。在验证某一密码时,将用数据库中的 salt 值对该密码进行散列以进行验证。 哈希密码不能检索。 
        /// 可以使用 MembershipProvider 类的 EncryptPassword 和 DecryptPassword 虚方法加密和解密密码值,
        /// 也可以提供自己的加密代码。 如果使用 MembershipProvider 类的 EncryptPassword 和 DecryptPassword 虚方法,
        /// 则使用配置文件 machineKey 元素中提供的密钥信息加密 Encrypted 密码。 
        /// 此属性为只读。
        /// </summary>
        public override MembershipPasswordFormat PasswordFormat
        {
            get { return m_passwordFormat; }
        }

        /// <summary>
        /// 获取密码所要求的最小长度。
        /// </summary>
        public override int MinRequiredPasswordLength
        {
            get { return m_minRequiredPasswordLength; }
        }

        /// <summary>
        /// 获取有效密码中必须包含的最少特殊字符数。 
        /// </summary>
        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return m_minRequiredNonAlphanumericCharacters; }
        }

        /// <summary>
        /// 获取用于计算密码的正则表达式。
        /// </summary>
        public override string PasswordStrengthRegularExpression
        {
            get { return m_passwordStrengthRegularExpression; }
        }

        /// <summary>
        /// 如果值为false,异常抛给调用者,如果为true,将异常信息写入日志
        /// </summary>
        public bool WriteExceptionsToEventLog
        {
            get { return m_writeExceptionsToEventLog; }
            set { m_writeExceptionsToEventLog = value; }
        }

        #endregion

        /// <summary>
        /// 初始化提供程序。 (从 ProviderBase 继承。)
        /// </summary>
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            /*
             * 初始化web.config.的Membership节的值.
             */

            if (config == null)
                throw new ArgumentNullException("config");

            if (name == null || name.Length == 0)
                name = "MySqlMembershipProvider";

            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Sample MySql Membership provider");
            }

            //初始化抽象基类
            base.Initialize(name, config);

            m_applicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            m_maxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
            m_passwordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
            m_minRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
            m_minRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "6"));
            m_passwordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
            m_enablePasswordRest = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
            m_enablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
            m_requiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
            m_requiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
            m_writeExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "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.");
            }

            /*
             * 初始化MySql数据库连接
             */

            ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];
            if (connectionStringSettings == null || connectionStringSettings.ConnectionString.Trim() =="")
            {
                throw new ProviderException("Connection string cannot be blank.");
            }

            m_connectionString = connectionStringSettings.ConnectionString;

            /*
             * 从配置得到加密和解密钥匙信息
             */

            Configuration cfg = WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            m_machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");

            if (m_machineKey.ValidationKey.Contains("AutoGenerate"))
                if (PasswordFormat != MembershipPasswordFormat.Clear)
                    throw new ProviderException("Hashed or Encrypted passwords are not supported with auto-generated keys .");
        }

        /// <summary>
        /// 采用用户名、当前密码和新密码作为输入,并在所提供的用户名和当前密码有效的情况下更新数据源中的密码。
        /// 如果成功更新密码,则 ChangePassword 方法返回 true;否则,返回 false。 
        /// 在已指定 MembershipValidatePasswordEventHandler 的条件下,ChangePassword 方法会引发 ValidatingPassword 事件,并根据该事件的结果继续或取消更改密码操作。 
        /// 可使用 OnValidatingPassword 虚方法执行指定的 MembershipValidatePasswordEventHandler。
        /// </summary>
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            if (!ValidateUser(username, oldPassword))
                return false;

            ValidatePasswordEventArgs args =
              new ValidatePasswordEventArgs(username, newPassword, true);

            OnValidatingPassword(args);

            if (args.Cancel)
                if (args.FailureInformation != null)
                    throw args.FailureInformation;
                else
                    throw new MembershipPasswordException("Change password canceled due to new password validation failure.");

            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("UPDATE `" + m_tableName + "`" +
                    " SET Password = ?Password, LastPasswordChangedDate = ?LastPasswordChangedDate " +
                    " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Password", MySqlDbType.VarChar, 255).Value = EncodePassword(newPassword);
            cmd.Parameters.Add("?LastPasswordChangedDate", MySqlDbType.DateTime).Value = DateTime.Now;
            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            int rowsAffected = 0;

            try
            {
                conn.Open();

                rowsAffected = cmd.ExecuteNonQuery();
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ChangePassword");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                conn.Close();
            }

            if (rowsAffected > 0)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 采用用户名、密码、密码提示问题和密码提示问题答案作为输入,
        /// 并在所提供的用户名和密码有效的情况下更新数据源中的密码提示问题和答案。 
        /// 如果成功更新密码提示问题和答案,则 ChangePasswordQuestionAndAnswer 方法返回 true;否则,返回 false。 
        /// 如果所提供的用户名和密码无效,则返回 false。 
        /// </summary>
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            if (!ValidateUser(username, password))
                return false;

            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("UPDATE `" + m_tableName + "`" +
                    " SET PasswordQuestion = ?Question, PasswordAnswer = ?Answer" +
                    " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Question", MySqlDbType.VarChar, 255).Value = newPasswordQuestion;
            cmd.Parameters.Add("?Answer", MySqlDbType.VarChar, 255).Value = EncodePassword(newPasswordAnswer);
            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            int rowsAffected = 0;

            try
            {
                conn.Open();

                rowsAffected = cmd.ExecuteNonQuery();
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ChangePasswordQuestionAndAnswer ");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                conn.Close();
            }

            if (rowsAffected > 0)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 接受新用户名、密码和电子邮件地址作为输入,并将应用程序的新用户插入到数据源中。 
        /// CreateUser 方法返回一个 MembershipUser 对象,其中填充了新创建用户的信息。 
        /// 此外,CreateUser 方法还定义一个 out 参数(在 Visual Basic 中,可以使用 ByRef),
        /// 该参数返回一个 MembershipCreateStatus 值,指示是否已成功创建用户,如创建失败则指明原因。 
        /// 在已指定 MembershipValidatePasswordEventHandler 的条件下,CreateUser 方法会引发 ValidatingPassword 事件,
        /// 并根据该事件的结果继续或取消用户创建操作。可使用 OnValidatingPassword 虚方法执行指定的 MembershipValidatePasswordEventHandler。
        /// </summary>
        public override System.Web.Security.MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out System.Web.Security.MembershipCreateStatus status)
        {
            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, password, true);

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

            if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            MembershipUser u = GetUser(username, false);
            //如果没有用户就创建
            if (u == null)
            {
                DateTime createDate = DateTime.Now;
                if (providerUserKey == null)
                {
                    providerUserKey = Guid.NewGuid();
                }
                else
                {
                    if (!(providerUserKey is Guid))
                    {
                        status = MembershipCreateStatus.InvalidProviderUserKey;
                        return null;
                    }
                }

                MySqlConnection conn = new MySqlConnection(m_connectionString);
                MySqlCommand cmd = new MySqlCommand("INSERT INTO `" + m_tableName + "`" +
                      " (PKID, Username, Password, Email, PasswordQuestion, " +
                      " PasswordAnswer, IsApproved," +
                      " Comment, CreationDate, LastPasswordChangedDate, LastActivityDate," +
                      " ApplicationName, IsLockedOut, LastLockedOutDate," +
                      " FailedPasswordAttemptCount, FailedPasswordAttemptWindowStart, " +
                      " FailedPasswordAnswerAttemptCount, FailedPasswordAnswerAttemptWindowStart)" +
                      " Values(?PKID, ?Username, ?Password, ?Email, ?PasswordQuestion, " +
                      " ?PasswordAnswer, ?IsApproved, ?Comment, ?CreationDate, ?LastPasswordChangedDate, " +
                      " ?LastActivityDate, ?ApplicationName, ?IsLockedOut, ?LastLockedOutDate, " +
                      " ?FailedPasswordAttemptCount, ?FailedPasswordAttemptWindowStart, " +
                      " ?FailedPasswordAnswerAttemptCount, ?FailedPasswordAnswerAttemptWindowStart)", conn);

                cmd.Parameters.Add("?PKID", MySqlDbType.VarChar).Value = providerUserKey.ToString();
                cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
                cmd.Parameters.Add("?Password", MySqlDbType.VarChar, 255).Value = EncodePassword(password);
                cmd.Parameters.Add("?Email", MySqlDbType.VarChar, 128).Value = email;
                cmd.Parameters.Add("?PasswordQuestion", MySqlDbType.VarChar, 255).Value = passwordQuestion;
                cmd.Parameters.Add("?PasswordAnswer", MySqlDbType.VarChar, 255).Value = passwordAnswer == null ? null : EncodePassword(passwordAnswer);
                cmd.Parameters.Add("?IsApproved", MySqlDbType.Bit).Value = isApproved;
                cmd.Parameters.Add("?Comment", MySqlDbType.VarChar, 255).Value = "";
                cmd.Parameters.Add("?CreationDate", MySqlDbType.DateTime).Value = createDate;
                cmd.Parameters.Add("?LastPasswordChangedDate", MySqlDbType.DateTime).Value = createDate;
                cmd.Parameters.Add("?LastActivityDate", MySqlDbType.DateTime).Value = createDate;
                cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;
                cmd.Parameters.Add("?IsLockedOut", MySqlDbType.Bit).Value = 0;	//false
                cmd.Parameters.Add("?LastLockedOutDate", MySqlDbType.DateTime).Value = createDate;
                cmd.Parameters.Add("?FailedPasswordAttemptCount", MySqlDbType.Int32).Value = 0;
                cmd.Parameters.Add("?FailedPasswordAttemptWindowStart", MySqlDbType.DateTime).Value = createDate;
                cmd.Parameters.Add("?FailedPasswordAnswerAttemptCount", MySqlDbType.Int32).Value = 0;
                cmd.Parameters.Add("?FailedPasswordAnswerAttemptWindowStart", MySqlDbType.DateTime).Value = createDate;

                try
                {
                    conn.Open();

                    int recAdded = cmd.ExecuteNonQuery();
                    if (recAdded > 0)
                    {
                        status = MembershipCreateStatus.Success;
                    }
                    else
                    {
                        status = MembershipCreateStatus.UserRejected;
                    }
                }
                catch (MySqlException e)
                {
                	if (WriteExceptionsToEventLog)
                	{
                        WriteToEventLog(e, "CreateUser");
                	}

                    status = MembershipCreateStatus.ProviderError;
                }
                finally
                {
                    conn.Close();
                }

                return GetUser(username, false);
            }
            else
            {
                status = MembershipCreateStatus.DuplicateUserName;
            }

            return null;
        }

        /// <summary>
        /// 采用用户名作为输入,并从数据源删除该用户的信息。 
        /// 如果成功删除用户,则 DeleteUser 方法返回 true;否则,返回 false。 
        /// 所包含的另一个 Boolean 参数用于指示是否也删除该用户的角色或配置文件信息等相关信息。
        /// </summary>
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("DELETE FROM `" + m_tableName + "`" +
                    " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            int rowsAffected = 0;

            try
            {
                conn.Open();

                rowsAffected = cmd.ExecuteNonQuery();

                if (deleteAllRelatedData)
                {
                    // Process commands to delete all data for the user in the database.
                }
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "DeleteUser");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                conn.Close();
            }

            if (rowsAffected > 0)
                return true;

            return false;
        }

        /// <summary>
        /// 返回一个成员资格用户列表,其中的用户名与为所配置的 ApplicationName 提供的 emailToMatch 匹配或部分匹配。 
        /// 例如,如果 emailToMatch 参数设置为“address@example.com”,则返回具有诸如“address1@example.com”、“address2@example.com”等电子邮件地址的用户。 
        /// 根据数据源提供通配符支持。 根据用户名按字母顺序返回用户。 
        /// FindUsersByEmail 返回的结果受 pageIndex 和 pageSize 参数的约束。 
        /// pageSize 参数标识在 MembershipUserCollection 集合中返回的 MembershipUser 对象的数目。 
        /// pageIndex 参数标识要返回的结果页,1 标识第 1 页。 totalRecords 参数是设置为与 emailToMatch 值匹配成员资格用户总数的 out 参数。 
        /// 例如,如果找到 13 个 emailToMatch 与部分或整个用户名匹配的用户,并且 pageIndex 值为 2,pageSize 值为 5,则 MembershipUserCollection 将包含返回的第 6 个到第 10 个用户。 
        /// totalRecords 将被设置为 13。 
        /// </summary>
        public override System.Web.Security.MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT Count(*) FROM `" + m_tableName + "` " +
                                              "WHERE Email LIKE ?EmailSearch AND ApplicationName = ?ApplicationName", conn);
            cmd.Parameters.Add("?EmailSearch", MySqlDbType.VarChar, 255).Value = emailToMatch;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName;

            MembershipUserCollection users = new MembershipUserCollection();

            MySqlDataReader reader = null;
            totalRecords = 0;

            try
            {
                conn.Open();
                totalRecords = (int)cmd.ExecuteScalar();

                if (totalRecords <= 0) { return users; }

                cmd.CommandText = "SELECT PKID, Username, Email, PasswordQuestion," +
                        " Comment, IsApproved, IsLockedOut, CreationDate, LastLoginDate," +
                        " LastActivityDate, LastPasswordChangedDate, LastLockedOutDate " +
                        " FROM `" + m_tableName + "` " +
                        " WHERE Email LIKE ?Username AND ApplicationName = ?ApplicationName " +
                        " ORDER BY Username Asc";

                using(reader = cmd.ExecuteReader())
                {
                    int counter = 0;
                    int startIndex = pageSize * pageIndex;
                    int endIndex = startIndex + pageSize - 1;

                    while(reader.Read())
                    {
                        if (counter >= startIndex)
                        {
                            MembershipUser u = GetUserFromReader(reader);
                            users.Add(u);
                        }

                        if (counter >= endIndex) { cmd.Cancel(); }

                        counter++;
                    }
                    reader.Close();
                }
            }
            catch (MySqlException e)
            {
            	if (WriteExceptionsToEventLog)
            	{
                    WriteToEventLog(e, "FindUsersByEmail  ");

                    throw new ProviderException(m_exceptionMessage);
            	}
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null)
                    reader.Close();

                conn.Close();
            }

            return users;
        }

        /// <summary>
        /// 返回一个成员资格用户列表,其中的用户名与为所配置的 ApplicationName 提供的 usernameToMatch 匹配或部分匹配。 
        /// 例如,如果 usernameToMatch 参数设置为“user”,则返回诸如“user1”、“user2”、“user3”的用户。 
        /// 根据数据源提供通配符支持。 根据用户名按字母顺序返回用户。FindUsersByName 返回的结果受 pageIndex 和 pageSize 参数的约束。 
        /// pageSize 参数标识在 MembershipUserCollection 中返回的 MembershipUser 对象的数目。 
        /// pageIndex 参数标识要返回的结果页,1 标识第 1 页。 totalRecords 参数是设置为与 usernameToMatch 值匹配的成员资格用户总数的 out 参数。 
        /// 例如,如果找到 13 个 usernameToMatch 与部分或整个用户名匹配的用户,并且 pageIndex 值为 2,pageSize 值为 5,
        /// 则 MembershipUserCollection 将包含返回的第 6 个到第 10 个用户。totalRecords 将被设置为 13。
        /// </summary>
        public override System.Web.Security.MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT Count(*) FROM `" + m_tableName + "` " +
                      "WHERE Username LIKE ?UsernameSearch AND ApplicationName = ?ApplicationName", conn);
            cmd.Parameters.Add("?UsernameSearch", MySqlDbType.VarChar, 255).Value = usernameToMatch;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            MembershipUserCollection users = new MembershipUserCollection();

            MySqlDataReader reader = null;

            try
            {
                conn.Open();
                totalRecords = Convert.ToInt32(cmd.ExecuteScalar());

                if (totalRecords <= 0) { return users; }

                cmd.CommandText = "SELECT PKID, Username, Email, PasswordQuestion," +
                  " Comment, IsApproved, IsLockedOut, CreationDate, LastLoginDate," +
                  " LastActivityDate, LastPasswordChangedDate, LastLockedOutDate " +
                  " FROM `" + m_tableName + "` " +
                  " WHERE Username LIKE ?UsernameSearch AND ApplicationName = ?ApplicationName " +
                  " ORDER BY Username Asc";

                using (reader = cmd.ExecuteReader())
                {
                    int counter = 0;
                    int startIndex = pageSize * pageIndex;
                    int endIndex = startIndex + pageSize - 1;

                    while (reader.Read())
                    {
                        if (counter >= startIndex)
                        {
                            MembershipUser u = GetUserFromReader(reader);
                            users.Add(u);
                        }

                        if (counter >= endIndex) { cmd.Cancel(); }

                        counter++;
                    }
                    reader.Close();
                }
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "FindUsersByName");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null) { reader.Close(); }

                conn.Close();
            }

            return users;
        }

        /// <summary>
        /// 返回一个用数据源中所有用户的 MembershipUser 对象填充的 MembershipUserCollection。 
        /// GetAllUsers 返回的结果受 pageIndex 和 pageSize 参数的约束。 
        /// pageSize 参数标识在 MembershipUserCollection 中返回的 MembershipUser 对象的最大数。 
        /// pageIndex 参数标识要返回的结果页,1 标识第 0 页。 totalRecords 参数是设置为成员资格用户总数的 out 参数。 
        /// 例如,如果应用程序在数据库中有 13 个用户,并且 pageIndex 值为 1,pageSize 为 5,则返回的 MembershipUserCollection 将包含返回的第 6 个到第 10 个用户。 
        /// totalRecords 将被设置为 13。 
        /// </summary>
        public override System.Web.Security.MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT Count(*) FROM `" + m_tableName + "` " +
                                              "WHERE ApplicationName = ?ApplicationName", conn);
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName;

            MembershipUserCollection users = new MembershipUserCollection();

            MySqlDataReader reader = null;
            totalRecords = 0;

            try
            {
                conn.Open();
                totalRecords = (int)cmd.ExecuteScalar();

                if (totalRecords <= 0) { return users; }

                cmd.CommandText = "SELECT PKID, Username, Email, PasswordQuestion," +
                        " Comment, IsApproved, IsLockedOut, CreationDate, LastLoginDate," +
                        " LastActivityDate, LastPasswordChangedDate, LastLockedOutDate " +
                        " FROM `" + m_tableName + "` " +
                        " WHERE ApplicationName = ?ApplicationName " +
                        " ORDER BY Username Asc";

                using(reader = cmd.ExecuteReader())
                {
                    int counter = 0;
                    int startIndex = pageSize * pageIndex;
                    int endIndex = startIndex + pageSize - 1;

                    while(reader.Read())
                    {
                        if (counter >= startIndex)
                        {
                            MembershipUser u = GetUserFromReader(reader);
                            users.Add(u);
                        }

                        if (counter >= endIndex) { cmd.Cancel(); }
                        
                        counter++;
                    }
                    reader.Close();
                }
            }
            catch (MySqlException e)
            {
            	if (WriteExceptionsToEventLog)
            	{
                    WriteToEventLog(e, "GetAllUsers  ");

                    throw new ProviderException(m_exceptionMessage);
            	}
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null)
                    reader.Close();

                conn.Close();
            }

            return users;
        }

        /// <summary>
        /// 如果 LastActivityDate 大于当前日期和时间减去 UserIsOnlineTimeWindow 属性值所得的值,则返回数据源中所有用户计数的整数值。 
        /// UserIsOnlineTimeWindow 属性是一个整数值,用于指定在确定用户是否联机时使用的分钟数。 
        /// </summary>
        public override int GetNumberOfUsersOnline()
        {
            TimeSpan onlineSpan = new TimeSpan(0, System.Web.Security.Membership.UserIsOnlineTimeWindow, 0);
            DateTime compareTime = DateTime.Now.Subtract(onlineSpan);

            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT Count(*) FROM `" + m_tableName + "`" +
                    " WHERE LastActivityDate > ?CompareDate AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?CompareDate", MySqlDbType.DateTime).Value = compareTime;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            int numOnline = 0;

            try
            {
                conn.Open();

                numOnline = Convert.ToInt32(cmd.ExecuteScalar());
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetNumberOfUsersOnline");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                conn.Close();
            }

            return numOnline;
        }

        /// <summary>
        /// 采用用户名和密码提示问题答案作为输入,并从数据源中检索该用户的密码,然后将该密码作为一个 string 返回。
        /// GetPassword 可确保在执行任何操作之前都将 EnablePasswordRetrieval 属性设置为 true。 
        /// 如果 EnablePasswordRetrieval 属性为 false,则将引发 ProviderException。 
        /// GetPassword 方法还会检查 RequiresQuestionAndAnswer 属性的值。 
        /// 如果 RequiresQuestionAndAnswer 属性为 true,则 GetPassword 方法会对照数据源中存储的密码提示问题答案检查所输入的提示问题答案参数的值。 
        /// 如果它们不匹配,就引发 MembershipPasswordException。
        /// </summary>
        public override string GetPassword(string username, string answer)
        {
            if (!EnablePasswordRetrieval)
            {
                throw new ProviderException("Password Retrieval Not Enabled.");
            }

            if (PasswordFormat == MembershipPasswordFormat.Hashed)
            {
                throw new ProviderException("Cannot retrieve Hashed passwords.");
            }

            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT Password, PasswordAnswer, IsLockedOut FROM `" + m_tableName + "`" +
                  " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            string password = "";
            string passwordAnswer = "";
            MySqlDataReader reader = null;

            try
            {
                conn.Open();

                using (reader = cmd.ExecuteReader(System.Data.CommandBehavior.SingleRow))
                {
                    if (reader.HasRows)
                    {
                        reader.Read();

                        if (reader.GetBoolean(2))
                            throw new MembershipPasswordException("The supplied user is locked out.");

                        password = reader.GetString(0);
                        passwordAnswer = reader.GetString(1);
                    }
                    else
                    {
                        throw new MembershipPasswordException("The supplied user name is not found.");
                    }
                    reader.Close();
                }
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetPassword");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null) { reader.Close(); }
                conn.Close();
            }

            if (RequiresQuestionAndAnswer && !CheckPassword(answer, passwordAnswer))
            {
                UpdateFailureCount(username, "passwordAnswer");

                throw new MembershipPasswordException("Incorrect password answer.");
            }

            if (PasswordFormat == MembershipPasswordFormat.Encrypted)
            {
                password = UnEncodePassword(password);
            }

            return password;
        }
        
        /// <summary>
        /// 采用一个用户名和一个 Boolean 值(指示是否更新该用户的 LastActivityDate 值)作为输入,显示该用户当前是否联机。 
        /// GetUser 方法返回一个用数据源中指定用户的当前值填充的 MembershipUser 对象。 
        /// 如果未在数据源中找到该用户名,则 GetUser 方法返回 null(在 Visual Basic 中为 Nothing)。
        /// </summary>
        public override System.Web.Security.MembershipUser GetUser(string username, bool userIsOnline)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT PKID, Username, Email, PasswordQuestion," +
                " Comment, IsApproved, IsLockedOut, CreationDate, LastLoginDate," +
                " LastActivityDate, LastPasswordChangedDate, LastLockedOutDate" +
                " FROM `" + m_tableName + "` WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            MembershipUser u = null;
            MySqlDataReader reader = null;

            try
            {
                conn.Open();

                using(reader = cmd.ExecuteReader())
                {
                    if (reader.HasRows)
                    {
                        reader.Read();
                        u = GetUserFromReader(reader);
                        reader.Close();

                        if (userIsOnline)
                        {
                            MySqlCommand updateCmd = new MySqlCommand("UPDATE `" + m_tableName + "` " +
                                      "SET LastActivityDate = ?LastActivityDate " +
                                      "WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

                            updateCmd.Parameters.Add("?LastActivityDate", MySqlDbType.VarChar).Value = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
                            updateCmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
                            updateCmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

                            updateCmd.ExecuteNonQuery();
                        }
                    }
                    reader.Close();
                }
            }
            catch (MySqlException e)
            {
            	if (WriteExceptionsToEventLog)
            	{
                    WriteToEventLog(e, "GetUser(string, Boolean");
                    throw new ProviderException(m_exceptionMessage);
            	}
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null) 
                    reader.Close(); 

                conn.Close();
            }
            return u;
        }

        /// <summary>
        /// 采用唯一的用户标识符和一个Boolean 值(指示是否更新该用户的 LastActivityDate 值)作为输入,显示该用户当前是否联机。 
        /// GetUser 方法返回一个用数据源中指定用户的当前值填充的 MembershipUser 对象。 
        /// 如果未在数据源中找到该用户名,则 GetUser 方法返回 null(在 Visual Basic 中为 Nothing)。
        /// </summary>
        public override System.Web.Security.MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT PKID, Username, Email, PasswordQuestion," +
                  " Comment, IsApproved, IsLockedOut, CreationDate, LastLoginDate," +
                  " LastActivityDate, LastPasswordChangedDate, LastLockedOutDate" +
                  " FROM `" + m_tableName + "` WHERE PKID = ?PKID", conn);

            cmd.Parameters.Add("?PKID", MySqlDbType.VarChar).Value = providerUserKey;

            MembershipUser u = null;
            MySqlDataReader reader = null;

            try
            {
                conn.Open();

                using (reader = cmd.ExecuteReader())
                {
                    if (reader.HasRows)
                    {
                        u = GetUserFromReader(reader);
                        reader.Close();

                        if (userIsOnline)
                        {
                            MySqlCommand updateCmd = new MySqlCommand("UPDATE `" + m_tableName + "` " +
                                      "SET LastActivityDate = ?LastActivityDate " +
                                      "WHERE PKID = ?PKID", conn);

                            updateCmd.Parameters.Add("?LastActivityDate", MySqlDbType.VarChar).Value = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
                            updateCmd.Parameters.Add("?PKID", MySqlDbType.VarChar).Value = providerUserKey;

                            updateCmd.ExecuteNonQuery();
                        }
                    }
                    reader.Close();
                }
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUser(Object, Boolean)");
                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null)
                    reader.Close();

                conn.Close();
            }
            return u;
        }

        /// <summary>
        /// GetUserFromReader
        /// A helper function that takes the current row from the MySqlDataReader
        /// and hydrates a MembershiUser from the values. Called by the 
        /// MembershipUser.GetUser implementation.
        /// </summary>
        private MembershipUser GetUserFromReader(MySqlDataReader reader)
        {
            object providerUserKey = new Guid(reader.GetValue(0).ToString());
            string username = reader.IsDBNull(1) ? "" : reader.GetString(1);
            string email = reader.IsDBNull(2) ? "" : reader.GetString(2);
            string passwordQuestion = reader.IsDBNull(3) ? "" : reader.GetString(3);
            string comment = reader.IsDBNull(4) ? "" : reader.GetString(4);
            bool isApproved = reader.IsDBNull(5) ? false : reader.GetBoolean(5);
            bool isLockedOut = reader.IsDBNull(6) ? false : reader.GetBoolean(6);
            DateTime creationDate = reader.IsDBNull(7) ? DateTime.Now : reader.GetDateTime(7);
            DateTime lastLoginDate = reader.IsDBNull(8) ? DateTime.Now : reader.GetDateTime(8);
            DateTime lastActivityDate = reader.IsDBNull(9) ? DateTime.Now : reader.GetDateTime(9);
            DateTime lastPasswordChangedDate = reader.IsDBNull(10) ? DateTime.Now : reader.GetDateTime(10);
            DateTime lastLockedOutDate = reader.IsDBNull(11) ? DateTime.Now : reader.GetDateTime(11);

            MembershipUser u = new MembershipUser(this.Name,
                                                  username,
                                                  providerUserKey,
                                                  email,
                                                  passwordQuestion,
                                                  comment,
                                                  isApproved,
                                                  isLockedOut,
                                                  creationDate,
                                                  lastLoginDate,
                                                  lastActivityDate,
                                                  lastPasswordChangedDate,
                                                  lastLockedOutDate);

            return u;
        }

        /// <summary>
        /// 采用电子邮件地址作为输入,并返回数据源中电子邮件地址与所提供的 email 参数值相匹配的第一个用户名。 
        /// 如果未找到电子邮件地址匹配的用户名,则返回一个空字符串。 
        /// 如果找到匹配特定电子邮地址的多个用户名,则只返回第一个找到的用户名。 
        /// </summary>
        public override string GetUserNameByEmail(string email)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT Username" +
                  " FROM `" + m_tableName + "` WHERE Email = ?Email AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Email", MySqlDbType.VarChar, 128).Value = email;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            string username = "";

            try
            {
                conn.Open();

                username = (string)cmd.ExecuteScalar();
            }
            catch (MySqlException e)
            {
            	if (WriteExceptionsToEventLog)
            	{
                    WriteToEventLog(e, "GetUserNameByEmail");
                    throw new ProviderException(m_exceptionMessage);
            	}
                else
                {
                    throw e;
                }
            }
            finally
            {
                conn.Close();
            }

            if (username == null)
            {
                username = "";
            }

            return username;
        }

        /// <summary>
        /// 采用用户名和密码提示问题答案作为输入,为指定用户生成一个随机新密码。 
        /// ResetPassword 方法用新密码值更新数据源中的用户信息,并将新密码作为一个 string 返回。 
        /// 用于生成随机密码的便捷机制是 Membership 类的 GeneratePassword 方法。 
        /// ResetPassword 方法可确保在执行任何操作之前都将 EnablePasswordReset 属性设置为 true。
        /// 如果 EnablePasswordReset 属性为 false,则将引发 NotSupportedException。 
        /// ResetPassword 方法还会检查 RequiresQuestionAndAnswer 属性的值。 
        /// 如果 RequiresQuestionAndAnswer 属性为 true,则 ResetPassword 方法会对照数据源中存储的密码提示问题答案检查所输入的提示问题答案参数的值。
        /// 如果它们不匹配,就引发 MembershipPasswordException。 
        /// 在已指定 MembershipValidatePasswordEventHandler 的条件下,ResetPassword 方法会引发 ValidatingPassword 事件以验证新生成的密码,
        /// 并根据该事件的结果继续或取消密码重置操作。 可使用 OnValidatingPassword 虚方法执行指定的 MembershipValidatePasswordEventHandler。
        /// </summary>
        public override string ResetPassword(string username, string answer)
        {
            if (!EnablePasswordReset)
            {
                throw new NotSupportedException("Password reset is not enabled. ");
            }

            if (answer == null && RequiresQuestionAndAnswer)
            {
                UpdateFailureCount(username, "passwordAnswer");

                throw new ProviderException("Password answer required for password reset. ");
            }

            string newPassword = System.Web.Security.Membership.GeneratePassword(m_newPasswordLength, MinRequiredNonAlphanumericCharacters);

            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);

            OnValidatingPassword(args);

            if (args.Cancel)
                if (args.FailureInformation != null)
                    throw args.FailureInformation;
                else
                    throw new MembershipPasswordException("Reset password canceled due to password validation failure. ");

            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT PasswordAnswer, IsLockedOut FROM `" + m_tableName + "`" +
                 " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            int rowsAffected = 0;
            string passwordAnswer = "";
            MySqlDataReader reader = null;

            try
            {
                conn.Open();

                using(reader = cmd.ExecuteReader(System.Data.CommandBehavior.SingleRow))
                {
                    if (reader.HasRows)
                    {
                        reader.Read();

                        if (reader.GetBoolean(1))
                            throw new MembershipPasswordException("The supplied user is locked out. ");

                        passwordAnswer = reader.GetString(0);
                    }
                    else
                    {
                        throw new MembershipPasswordException("The supplied user name is not fount. ");
                    }
                    reader.Close();
                }

                if (RequiresQuestionAndAnswer && !CheckPassword(answer, passwordAnswer))
                {
                    UpdateFailureCount(username, "passwordAnswer");

                    throw new MembershipPasswordException("Incorrect password answer. ");
                }

                MySqlCommand updateCmd = new MySqlCommand("UPDATE `" + m_tableName + "`" +
                   " SET Password = ?Password, LastPasswordChangedDate = ?LastPasswordChangedDate" +
                   " WHERE Username = ?Username AND ApplicationName = ?ApplicationName AND IsLockedOut = 0", conn);

                updateCmd.Parameters.Add("?Password", MySqlDbType.VarChar, 255).Value = EncodePassword(newPassword);    //新随机密码
                updateCmd.Parameters.Add("?LastPasswordChangedDate", MySqlDbType.DateTime).Value = DateTime.Now;        //密码最后改变时间
                updateCmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
                updateCmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

                rowsAffected = updateCmd.ExecuteNonQuery();
            }
            catch (MySqlException e)
            {
            	if (WriteExceptionsToEventLog)
            	{
                    WriteToEventLog(e, "ResetPassword");

                    throw new ProviderException(m_exceptionMessage);
            	}
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null)
                    reader.Close();

                conn.Close();
            }

            if (rowsAffected > 0)
            {
                return newPassword;
            }
            else
            {
                throw new MembershipPasswordException("User not fount, or user is locked out.  Password not Reset. ");
            }
        }

        /// <summary>
        /// UpdateFailureCount
        /// A helper method that performs the checks and updates associated with
        /// password failure tracking.
        /// </summary>
        private void UpdateFailureCount(string username, string failureType)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT FailedPasswordAttemptCount, " +
                                              "  FailedPasswordAttemptWindowStart, " +
                                              "  FailedPasswordAnswerAttemptCount, " +
                                              "  FailedPasswordAnswerAttemptWindowStart " +
                                              "  FROM `" + m_tableName + "` " +
                                              "  WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            MySqlDataReader reader = null;
            DateTime windowStart = new DateTime();
            int failureCount = 0;

            try
            {
                conn.Open();

                using (reader = cmd.ExecuteReader(System.Data.CommandBehavior.SingleRow))
                {
                    if (reader.HasRows)
                    {
                        reader.Read();

                        if (failureType == "password")
                        {
                            failureCount = reader.GetInt32(0);          //FailedPasswordAttemptCount
                            windowStart = reader.GetDateTime(1);    //FailedPasswordAttemptWindowStart
                        }

                        if (failureType == "passwordAnswer")
                        {
                            failureCount = reader.GetInt32(2);          //FailedPasswordAnswerAttemptCount
                            windowStart = reader.GetDateTime(3);    //FailedPasswordAnswerAttemptWindowStart
                        }
                    }
                    reader.Close();
                }

                DateTime windowEnd = windowStart.AddMinutes(PasswordAttemptWindow);

                if (failureCount == 0 || DateTime.Now > windowEnd)
                {
                    // First password failure or outside of PasswordAttemptWindow. 
                    // Start a new password failure count from 1 and a new window starting now.

                    if (failureType == "password")
                        cmd.CommandText = "UPDATE `" + m_tableName + "` " +
                                          "  SET FailedPasswordAttemptCount = ?Count, " +
                                          "      FailedPasswordAttemptWindowStart = ?WindowStart " +
                                          "  WHERE Username = ?Username AND ApplicationName = ?ApplicationName";

                    if (failureType == "passwordAnswer")
                        cmd.CommandText = "UPDATE `" + m_tableName + "` " +
                                          "  SET FailedPasswordAnswerAttemptCount = ?Count, " +
                                          "      FailedPasswordAnswerAttemptWindowStart = ?WindowStart " +
                                          "  WHERE Username = ?Username AND ApplicationName = ?ApplicationName";

                    cmd.Parameters.Clear();

                    cmd.Parameters.Add("?Count", MySqlDbType.Int32).Value = 1;
                    cmd.Parameters.Add("?WindowStart", MySqlDbType.DateTime).Value = DateTime.Now;
                    cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
                    cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

                    if (cmd.ExecuteNonQuery() < 0)
                        throw new ProviderException("Unable to update failure count and window start. ");
                }
                else
                {
                    if (failureCount++ >= MaxInvalidPasswordAttempts)
                    {
                        // Password attempts have exceeded the failure threshold. Lock out
                        // the user.

                        cmd.CommandText = "UPDATE `" + m_tableName + "` " +
                          "  SET IsLockedOut = ?IsLockedOut, LastLockedOutDate = ?LastLockedOutDate " +
                          "  WHERE Username = ?Username AND ApplicationName = ?ApplicationName";

                        cmd.Parameters.Clear();

                        cmd.Parameters.Add("?IsLockedOut", MySqlDbType.Bit).Value = true;
                        cmd.Parameters.Add("?LastLockedOutDate", MySqlDbType.DateTime).Value = DateTime.Now;
                        cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
                        cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

                        if (cmd.ExecuteNonQuery() < 0)
                            throw new ProviderException("Unable to lock out user.");
                    }
                    else
                    {
                        // Password attempts have not exceeded the failure threshold. Update
                        // the failure counts. Leave the window the same.

                        if (failureType == "password")
                            cmd.CommandText = "UPDATE `" + m_tableName + "` " +
                                              "  SET FailedPasswordAttemptCount = ?Count" +
                                              "  WHERE Username = ?Username AND ApplicationName = ?ApplicationName";

                        if (failureType == "passwordAnswer")
                            cmd.CommandText = "UPDATE `" + m_tableName + "` " +
                                              "  SET FailedPasswordAnswerAttemptCount = ?Count" +
                                              "  WHERE Username = ?Username AND ApplicationName = ?ApplicationName";

                        cmd.Parameters.Clear();

                        cmd.Parameters.Add("?Count", MySqlDbType.Int32).Value = failureCount;
                        cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
                        cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

                        if (cmd.ExecuteNonQuery() < 0)
                            throw new ProviderException("Unable to update failure count.");
                    }
                }
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UpdateFailureCount");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null)
                    reader.Close();

                conn.Close();
            }
        }

        /// <summary>
        /// 采用用户名作为输入,将数据源中存储 IsLockedOut 属性的字段更新为 false。 
        /// 如果成功更新成员资格用户的记录,则 UnlockUser 方法返回 true;否则,返回 false。
        /// </summary>
        public override bool UnlockUser(string userName)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("UPDATE `" + m_tableName + "` " +
                                              " SET IsLockedOut = 0, LastLockedOutDate = ?LastLockedOutDate " +
                                              " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?LastLockedOutDate", MySqlDbType.DateTime).Value = DateTime.Now;
            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = userName;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            int rowsAffected = 0;

            try
            {
                conn.Open();

                rowsAffected = cmd.ExecuteNonQuery();
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UnlockUser");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                conn.Close();
            }

            if (rowsAffected > 0)
                return true;

            return false;
        }

        /// <summary>
        /// 采用以用户信息填充的 MembershipUser 对象作为输入,并用所提供的值更新数据源。
        /// </summary>
        public override void UpdateUser(System.Web.Security.MembershipUser user)
        {
            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("UPDATE `" + m_tableName + "`" +
                    " SET Email = ?Email, Comment = ?Comment," +
                    " IsApproved = ?IsApproved" +
                    " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

            cmd.Parameters.Add("?Email", MySqlDbType.VarChar, 128).Value = user.Email;
            cmd.Parameters.Add("?Comment", MySqlDbType.VarChar, 255).Value = user.Comment;
            cmd.Parameters.Add("?IsApproved", MySqlDbType.Bit).Value = user.IsApproved;
            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = user.UserName;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            try
            {
                conn.Open();

                cmd.ExecuteNonQuery();
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UpdateUser");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                conn.Close();
            }
        }

        //
        // MembershipProvider.ValidateUser
        //

        public override bool ValidateUser(string username, string password)
        {
            bool isValid = false;

            MySqlConnection conn = new MySqlConnection(m_connectionString);
            MySqlCommand cmd = new MySqlCommand("SELECT Password, IsApproved FROM `" + m_tableName + "`" +
                    " WHERE Username = ?Username AND ApplicationName = ?ApplicationName AND IsLockedOut = 0", conn);

            cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

            MySqlDataReader reader = null;
            bool isApproved = false;
            string pwd = "";

            try
            {
                conn.Open();

                using (reader = cmd.ExecuteReader(System.Data.CommandBehavior.SingleRow))
                {
                    if (reader.HasRows)
                    {
                        reader.Read();
                        pwd = reader.GetString(0);
                        isApproved = reader.GetBoolean(1);
                    }
                    else
                    {
                        return false;
                    }
                    reader.Close();
                }

                if (CheckPassword(password, pwd))
                {
                    if (isApproved)
                    {
                        isValid = true;

                        MySqlCommand updateCmd = new MySqlCommand("UPDATE `" + m_tableName + "` SET LastLoginDate = ?LastLoginDate, LastActivityDate = ?LastActivityDate" +
                                                                " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn);

                        updateCmd.Parameters.Add("?LastLoginDate", MySqlDbType.DateTime).Value = DateTime.Now;
                        updateCmd.Parameters.Add("?LastActivityDate", MySqlDbType.DateTime).Value = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
                        updateCmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username;
                        updateCmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName;

                        updateCmd.ExecuteNonQuery();
                    }
                }
                else
                {
                    conn.Close();

                    UpdateFailureCount(username, "password");
                }
            }
            catch (MySqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "ValidateUser");

                    throw new ProviderException(m_exceptionMessage);
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                if (reader != null) { reader.Close(); }
                conn.Close();
            }

            return isValid;
        }

        /// <summary>
        /// 检索配置文件的值
        /// </summary>
        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (string.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }

    
        /// <summary>
        ///  CheckPassword
        ///  Compares password values based on the MembershipPasswordFormat.
        /// </summary>
        private bool CheckPassword(string password, string dbpassword)
        {
            string pass1 = password;
            string pass2 = dbpassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Encrypted:
                    pass2 = UnEncodePassword(dbpassword);
                    break;
                case MembershipPasswordFormat.Hashed:
                    pass1 = EncodePassword(password);
                    break;
                default:
                    break;
            }

            if (pass1 == pass2)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Encode Password
        /// Encrypts, Hashes, or leaves the password clear based on the PasswordFormat.
        /// </summary>
        private string EncodePassword(string password)
        {
            string encodePassword = password;

            switch(PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    encodePassword = Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    System.Security.Cryptography.HMACSHA1 hash = new System.Security.Cryptography.HMACSHA1();
                    hash.Key = HexToByte(m_machineKey.ValidationKey);
                    encodePassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
                    break;
                default:
                    throw new ProviderException("Unsupported password format .");
            }

            return encodePassword;
        }

        /// <summary>
        /// UnEncodePassword
        /// Decrypts or leaves the password clear based on the PasswordFormat.
        /// </summary>
        private string UnEncodePassword(string encodedPassword)
        {
            string password = encodedPassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    password =
                      Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    throw new ProviderException("Cannot unencode a hashed password.");
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return password;
        }

        /// <summary>
        ///  HexToByte
        ///  Converts a hexadecimal string to a byte array. Used to convert encryption
        ///  key values from the configuration.
        /// </summary>
        private byte[] HexToByte(string hexString)
        {
            byte[] returnBytes = new byte[hexString.Length / 2];
            for(int i =0;i<returnBytes.Length;i++)
            {
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            }
            return returnBytes;
        }

        /// <summary>
        /// WriteToEventLog
        /// A helper function that writes exception detail to the event log. Exceptions
        /// are written to the event log as a security measure to avoid private database
        /// details from being returned to the browser. If a method does not return a status
        /// or boolean indicating the action succeeded or failed, a generic exception is also 
        /// thrown by the caller.
        /// </summary>
        private void WriteToEventLog(Exception e, string action)
        {
            EventLog log = new EventLog();
            log.Source = m_eventSource;
            log.Log = m_eventLog;

            string message = "An exception occurred communicating with the data source. /n/n";
            message += "Action:  " + action + "/n/n";
            message += "Exception:  " + e.ToString();

            log.WriteEntry(message);
        }
    }
}






如果找不到System.Web.Security.MembershipProvider基类,请参考:



.net4.0开发支持MySql的Membership时遇到找不到MembershipProvider类的问题:http://blog.csdn.net/llxchen/archive/2010/12/09/6066145.aspx#1554597
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐