本文将自定义自己的两个
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的关联
为了简化本示例,我们不再提供如下功能
密码的“加密-解密”功能
用户注册时主要输入用户名,密码和邮件即可,不用输入安全提问和答案
我们不需要诸如密码恢复,账号锁定等功能
数据访问
这里我们使用BinaryIntellect DatabaseHelper 来完成本模块的数据库访问
建立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类的定义可以看到其属性/方法
下面列出了改类属性/方法以及意义的列表
(天天注:这里的意义翻译并没有取之原文,因为中文版本的MSDN已经提供了,你可以查看Member Provider类的成员列表)
属性/方法 | 意义 |
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,部分代码如下
(天天注:这里引用了更多的源代码)
1
using
BinaryIntellect.DataAccess;
2

3
public
class
MyMembershipProvider:MembershipProvider
4

{
5
private DatabaseHelper db = null;
6
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
7
{
8
string connstr = ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString;
9
db = new DatabaseHelper(connstr);
10
}
11
12
public override string Name
13
{
14
get
15
{
16
return "MyMembershipProvider"; }
17
}
18
19
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
20
{
21
MembershipUser user = new MembershipUser(Name, username, providerUserKey, email, passwordQuestion, null, isApproved, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
22
string sql = "INSERT INTO USERS(USERNAME,PASSWORD,EMAIL,ISACTIVE) VALUES(@UID,@PWD,@EMAIL,@ISACTIVE)";
23
db.AddParameter("@UID", username);
24
db.AddParameter("@PWD", password);
25
db.AddParameter("@EMAIL", email);
26
db.AddParameter("@ISACTIVE", (isApproved == true ? "Y" : "N"));
27
int i = db.ExecuteNonQuery(sql);
28
if (i > 0)
29
{
30
status = MembershipCreateStatus.Success;
31
return user;
32
}
33
34
else
35
{
36
status = MembershipCreateStatus.ProviderError;
37
return null;
38
}
39
}
40
41
public override void UpdateUser(MembershipUser user)
42
{
43
string sql = "UPDATE USERS SET EMAIL=@EMAIL,ISACTIVE=@ISACTIVE WHERE USERNAME=@UID";
44
db.AddParameter("@EMAIL", user.Email);
45
db.AddParameter("@ISACTIVE", (user.IsApproved ? "Y" : "N"));
46
db.AddParameter("@UID", user.UserName);
47
int i = db.ExecuteNonQuery(sql);
48
}
49
50
public override bool DeleteUser(string username, bool deleteAllRelatedData)
51
{
52
string sql = "DELETE FROM USERS WHERE USERNAME=@UID";
53
db.AddParameter("@UID", username);
54
int i = db.ExecuteNonQuery(sql);
55
if (i > 0)
56
return true;
57
else
58
return false;
59
}
60
61
public override MembershipUser GetUser(string username, bool userIsOnline)
62
{
63
MembershipUser user = null;
64
65
string sql = "SELECT * FROM USERS WHERE USERNAME=@UID AND ISACTIVE='Y'";
66
67
db.AddParameter("@UID", username);
68
69
SqlDataReader reader = (SqlDataReader)db.ExecuteReader(sql);
70
71
while (reader.Read())
72
{
73
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);
74
}
75
76
reader.Close();
77
return user;
78
}
79
80
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
81
{
82
MembershipUserCollection users = new MembershipUserCollection();
83
84
object obj=db.ExecuteScalar("SELECT COUNT(*) FROM USERS");
85
86
int reccount=0;
87
88
if (obj != null)
89
reccount = (int)obj;
90
91
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT * FROM USERS ORDER BY USERNAME");
92
93
while (reader.Read())
94
{
95
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);
96
users.Add(user);
97
}
98
reader.Close();
99
totalRecords = reccount;
100
return users;
101
}
102
103
#endregion
104
105
Password Management#region Password Management
106
107
public override bool ChangePassword(string username, string oldPassword, string newPassword)
108
{
109
string sql = "UPDATE USERS SET PASSWORD=@NEWPWD WHERE USERNAME=@UID AND PASSWORD=@OLDPWD";
110
db.AddParameter("@NEWPWD", newPassword);
111
db.AddParameter("@UID", username);
112
db.AddParameter("@OLDPWD", oldPassword);
113
int i = db.ExecuteNonQuery(sql);
114
if (i > 0)
115
return true;
116
else
117
return false;
118
}
119
120
public override string GetPassword(string username, string answer)
121
{
122
string sql = "SELECT PASSWORD FROM USERS WHERE USERNAME=@UID";
123
db.AddParameter("@UID", username);
124
object obj = db.ExecuteScalar(sql);
125
if (obj != null)
126
return obj.ToString();
127
else
128
return "";
129
}
130
131
#endregion
132
133
Authentication#region Authentication
134
135
public override bool ValidateUser(string username, string password)
136
{
137
string sql = "SELECT USERNAME FROM USERS WHERE USERNAME=@UID AND PASSWORD=@PWD";
138
139
db.AddParameter("@PWD", password);
140
141
db.AddParameter("@UID", username);
142
143
string uid = db.ExecuteScalar(sql) as string;
144
145
if (uid == null)
146
{
147
return false;
148
}
149
150
else
151
{
152
return true;
153
}
154
}
155
156
#endregion
157
158
Provider Configuration#region Provider Configuration
159
160
public override bool EnablePasswordReset
161
{
162
get
163
{
164
return false;
165
}
166
}
167
168
public override bool EnablePasswordRetrieval
169
{
170
get
171
{
172
return true;
173
}
174
}
175
176
public override bool RequiresQuestionAndAnswer
177
{
178
get
179
{
180
return false;
181
}
182
}
183
184
public override bool RequiresUniqueEmail
185
{
186
get
187
{
188
return false;
189
}
190
}
191
#endregion
192
}
193

