在本系列的第一和 第二部分,我们介绍了
ASP.NET Provider
的概念和内部体系结构,我们知道
provider
模型是可以扩展的,我们可以自己定义自己的模型结构来完成更适合自己需求的
Provider
。本文将自定义自己的两个
Providers--
一个是成员
membership Provider
,另外一个是角色
Role Provider
。
为什么开发自定义
Membership
和
Role
的
Provider
怎么说呢,原因可能很多,下面列出了部分原因:
你使用了其它数据库(也就是不是
SQL Server
或者
Access
数据库)来存放你的数据
你使用了一些非标准的数据库而这些数据库并没有内置的
membership
和
role Providers
模型
你想要执行数据在包含和检索过程具有加密的功能
你想要自己写数据库的处理而非以来于
membership
和
role Provider
模型
需求
现在让我们决定自定的
membership
和
role Provider
需要什么要求:
我们想要使用我们自己的数据库来存放
membership
和
role
信息,这就意味者我们不需要使用中间数据存储应用程序的名称
我们使用自定义的
Users
表来存放成员关系的详细信息
我们使用自定义的
Roles
表存放系统里可以使用的角色
我们使用
UserRoles
表来映射
User-Role
的关联
为了简化本示例,我们不再提供如下功能
密码的“加密
-
解密”功能
用户注册时主要输入用户名,密码和邮件即可,不用输入安全提问和答案
我们不需要诸如密码恢复,账号锁定等功能
数据访问
建立
Web
站点
首先,建立一个
Web
站点,然后在
App_Code
目录学建立两个类:
MyMembershipProvider
和
MyRoleProvider
。为了简化应用程序,我们建立
webSite
本身需要的必要类。在实际的应用中,你可以建立单独的类来包含这些类。
配置
Web
应用程序使用自定义
Providers
打开
web.config
并增加如下的标记:
<membership defaultProvider="mymembershipprovider">
<providers>
<add name="mymembershipprovider"
type="MyMembershipProvider"
connectionStringName="connstr"/>
</providers>
</membership>
<roleManager enabled="true" defaultProvider="myrolesprovider">
<providers>
<add name="myrolesprovider"
type="MyRolesProvider"
connectionStringName="connstr"/>
</providers>
</roleManager>
在这两段代码里,我们告诉
ASP.NET
系统使用
MyMembershipProvider
类来作为成员关系的提供者,使用
MyRolesProvider
类来作为角色的提供者,建立自定义成员关系的
Provider
回忆一下第二部分介绍的程序关系
Provider
模型,我们需要从
System.Web.Security.MembershipProvider
类派生出自定义的成员类
MembershipProvider
类又是从
ProviderBase
类继承而来。
MembershipProvider
类包含几个抽象的方法,你可以在你自定义的类中使用这些方法
如果你使用的是
VS.NET
开发环境,那么你可以减轻开发的工作。在
MembershipProvider
类的定义处右击属性,转到
MembershipProvider
类的定义可以看到其属性
/
方法
下面列出了改类属性
/
方法以及意义的列表
属性
/
方法
|
意义
|
Initialize()*
|
初始化提供程序。在这里获取
web.config
里数据库链接字符串的值,我们需要在自己的类里进行数据库处理
|
Name*
|
这里程序自定义
Provider
的名称。
|
CreateUser()*
|
建立一个用户
|
UpdateUser()*
|
保护注册用户资料的更改
|
DeleteUser()*
|
删除一个用户
|
GetUser()*
|
获取一个
MembershipUser
对象实例
|
GetAllUsers()*
|
获取
MembershipUserCollection
里用户的集合
|
ChangePassword()*
|
更改密码
|
GetPassword()*
|
从数据源获取指定用户名所对应的密码。
|
ValidateUser()*
|
验证数据源中是否存在指定的用户名和密码。
|
EnablePasswordReset*
|
指示成员资格提供程序是否配置为允许用户重置其密码。
|
EnablePasswordRetrieval*
|
指示成员资格提供程序是否配置为允许用户检索其密码。
|
RequiresQuestionAndAnswer*
|
获取一个值,该值指示成员资格提供程序是否配置为要求用户在进行密码重置和检索时回答密码提示问题。
|
RequiresUniqueEmail*
|
获取一个值,指示成员资格提供程序是否配置为要求每个用户名具有唯一的电子邮件地址。
|
ApplicationName
|
使用自定义成员资格提供程序的应用程序的名称。
|
MaxInvalidPasswordAttempts
|
获取锁定成员资格用户前允许的无效密码或无效密码提示问题答案尝试次数。
|
MinRequiredNonAlphanumericCharacters
|
获取有效密码中必须包含的最少特殊字符数。
|
MinRequiredPasswordLength
|
获取密码所要求的最小长度。
|
ChangePasswordQuestionAndAnswer()
|
处理更新成员资格用户的密码提示问题和答案的请求。
|
FindUsersByEmail()
|
获取一个成员资格用户的集合,这些用户的电子邮件地址包含要匹配的指定电子邮件地址。
|
FindUsersByName()
|
获取一个成员资格用户的集合,这些用户的用户名包含要匹配的指定用户名。
|
GetNumberOfUsersOnline()
|
获取当前访问该应用程序的用户数。
|
GetUser()
|
从数据源获取成员资格用户的信息。
|
GetUserNameByEmail()
|
利用邮件获取用户名
|
PasswordAttemptWindow
|
指示密码输入间隔的时间
|
PasswordFormat
|
密码格式,例如明文,
Hash
表等
|
PasswordStrengthRegularExpression
|
密码使用的正则表达式
|
ResetPassword()
|
重置用户密码
|
UnlockUser()
|
接锁用户
|
在我们的代码里,我们将重载上面列出的前面具有“
*
”标记的属性或者方法,其它的属性方法只简单的抛出一个“没有执行”的异常
完整的源代码请见下载文件里的
MyMembershipProvider.cs
,部分代码如下
(天天注:这里引用了更多的源代码)
using BinaryIntellect.DataAccess;
public class MyMembershipProvider:MembershipProvider
{
private DatabaseHelper db = null;
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
string connstr = ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString;
db = new DatabaseHelper(connstr);
}
public override string Name
{ get { return "MyMembershipProvider"; }
}
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
MembershipUser user = new MembershipUser(Name, username, providerUserKey, email, passwordQuestion, null, isApproved, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
string sql = "INSERT INTO USERS(USERNAME,PASSWORD,EMAIL,ISACTIVE) VALUES(@UID,@PWD,@EMAIL,@ISACTIVE)";
db.AddParameter("@UID", username);
db.AddParameter("@PWD", password);
db.AddParameter("@EMAIL", email);
db.AddParameter("@ISACTIVE", (isApproved == true ? "Y" : "N"));
int i = db.ExecuteNonQuery(sql);
if (i > 0)
{
status = MembershipCreateStatus.Success;
return user;
}
else
{
status = MembershipCreateStatus.ProviderError;
return null;
}
}
public override void UpdateUser(MembershipUser user)
{
string sql = "UPDATE USERS SET EMAIL=@EMAIL,ISACTIVE=@ISACTIVE WHERE USERNAME=@UID";
db.AddParameter("@EMAIL", user.Email);
db.AddParameter("@ISACTIVE", (user.IsApproved ? "Y" : "N"));
db.AddParameter("@UID", user.UserName);
int i = db.ExecuteNonQuery(sql);
}
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
string sql = "DELETE FROM USERS WHERE USERNAME=@UID";
db.AddParameter("@UID", username);
int i = db.ExecuteNonQuery(sql);
if (i > 0)
return true;
else
return false;
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
MembershipUser user = null;
string sql = "SELECT * FROM USERS WHERE USERNAME=@UID AND ISACTIVE='Y'";
db.AddParameter("@UID", username);
SqlDataReader reader = (SqlDataReader)db.ExecuteReader(sql);
while (reader.Read())
{
user = new MembershipUser(Name, reader.GetString(reader.GetOrdinal("username")), null, reader.GetString(reader.GetOrdinal("email")), null, null, (reader.GetString(reader.GetOrdinal("isactive")) == "Y" ? true : false), false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
}
reader.Close();
return user;
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
MembershipUserCollection users = new MembershipUserCollection();
object obj=db.ExecuteScalar("SELECT COUNT(*) FROM USERS");
int reccount=0;
if (obj != null)
reccount = (int)obj;
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT * FROM USERS ORDER BY USERNAME");
while (reader.Read())
{
MembershipUser user = new MembershipUser(Name, reader.GetString(reader.GetOrdinal("username")), null, reader.GetString(reader.GetOrdinal("email")), null, null, (reader.GetString(reader.GetOrdinal("isactive")) == "Y" ? true : false), false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
users.Add(user);
}
reader.Close();
totalRecords = reccount;
return users;
}
#endregion
#region Password Management
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
string sql = "UPDATE USERS SET PASSWORD=@NEWPWD WHERE USERNAME=@UID AND PASSWORD=@OLDPWD";
db.AddParameter("@NEWPWD", newPassword);
db.AddParameter("@UID", username);
db.AddParameter("@OLDPWD", oldPassword);
int i = db.ExecuteNonQuery(sql);
if (i > 0)
return true;
else
return false;
}
public override string GetPassword(string username, string answer)
{
string sql = "SELECT PASSWORD FROM USERS WHERE USERNAME=@UID";
db.AddParameter("@UID", username);
object obj = db.ExecuteScalar(sql);
if (obj != null)
return obj.ToString();
else
return "";
}
#endregion
#region Authentication
public override bool ValidateUser(string username, string password)
{
string sql = "SELECT USERNAME FROM USERS WHERE USERNAME=@UID AND PASSWORD=@PWD";
db.AddParameter("@PWD", password);
db.AddParameter("@UID", username);
string uid = db.ExecuteScalar(sql) as string;
if (uid == null)
{
return false;
}
else
{
return true;
}
}
#endregion
#region Provider Configuration
public override bool EnablePasswordReset
{
get
{
return false;
}
}
public override bool EnablePasswordRetrieval
{
get
{
return true;
}
}
public override bool RequiresQuestionAndAnswer
{
get
{
return false;
}
}
public override bool RequiresUniqueEmail
{
get
{
return false;
}
}
#endregion
}
建立自定义的角色
Provider
属性
/
方法
|
意义
|
Initialize()*
|
初始化提供程序。
|
Name*
|
显示自定义
Provider
的名称
|
CreateRole*
|
在数据源中为已配置的
applicationName
添加一个新角色。
|
DeleteRole*
|
从数据源中移除已配置的
applicationName
的角色。
|
GetAllRoles*
|
获取已配置的
applicationName
的所有角色的列表。
|
RoleExists*
|
获取一个值,该值指示指定角色名是否已存在于已配置的
applicationName
的角色数据源中。
|
AddUsersToRoles*
|
将指定用户名添加到已配置的
applicationName
的指定角色名。
|
RemoveUsersFromRoles*
|
移除已配置的
applicationName
的指定角色中的指定用户名。
|
GetRolesForUser*
|
获取指定用户对于已配置的
applicationName
所属于的角色的列表。
|
GetUsersInRole*
|
获取属于已配置的
applicationName
的指定角色的用户的列表。
|
IsUserInRole*
|
获取一个值,指示指定用户是否属于已配置的
applicationName
的指定角色。
|
ApplicationName
|
获取或设置要存储和检索其角色信息的应用程序的名称。
|
FindUsersInRole
|
获取属于某个角色且与指定的用户名相匹配的用户名的数组。
|
在我们的代码里,我们将重载上面列出的前面具有“
*
”标记的属性或者方法,其它的属性方法只简单的抛出一个“没有执行”的异常
下面列出了部分代码
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using BinaryIntellect.DataAccess;
using System.Collections;
public class MyRolesProvider:RoleProvider
{
private DatabaseHelper db = null;
#region General
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
string connstr = ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString;
db = new DatabaseHelper(connstr);
}
public override string Name
{
get
{
return "MyRolesProvider";
}
}
#endregion
#region Role Management
public override void CreateRole(string roleName)
{
db.AddParameter("@ROLE", roleName);
db.ExecuteNonQuery("INSERT INTO ROLES(ROLENAME) VALUES(@ROLE)");
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
db.AddParameter("@ROLE", roleName);
int i = db.ExecuteNonQuery("DELETE FROM ROLES WHERE ROLENAME=@ROLE");
if (i > 0)
return true;
else
return false;
}
public override string[] GetAllRoles()
{
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM ROLES");
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT ROLENAME FROM ROLES");
string[] roles = new string[reccount];
int i = 0;
while (reader.Read())
{
roles[i] = reader.GetString(0);
i++;
}
reader.Close();
return roles;
}
public override bool RoleExists(string roleName)
{
db.AddParameter("@RID", roleName);
object obj = db.ExecuteScalar("SELECT ROLENAME FROM ROLES WHERE ROLENAME=@RID");
if (obj != null)
{
return true;
}
else
{
return false;
}
}
#endregion
#region Users and Roles
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
foreach (string user in usernames)
{
foreach (string role in roleNames)
{
db.AddParameter("@UID", user);
db.AddParameter("@RID", role);
db.ExecuteNonQuery("INSERT INTO USERROLES(USERNAME,ROLENAME) VALUES(@UID,@RID)");
}
}
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
foreach (string user in usernames)
{
foreach (string role in roleNames)
{
db.AddParameter("@UID", user);
db.AddParameter("@RID", role);
db.ExecuteNonQuery("DELETE FROM USERROLES WHERE USERNAME=@UID AND ROLENAME=@RID");
}
}
}
public override string[] GetRolesForUser(string username)
{
db.AddParameter("@UID", username);
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM USERROLES WHERE USERNAME=@UID");
string[] roles = new string[reccount];
db.AddParameter("@UID", username);
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT ROLENAME FROM USERROLES WHERE USERNAME=@UID");
int i = 0;
while (reader.Read())
{
roles[i] = reader.GetString(0);
i++;
}
reader.Close();
return roles;
}
public override string[] GetUsersInRole(string roleName)
{
db.AddParameter("@RID", roleName);
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM USERROLES WHERE ROLENAME=@RID");
string[] users = new string[reccount];
db.AddParameter("@RID", roleName);
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT USERNAME FROM USERROLES WHERE ROLENAME=@RID");
int i = 0;
while (reader.Read())
{
users[i] = reader.GetString(0);
i++;
}
reader.Close();
return users;
}
public override bool IsUserInRole(string username, string roleName)
{
db.AddParameter("@UID", username);
db.AddParameter("@RID", roleName);
object obj = db.ExecuteScalar("SELECT ROLENAME FROM USERROLES WHERE USERNAME=@UID AND ROLENAME=@RID");
if (obj != null)
{
return true;
}
else
{
return false;
}
}
测试自定义
Provider
在你下载的页面里包含了四个
Web
窗体--
default.aspx
,
Login.aspx
,
RoleManager.aspx
和
UserRoles.aspx
。前面两个用于测试成员关系的
Provider
模型,后面两个用户测试角色的
Provider
模型。
我们使用了
ASP.NET2.0
提供的
Membership
和
Roles
的基本功能,这些类会使用我们自定义的
Provider
来完成相应的工作
总结
在本文我们我们可以看到自定义
Membership
和
Role
是多么的简单。你可以扩展该功能来适用你的应用程序,你还可以扩展诸如加密解密等功能