MySqlRoleProvider.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using MySql.Data.MySqlClient; using System.Configuration; using System.Collections.Specialized; using System.Configuration.Provider; /* CREATE TABLE Roles ( Rolename Varchar (255) NOT NULL, ApplicationName varchar (255) NOT NULL ) CREATE TABLE UsersInRoles ( Username Varchar (255) NOT NULL, Rolename Varchar (255) NOT NULL, ApplicationName Text (255) NOT NULL ) ALTER TABLE `usersinroles` ADD INDEX ( `Username` , `Rolename` , `ApplicationName` ) ; ALTER TABLE `roles` ADD INDEX ( `Rolename` , `ApplicationName` ) ; */ namespace Membership { public class MySqlRoleProvider : System.Web.Security.RoleProvider { #region MySqlRoleProvider properties // // Global connection string, generic exception message, event log info. // private string m_rolesTable = "Roles"; private string m_usersInRolesTable = "UsersInRoles"; private string m_eventSource = "MySqlRoleProvider"; private string m_eventLog = "Application"; private string m_exceptionMessage = "An exception occurred. Please check the Event Log."; private ConnectionStringSettings m_ConnectionStringSettings; private string m_connectionString; // // If false, exceptions are thrown to the caller. If true, // exceptions are written to the event log. // private bool m_writeExceptionsToEventLog = false; public bool WriteExceptionsToEventLog { get { return m_writeExceptionsToEventLog; } set { m_writeExceptionsToEventLog = value; } } // // System.Web.Security.RoleProvider properties. // private string m_applicationName; public override string ApplicationName { get { return m_applicationName; } set { m_applicationName = value; } } #endregion #region System.Web.Security.RoleProvider methods. // // System.Configuration.Provider.ProviderBase.Initialize Method // public override void Initialize(string name, NameValueCollection config) { // // Initialize values from web.config. // if (config == null) throw new ArgumentNullException("config"); if (name == null || name.Length == 0) name = "MySqlRoleProvider"; if (String.IsNullOrEmpty(config["description"])) { config.Remove("description"); config.Add("description", "Sample MySql Role provider"); } // Initialize the abstract base class. base.Initialize(name, config); if (config["applicationName"] == null || config["applicationName"].Trim() == "") { m_applicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath; } else { m_applicationName = config["applicationName"]; } if (config["writeExceptionsToEventLog"] != null) { if (config["writeExceptionsToEventLog"].ToUpper() == "TRUE") { m_writeExceptionsToEventLog = true; } } // // Initialize MySqlConnection. // m_ConnectionStringSettings = ConfigurationManager. ConnectionStrings[config["connectionStringName"]]; if (m_ConnectionStringSettings == null || m_ConnectionStringSettings.ConnectionString.Trim() == "") { throw new ProviderException("Connection string cannot be blank."); } m_connectionString = m_ConnectionStringSettings.ConnectionString; } /// <summary> /// 将指定用户名添加到已配置的 applicationName 的指定角色名。 /// AddUsersToRoles 由 Roles 类调用,以使指定用户与数据源中的指定角色相关联。 角色会被添加到已配置的 ApplicationName。 /// 如果没有为已配置的 applicationName 找到任何指定的角色名称,则引发 ProviderException。 /// 如果任何指定的用户名与已配置的 applicationName 的任何指定的角色名称均不相关,则引发 ProviderException。 /// 如果任何指定的用户名为 null 或是空字符串,则引发异常。 /// 如果任何指定的角色名称为 null 或是空字符串,则引发异常。 /// 如果数据源支持事务,则建议将每个添加操作包含在一个事务中,如果有任何添加操作出现失败,则回滚事务并引发异常。 /// </summary> /// <param name="usernames">一个字符串数组,其中包含要添加到指定角色的用户名。</param> /// <param name="rolenames">一个字符串数组,其中包含要将指定用户名添加到的角色的名称。</param> public override void AddUsersToRoles(string[] usernames, string[] rolenames) { foreach (string rolename in rolenames) { if (rolename == null || rolename == "") throw new ProviderException("Role name cannot be empty or null."); if (!RoleExists(rolename)) throw new ProviderException("Role name not found."); } foreach (string username in usernames) { if (username == null || username == "") throw new ProviderException("User name cannot be empty or null."); if (username.IndexOf(',') > 0) throw new ArgumentException("User names cannot contain commas."); foreach (string rolename in rolenames) { if (IsUserInRole(username, rolename)) throw new ProviderException("User is already in role."); } } MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("INSERT INTO `" + m_usersInRolesTable + "`" + " (Username, Rolename, ApplicationName) " + " Values(?Username, ?Rolename, ?ApplicationName)", conn); MySqlParameter userParm = cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255); MySqlParameter roleParm = cmd.Parameters.Add("?Rolename", MySqlDbType.VarChar, 255); cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; MySqlTransaction tran = null; try { conn.Open(); tran = conn.BeginTransaction(); cmd.Transaction = tran; foreach (string username in usernames) { foreach (string rolename in rolenames) { userParm.Value = username; roleParm.Value = rolename; cmd.ExecuteNonQuery(); } } tran.Commit(); } catch (MySqlException e) { try { tran.Rollback(); } catch { } if (WriteExceptionsToEventLog) { WriteToEventLog(e, "AddUsersToRoles"); } else { throw e; } } finally { conn.Close(); } } /// <summary> /// 在数据源中为已配置的 applicationName 添加一个新角色。 /// CreateRole 由 Roles 类的 CreateRole 方法调用,以在数据源中为已配置的 ApplicationName 添加指定角色。 /// 如果指定角色名对于已配置的 applicationName 已存在,或者指定角色名为 null 或为空字符串,则提供程序引发异常。 /// 如果指定角色名包含逗号,则提供程序引发异常。 /// 如果数据源限制角色名的长度(例如通过数据库中的表的固定长度列),则建议在角色名超过数据源所允许的最大长度时引发异常。 /// </summary> public override void CreateRole(string rolename) { if (rolename == null || rolename == "") throw new ProviderException("Role name cannot be empty or null."); if (rolename.Contains(",")) throw new ArgumentException("Role names cannot contain commas."); if (RoleExists(rolename)) throw new ProviderException("Role name already exists."); if (rolename.Length > 255) throw new ProviderException("Role name cannot exceed 255 characters."); MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("INSERT INTO `" + m_rolesTable + "`" + " (Rolename, ApplicationName) " + " Values(?Rolename, ?ApplicationName)", conn); cmd.Parameters.Add("?Rolename", MySqlDbType.VarChar, 255).Value = rolename; cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; try { conn.Open(); cmd.ExecuteNonQuery(); } catch (MySqlException e) { if (WriteExceptionsToEventLog) { WriteToEventLog(e, "CreateRole"); } else { throw e; } } finally { conn.Close(); } } /// <summary> /// 从数据源中移除已配置的 applicationName 的角色。 /// DeleteRole 由 Roles 类的 DeleteRole 和 DeleteRole 方法调用,以从数据源中删除已配置的 ApplicationName 的指定角色。 /// 从数据源删除角色时,确保也删除用户名与已配置的 applicationName 的已删除角色之间的任何关联。 /// 如果 throwOnPopulatedRole 为 true,且由 roleName 参数标识的角色具有一个或多个成员,则引发 ProviderException,并且不删除该角色。 /// 如果 throwOnPopulatedRole 为 false,则不论角色是否为空,都会删除该角色。 /// 如果指定的角色名称不存在、为 null 或是空字符串,则建议提供程序引发异常。 /// </summary> /// <param name="rolename">要删除的角色的名称。</param> /// <param name="throwOnPopulatedRole">如果为 true,则在 roleName 具有一个或多个成员时引发异常,并且不删除 roleName。</param> /// <returns>如果成功删除角色,则为 true;否则为 false。</returns> public override bool DeleteRole(string rolename, bool throwOnPopulatedRole) { if (!RoleExists(rolename)) { throw new ProviderException("Role does not exist."); } if (throwOnPopulatedRole && GetUsersInRole(rolename).Length > 0) { throw new ProviderException("Cannot delete a populated role."); } MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("DELETE FROM `" + m_rolesTable + "`" + " WHERE Rolename = ?Rolename AND ApplicationName = ?ApplicationName", conn); cmd.Parameters.Add("?Rolename", MySqlDbType.VarChar, 255).Value = rolename; cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; MySqlCommand cmd2 = new MySqlCommand("DELETE FROM `" + m_usersInRolesTable + "`" + " WHERE Rolename = ?Rolename AND ApplicationName = ?ApplicationName", conn); cmd2.Parameters.Add("?Rolename", MySqlDbType.VarChar, 255).Value = rolename; cmd2.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; MySqlTransaction tran = null; try { conn.Open(); tran = conn.BeginTransaction(); cmd.Transaction = tran; cmd2.Transaction = tran; cmd2.ExecuteNonQuery(); cmd.ExecuteNonQuery(); tran.Commit(); } catch (MySqlException e) { try { tran.Rollback(); } catch { } if (WriteExceptionsToEventLog) { WriteToEventLog(e, "DeleteRole"); return false; } else { throw e; } } finally { conn.Close(); } return true; } /// <summary> /// 获取已配置的 applicationName 的所有角色的列表。 /// GetAllRoles 由 Roles 类的 GetAllRoles 方法调用,以从数据源检索角色名的列表。 仅检索指定的 ApplicationName 的角色。 /// 如果对于已配置的 applicationName 不存在任何角色,则返回不包含任何元素的字符串数组。 /// </summary> /// <returns>一个字符串数组,包含在数据源中存储的已配置的 applicationName 的所有角色的名称。</returns> public override string[] GetAllRoles() { string tmpRoleNames = ""; MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("SELECT Rolename FROM `" + m_rolesTable + "`" + " WHERE ApplicationName = ?ApplicationName", conn); cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; MySqlDataReader reader = null; try { conn.Open(); using (reader = cmd.ExecuteReader()) { while (reader.Read()) { tmpRoleNames += reader.GetString(0) + ","; } reader.Close(); } } catch (MySqlException e) { if (WriteExceptionsToEventLog) { WriteToEventLog(e, "GetAllRoles"); } else { throw e; } } finally { if (reader != null) { reader.Close(); } conn.Close(); } if (tmpRoleNames.Length > 0) { // Remove trailing comma. tmpRoleNames = tmpRoleNames.Substring(0, tmpRoleNames.Length - 1); return tmpRoleNames.Split(','); } return new string[0]; } /// <summary> /// 获取指定用户对于已配置的 applicationName 所属于的角色的列表。 /// GetRolesForUser 由 Roles 类的 GetRolesForUser 方法调用,以从数据源检索指定用户相关联的角色名。 仅检索已配置的 ApplicationName 的角色。 /// 如果对于已配置的 applicationName 的指定用户不存在任何角色,则返回不包含任何元素的字符串数组。 /// 如果指定的用户名为 null 或是空字符串,则引发异常。 /// </summary> /// <param name="username">要为其返回角色列表的用户。</param> /// <returns>一个字符串数组,其中包含指定用户对于已配置的 applicationName 所属于的所有角色的名称。</returns> public override string[] GetRolesForUser(string username) { string tmpRoleNames = ""; MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("SELECT Rolename FROM `" + m_usersInRolesTable + "`" + " WHERE Username = ?Username AND ApplicationName = ?ApplicationName", conn); cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username; cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; MySqlDataReader reader = null; try { conn.Open(); using (reader = cmd.ExecuteReader()) { while (reader.Read()) { tmpRoleNames += reader.GetString(0) + ","; } reader.Close(); } } catch (MySqlException e) { if (WriteExceptionsToEventLog) { WriteToEventLog(e, "GetRolesForUser"); } else { throw e; } } finally { if (reader != null) { reader.Close(); } conn.Close(); } if (tmpRoleNames.Length > 0) { // Remove trailing comma. tmpRoleNames = tmpRoleNames.Substring(0, tmpRoleNames.Length - 1); return tmpRoleNames.Split(','); } return new string[0]; } /// <summary> /// 获取属于已配置的 applicationName 的指定角色的用户的列表。 /// GetUsersInRole 由 Roles 类的 GetUsersInRole 方法调用,以从数据源检索与某个角色相关联的用户名。 仅检索已配置的 ApplicationName 的角色。 /// 如果对于已配置的 applicationName 不存在指定角色名,或者如果指定角色名为 null 或空字符串,则引发异常。 /// 如果没有与已配置的 applicationName 的指定角色相关联的用户,则建议提供程序返回不包含任何元素的字符串数组。 /// </summary> /// <param name="rolename">一个角色名称,将获取该角色的用户列表。</param> /// <returns>一个字符串数组,其中包含属于已配置的 applicationName 的指定角色的成员的所有用户名。</returns> public override string[] GetUsersInRole(string rolename) { if (rolename == null || rolename == "") throw new ProviderException("Role name cannot be empty or null."); if (!RoleExists(rolename)) throw new ProviderException("Role does not exist."); string tmpUserNames = ""; MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("SELECT Username FROM `" + m_usersInRolesTable + "`" + " WHERE Rolename = ?Rolename AND ApplicationName = ?ApplicationName", conn); cmd.Parameters.Add("?Rolename", MySqlDbType.VarChar, 255).Value = rolename; cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; MySqlDataReader reader = null; try { conn.Open(); using (reader = cmd.ExecuteReader()) { while (reader.Read()) { tmpUserNames += reader.GetString(0) + ","; } reader.Close(); } } catch (MySqlException e) { if (WriteExceptionsToEventLog) { WriteToEventLog(e, "GetUsersInRole"); } else { throw e; } } finally { if (reader != null) { reader.Close(); } conn.Close(); } if (tmpUserNames.Length > 0) { // Remove trailing comma. tmpUserNames = tmpUserNames.Substring(0, tmpUserNames.Length - 1); return tmpUserNames.Split(','); } return new string[0]; } /// <summary> /// 获取一个值,指示指定用户是否属于已配置的 applicationName 的指定角色。 /// IsUserInRole 方法由 Roles 类的 IsUserInRole 方法调用,以确定当前登录用户是否与数据源中已配置的 ApplicationName 的某个角色相关联。 /// 如果指定的用户名为 null 或是空字符串,则引发异常。 /// 如果指定的角色名称为 null 或是空字符串,则引发异常。 /// </summary> /// <param name="username">要搜索的用户名。</param> /// <param name="rolename">作为搜索范围的角色。</param> /// <returns>如果指定用户属于已配置的 applicationName 的指定角色,则为 true;否则为 false。</returns> public override bool IsUserInRole(string username, string rolename) { if (username == null || username == "") throw new ProviderException("User name cannot be empty or null."); if (rolename == null || rolename == "") throw new ProviderException("Role name cannot be empty or null."); bool userIsInRole = false; MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("SELECT COUNT(*) FROM `" + m_usersInRolesTable + "`" + " WHERE Username = ?Username AND Rolename = ?Rolename AND ApplicationName = ?ApplicationName", conn); cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255).Value = username; cmd.Parameters.Add("?Rolename", MySqlDbType.VarChar, 255).Value = rolename; cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; try { conn.Open(); long numRecs = Convert.ToInt64(cmd.ExecuteScalar()); if (numRecs > 0) { userIsInRole = true; } } catch (MySqlException e) { if (WriteExceptionsToEventLog) { WriteToEventLog(e, "IsUserInRole"); } else { throw e; } } finally { conn.Close(); } return userIsInRole; } /// <summary> /// 移除已配置的 applicationName 的指定角色中的指定用户名。 /// RemoveUsersFromRoles 由 Roles 类的 RemoveUserFromRole、RemoveUsersFromRole、RemoveUserFromRoles 和 RemoveUsersFromRoles 方法调用,以从数据源中的指定角色中移除指定用户。 仅修改已配置的 ApplicationName 的角色。 /// 如果没有为已配置的 applicationName 找到任何指定的角色名称,则引发 ProviderException。 /// 如果任何指定的用户名与已配置的 applicationName 的任何指定的角色名称均不相关,则引发 ProviderException。 /// 如果任何指定的用户名为 null 或是空字符串,则引发异常。 /// 如果任何指定的角色名称为 null 或是空字符串,则引发异常。 /// 如果数据源支持事务,则建议将每个移除操作包含在一个事务中,如果有任何移除操作出现失败,则回滚事务并引发异常。 /// </summary> /// <param name="usernames">一个字符串数组,其中包含要从指定角色中移除的用户名。</param> /// <param name="rolenames">一个字符串数组,其中包含要将指定用户名从中移除的角色的名称。</param> public override void RemoveUsersFromRoles(string[] usernames, string[] rolenames) { foreach (string rolename in rolenames) { if (rolename == null || rolename == "") throw new ProviderException("Role name cannot be empty or null."); if (!RoleExists(rolename)) throw new ProviderException("Role name not found."); } foreach (string username in usernames) { if (username == null || username == "") throw new ProviderException("User name cannot be empty or null."); foreach (string rolename in rolenames) { if (!IsUserInRole(username, rolename)) throw new ProviderException("User is not in role."); } } MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("DELETE FROM `" + m_usersInRolesTable + "`" + " WHERE Username = ?Username AND Rolename = ?Rolename AND ApplicationName = ?ApplicationName", conn); MySqlParameter userParm = cmd.Parameters.Add("?Username", MySqlDbType.VarChar, 255); MySqlParameter roleParm = cmd.Parameters.Add("?Rolename", MySqlDbType.VarChar, 255); cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; MySqlTransaction tran = null; try { conn.Open(); tran = conn.BeginTransaction(); cmd.Transaction = tran; foreach (string username in usernames) { foreach (string rolename in rolenames) { userParm.Value = username; roleParm.Value = rolename; cmd.ExecuteNonQuery(); } } tran.Commit(); } catch (MySqlException e) { try { tran.Rollback(); } catch { } if (WriteExceptionsToEventLog) { WriteToEventLog(e, "RemoveUsersFromRoles"); } else { throw e; } } finally { conn.Close(); } } /// <summary> /// 获取一个值,该值指示指定角色名是否已存在于已配置的 applicationName 的角色数据源中。 /// RoleExists 由 Roles 类的 RoleExists 方法调用,以确定一个角色名是否存在于已配置的 ApplicationName 的数据源中。 /// 如果指定的角色名称为 null 或是空字符串,则引发异常。 /// </summary> /// <param name="rolename">要在数据源中搜索的角色的名称。</param> /// <returns>如果角色名已存在于已配置的 applicationName 的数据源中,则为 true;否则为 false。</returns> public override bool RoleExists(string rolename) { if (rolename == null || rolename == "") throw new ProviderException("Role name cannot be empty or null."); bool exists = false; MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("SELECT COUNT(*) FROM `" + m_rolesTable + "`" + " WHERE Rolename = ?Rolename AND ApplicationName = ?ApplicationName", conn); cmd.Parameters.Add("?Rolename", MySqlDbType.VarChar, 255).Value = rolename; cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = ApplicationName; try { conn.Open(); long numRecs = Convert.ToInt64(cmd.ExecuteScalar()); if (numRecs > 0) { exists = true; } } catch (MySqlException e) { if (WriteExceptionsToEventLog) { WriteToEventLog(e, "RoleExists"); } else { throw e; } } finally { conn.Close(); } return exists; } /// <summary> /// 获取属于某个角色且与指定的用户名相匹配的用户名的数组。 /// FindUsersInRole 方法由 Roles 类调用,并返回属于某个角色且与所提供的已配置 applicationName 的 usernameToMatch 相匹配的用户名列表。 /// 根据数据源的不同可提供通配符支持。 按用户名的字母顺序返回用户。 /// </summary> /// <param name="rolename">作为搜索范围的角色。</param> /// <param name="usernameToMatch">要搜索的用户名。</param> /// <returns>一个字符串数组,包含用户名与 usernameToMatch 匹配且用户是指定角色的成员的所有用户的名称。</returns> public override string[] FindUsersInRole(string rolename, string usernameToMatch) { MySqlConnection conn = new MySqlConnection(m_connectionString); MySqlCommand cmd = new MySqlCommand("SELECT Username FROM `" + m_usersInRolesTable + "` " + "WHERE Username LIKE ?UsernameSearch AND Rolename = ?Rolename AND ApplicationName = ?ApplicationName", conn); cmd.Parameters.Add("?UsernameSearch", MySqlDbType.VarChar, 255).Value = usernameToMatch; cmd.Parameters.Add("?RoleName", MySqlDbType.VarChar, 255).Value = rolename; cmd.Parameters.Add("?ApplicationName", MySqlDbType.VarChar, 255).Value = m_applicationName; string tmpUserNames = ""; MySqlDataReader reader = null; try { conn.Open(); using (reader = cmd.ExecuteReader()) { while (reader.Read()) { tmpUserNames += reader.GetString(0) + ","; } reader.Close(); } } catch (MySqlException e) { if (WriteExceptionsToEventLog) { WriteToEventLog(e, "FindUsersInRole"); } else { throw e; } } finally { if (reader != null) { reader.Close(); } conn.Close(); } if (tmpUserNames.Length > 0) { // Remove trailing comma. tmpUserNames = tmpUserNames.Substring(0, tmpUserNames.Length - 1); return tmpUserNames.Split(','); } return new string[0]; } #endregion // // 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. // private void WriteToEventLog(MySqlException e, string action) { EventLog log = new EventLog(); log.Source = m_eventSource; log.Log = m_eventLog; string message = m_exceptionMessage + "/n/n"; message += "Action: " + action + "/n/n"; message += "Exception: " + e.ToString(); log.WriteEntry(message); } } }