194
建立自定义的角色Provider
该类完成列表请参考MSDN:RoleProvider
属性/方法 | 意义 |
Initialize()* | 初始化提供程序。 |
Name* | 显示自定义Provider的名称 |
CreateRole* | 在数据源中为已配置的 applicationName 添加一个新角色。 |
DeleteRole* | 从数据源中移除已配置的 applicationName 的角色。 |
GetAllRoles* | 获取已配置的 applicationName 的所有角色的列表。 |
RoleExists* | 获取一个值,该值指示指定角色名是否已存在于已配置的 applicationName 的角色数据源中。 |
AddUsersToRoles* | 将指定用户名添加到已配置的 applicationName 的指定角色名。 |
RemoveUsersFromRoles* | 移除已配置的 applicationName 的指定角色中的指定用户名。 |
GetRolesForUser* | 获取指定用户对于已配置的 applicationName 所属于的角色的列表。 |
GetUsersInRole* | 获取属于已配置的 applicationName 的指定角色的用户的列表。 |
IsUserInRole* | 获取一个值,指示指定用户是否属于已配置的 applicationName 的指定角色。 |
ApplicationName | 获取或设置要存储和检索其角色信息的应用程序的名称。 |
FindUsersInRole | 获取属于某个角色且与指定的用户名相匹配的用户名的数组。 |
在我们的代码里,我们将重载上面列出的前面具有“*”标记的属性或者方法,其它的属性方法只简单的抛出一个“没有执行”的异常
下面列出了部分代码
1
using
System;
2

3
using
System.Data;
4

5
using
System.Data.SqlClient;
6

7
using
System.Configuration;
8

9
using
System.Web;
10

11
using
System.Web.Security;
12

13
using
System.Web.UI;
14

15
using
System.Web.UI.WebControls;
16

17
using
System.Web.UI.WebControls.WebParts;
18

19
using
System.Web.UI.HtmlControls;
20

21
using
BinaryIntellect.DataAccess;
22

23
using
System.Collections;
24

25
public
class
MyRolesProvider:RoleProvider
26

27

{
28
29
private DatabaseHelper db = null;
30
31
General#region General
32
33
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
34
35
{
36
37
string connstr = ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString;
38
39
db = new DatabaseHelper(connstr);
40
41
}
42
43
public override string Name
44
45
{
46
47
get
48
49
{
50
51
return "MyRolesProvider";
52
53
}
54
55
}
56
57
#endregion
58
59
Role Management#region Role Management
60
61
public override void CreateRole(string roleName)
62
63
{
64
65
db.AddParameter("@ROLE", roleName);
66
67
db.ExecuteNonQuery("INSERT INTO ROLES(ROLENAME) VALUES(@ROLE)");
68
69
}
70
71
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
72
73
{
74
75
db.AddParameter("@ROLE", roleName);
76
77
int i = db.ExecuteNonQuery("DELETE FROM ROLES WHERE ROLENAME=@ROLE");
78
79
if (i > 0)
80
81
return true;
82
83
else
84
85
return false;
86
87
}
88
89
public override string[] GetAllRoles()
90
91
{
92
93
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM ROLES");
94
95
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT ROLENAME FROM ROLES");
96
97
string[] roles = new string[reccount];
98
99
int i = 0;
100
101
while (reader.Read())
102
103
{
104
105
roles[i] = reader.GetString(0);
106
107
i++;
108
109
}
110
111
reader.Close();
112
113
return roles;
114
115
}
116
117
public override bool RoleExists(string roleName)
118
119
{
120
121
db.AddParameter("@RID", roleName);
122
123
object obj = db.ExecuteScalar("SELECT ROLENAME FROM ROLES WHERE ROLENAME=@RID");
124
125
if (obj != null)
126
127
{
128
129
return true;
130
131
}
132
133
else
134
135
{
136
137
return false;
138
139
}
140
141
}
142
143
#endregion
144
145
Users and Roles#region Users and Roles
146
147
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
148
149
{
150
151
foreach (string user in usernames)
152
153
{
154
155
foreach (string role in roleNames)
156
157
{
158
159
db.AddParameter("@UID", user);
160
161
db.AddParameter("@RID", role);
162
163
db.ExecuteNonQuery("INSERT INTO USERROLES(USERNAME,ROLENAME) VALUES(@UID,@RID)");
164
165
}
166
167
}
168
169
}
170
171
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
172
173
{
174
175
foreach (string user in usernames)
176
177
{
178
179
foreach (string role in roleNames)
180
181
{
182
183
db.AddParameter("@UID", user);
184
185
db.AddParameter("@RID", role);
186
187
db.ExecuteNonQuery("DELETE FROM USERROLES WHERE USERNAME=@UID AND ROLENAME=@RID");
188
189
}
190
191
}
192
193
}
194
195
public override string[] GetRolesForUser(string username)
196
197
{
198
199
db.AddParameter("@UID", username);
200
201
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM USERROLES WHERE USERNAME=@UID");
202
203
string[] roles = new string[reccount];
204
205
db.AddParameter("@UID", username);
206
207
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT ROLENAME FROM USERROLES WHERE USERNAME=@UID");
208
209
int i = 0;
210
211
while (reader.Read())
212
213
{
214
215
roles[i] = reader.GetString(0);
216
217
i++;
218
219
}
220
221
reader.Close();
222
223
return roles;
224
225
}
226
227
public override string[] GetUsersInRole(string roleName)
228
229
{
230
231
db.AddParameter("@RID", roleName);
232
233
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM USERROLES WHERE ROLENAME=@RID");
234
235
string[] users = new string[reccount];
236
237
db.AddParameter("@RID", roleName);
238
239
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT USERNAME FROM USERROLES WHERE ROLENAME=@RID");
240
241
int i = 0;
242
243
while (reader.Read())
244
245
{
246
247
users[i] = reader.GetString(0);
248
249
i++;
250
251
}
252
253
reader.Close();
254
255
return users;
256
257
}
258
259
public override bool IsUserInRole(string username, string roleName)
260
261
{
262
263
db.AddParameter("@UID", username);
264
265
db.AddParameter("@RID", roleName);
266
267
object obj = db.ExecuteScalar("SELECT ROLENAME FROM USERROLES WHERE USERNAME=@UID AND ROLENAME=@RID");
268
269
if (obj != null)
270
271
{
272
273
return true;
274
275
}
276
277
else
278
279
{
280
281
return false;
282
283
}
284
285
}
286
287
测试自定义Provider
在你下载的页面里包含了四个Web窗体--default.aspx,Login.aspx,RoleManager.aspx和UserRoles.aspx。前面两个用于测试成员关系的Provider模型,后面两个用户测试角色的Provider模型。
我们使用了ASP.NET2.0提供的Membership和Roles的基本功能,这些类会使用我们自定义的Provider来完成相应的工作
总结
在本文我们我们可以看到自定义Membership和Role是多么的简单。你可以扩展该功能来适用你的应用程序,你还可以扩展诸如加密解密等功能