在本系列的第一和 第二部分,我们介绍了ASP.NET Provider的概念和内部体系结构,我们知道provider模型是可以扩展的,我们可以自己定义自己的模型结构来完成更适合自己需求的Provider。本文将自定义自己的两个Providers--一个是成员membership Provider,另外一个是角色Role Provider。
在本系列的第一和 第二部分,我们介绍了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的关联
为了简化本示例,我们不再提供如下功能
密码的“加密-解密”功能
用户注册时主要输入用户名,密码和邮件即可,不用输入安全提问和答案
我们不需要诸如密码恢复,账号锁定等功能
数据访问
这里我们使用BinaryIntellect DatabaseHelper 来完成本模块的数据库访问
建立Web站点
首先,建立一个Web站点,然后在App_Code目录学建立两个类:MyMembershipProvider 和MyRoleProvider。为了简化应用程序,我们建立webSite本身需要的必要类。在实际的应用中,你可以建立单独的类来包含这些类。
配置Web应用程序使用自定义Providers
打开web.config并增加如下的标记:
1
<membership defaultProvider="mymembershipprovider">
2![]()
3
<providers>
4![]()
5
<add name="mymembershipprovider"
6![]()
7
type="MyMembershipProvider"
8![]()
9
connectionStringName="connstr"/>
10![]()
11
</providers>
12![]()
13
</membership>
14![]()
1
<roleManager enabled="true" defaultProvider="myrolesprovider">
2![]()
3
<providers>
4![]()
5
<add name="myrolesprovider"
6![]()
7
type="MyRolesProvider"
8![]()
9
connectionStringName="connstr"/>
10![]()
11
</providers>
12![]()
13
</roleManager>
14![]()
15![]()
在这两段代码里,我们告诉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
4![]()
5
public class MyMembershipProvider:MembershipProvider
6![]()
7![]()
![]()
{
8![]()
9
private DatabaseHelper db = null;
10![]()
11
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
12![]()
13![]()
{
14![]()
15
string connstr = ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString;
16![]()
17
db = new DatabaseHelper(connstr);
18![]()
19
}
20![]()
21
22![]()
23
public override string Name
24![]()
25![]()
{ get
{ return "MyMembershipProvider"; }
26![]()
27
}
28![]()
29
30![]()
31
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
32![]()
33![]()
{
34![]()
35
MembershipUser user = new MembershipUser(Name, username, providerUserKey, email, passwordQuestion, null, isApproved, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now);
36![]()
37
string sql = "INSERT INTO USERS(USERNAME,PASSWORD,EMAIL,ISACTIVE) VALUES(@UID,@PWD,@EMAIL,@ISACTIVE)";
38![]()
39
db.AddParameter("@UID", username);
40![]()
41
db.AddParameter("@PWD", password);
42![]()
43
db.AddParameter("@EMAIL", email);
44![]()
45
db.AddParameter("@ISACTIVE", (isApproved == true ? "Y" : "N"));
46![]()
47
int i = db.ExecuteNonQuery(sql);
48![]()
49
50![]()
51
if (i > 0)
52![]()
53![]()
{
54![]()
55
status = MembershipCreateStatus.Success;
56![]()
57
return user;
58![]()
59
}
60![]()
61
else
62![]()
63![]()
{
64![]()
65
status = MembershipCreateStatus.ProviderError;
66![]()
67
return null;
68![]()
69
}
70![]()
71
72![]()
73
}
74![]()
75
76![]()
77
public override void UpdateUser(MembershipUser user)
78![]()
79![]()
{
80![]()
81
string sql = "UPDATE USERS SET EMAIL=@EMAIL,ISACTIVE=@ISACTIVE WHERE USERNAME=@UID";
82![]()
83
db.AddParameter("@EMAIL", user.Email);
84![]()
85
db.AddParameter("@ISACTIVE", (user.IsApproved ? "Y" : "N"));
86![]()
87
db.AddParameter("@UID", user.UserName);
88![]()
89
int i = db.ExecuteNonQuery(sql);
90![]()
91
}
92![]()
93
94![]()
95
public override bool DeleteUser(string username, bool deleteAllRelatedData)
96![]()
97![]()
{
98![]()
99
string sql = "DELETE FROM USERS WHERE USERNAME=@UID";
100![]()
101
db.AddParameter("@UID", username);
102![]()
103
int i = db.ExecuteNonQuery(sql);
104![]()
105
if (i > 0)
106![]()
107
return true;
108![]()
109
else
110![]()
111
return false;
112![]()
113
114![]()
115
}
116![]()
117
118![]()
119
public override MembershipUser GetUser(string username, bool userIsOnline)
120![]()
121![]()
{
122![]()
123
MembershipUser user = null;
124![]()
125
string sql = "SELECT * FROM USERS WHERE USERNAME=@UID AND ISACTIVE='Y'";
126![]()
127
db.AddParameter("@UID", username);
128![]()
129
SqlDataReader reader = (SqlDataReader)db.ExecuteReader(sql);
130![]()
131
while (reader.Read())
132![]()
133![]()
{
134![]()
135
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);
136![]()
137
}
138![]()
139
reader.Close();
140![]()
141
return user;
142![]()
143
}
144![]()
145
146![]()
147
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
148![]()
149![]()
{
150![]()
151
MembershipUserCollection users = new MembershipUserCollection();
152![]()
153
object obj=db.ExecuteScalar("SELECT COUNT(*) FROM USERS");
154![]()
155
int reccount=0;
156![]()
157
if (obj != null)
158![]()
159
reccount = (int)obj;
160![]()
161
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT * FROM USERS ORDER BY USERNAME");
162![]()
163
while (reader.Read())
164![]()
165![]()
{
166![]()
167
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);
168![]()
169
users.Add(user);
170![]()
171
}
172![]()
173
reader.Close();
174![]()
175
totalRecords = reccount;
176![]()
177
return users;
178![]()
179
}
180![]()
181
182![]()
183
#endregion
184![]()
185
186![]()
187![]()
Password Management#region Password Management
188![]()
189
190![]()
191
public override bool ChangePassword(string username, string oldPassword, string newPassword)
192![]()
193![]()
{
194![]()
195
string sql = "UPDATE USERS SET PASSWORD=@NEWPWD WHERE USERNAME=@UID AND PASSWORD=@OLDPWD";
196![]()
197
db.AddParameter("@NEWPWD", newPassword);
198![]()
199
db.AddParameter("@UID", username);
200![]()
201
db.AddParameter("@OLDPWD", oldPassword);
202![]()
203
int i = db.ExecuteNonQuery(sql);
204![]()
205
if (i > 0)
206![]()
207
return true;
208![]()
209
else
210![]()
211
return false;
212![]()
213
}
214![]()
215
216![]()
217
public override string GetPassword(string username, string answer)
218![]()
219![]()
{
220![]()
221
string sql = "SELECT PASSWORD FROM USERS WHERE USERNAME=@UID";
222![]()
223
db.AddParameter("@UID", username);
224![]()
225
object obj = db.ExecuteScalar(sql);
226![]()
227
if (obj != null)
228![]()
229
return obj.ToString();
230![]()
231
else
232![]()
233
return "";
234![]()
235
}
236![]()
237
#endregion
238![]()
239
240![]()
241![]()
Authentication#region Authentication
242![]()
243
244![]()
245
public override bool ValidateUser(string username, string password)
246![]()
247![]()
{
248![]()
249
string sql = "SELECT USERNAME FROM USERS WHERE USERNAME=@UID AND PASSWORD=@PWD";
250![]()
251
db.AddParameter("@PWD", password);
252![]()
253
db.AddParameter("@UID", username);
254![]()
255
string uid = db.ExecuteScalar(sql) as string;
256![]()
257
if (uid == null)
258![]()
259![]()
{
260![]()
261
return false;
262![]()
263
}
264![]()
265
else
266![]()
267![]()
{
268![]()
269
return true;
270![]()
271
}
272![]()
273
}
274![]()
275
#endregion
276![]()
277
278![]()
279![]()
Provider Configuration#region Provider Configuration
280![]()
281
282![]()
283
public override bool EnablePasswordReset
284![]()
285![]()
{
286![]()
287
get
288![]()
289![]()
{
290![]()
291
return false;
292![]()
293
}
294![]()
295
}
296![]()
297
298![]()
299
public override bool EnablePasswordRetrieval
300![]()
301![]()
{
302![]()
303
get
304![]()
305![]()
{
306![]()
307
return true;
308![]()
309
}
310![]()
311
}
312![]()
313
314![]()
315
public override bool RequiresQuestionAndAnswer
316![]()
317![]()
{
318![]()
319
get
320![]()
321![]()
{
322![]()
323
return false;
324![]()
325
}
326![]()
327
}
328![]()
329
330![]()
331
public override bool RequiresUniqueEmail
332![]()
333![]()
{
334![]()
335
get
336![]()
337![]()
{
338![]()
339
return false;
340![]()
341
}
342![]()
343
}
344![]()
345
#endregion
346![]()
347
348![]()
349
}
350![]()
351![]()
建立自定义的角色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
26![]()
27
public class MyRolesProvider:RoleProvider
28![]()
29![]()
![]()
{
30![]()
31
private DatabaseHelper db = null;
32![]()
33
34![]()
35![]()
General#region General
36![]()
37
38![]()
39
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
40![]()
41![]()
{
42![]()
43
string connstr = ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString;
44![]()
45
db = new DatabaseHelper(connstr);
46![]()
47
}
48![]()
49
50![]()
51
public override string Name
52![]()
53![]()
{
54![]()
55
get
56![]()
57![]()
{
58![]()
59
return "MyRolesProvider";
60![]()
61
}
62![]()
63
}
64![]()
65
#endregion
66![]()
67
68![]()
69![]()
Role Management#region Role Management
70![]()
71
72![]()
73
public override void CreateRole(string roleName)
74![]()
75![]()
{
76![]()
77
db.AddParameter("@ROLE", roleName);
78![]()
79
db.ExecuteNonQuery("INSERT INTO ROLES(ROLENAME) VALUES(@ROLE)");
80![]()
81
}
82![]()
83
84![]()
85
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
86![]()
87![]()
{
88![]()
89
db.AddParameter("@ROLE", roleName);
90![]()
91
int i = db.ExecuteNonQuery("DELETE FROM ROLES WHERE ROLENAME=@ROLE");
92![]()
93
if (i > 0)
94![]()
95
return true;
96![]()
97
else
98![]()
99
return false;
100![]()
101
}
102![]()
103
104![]()
105
public override string[] GetAllRoles()
106![]()
107![]()
{
108![]()
109
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM ROLES");
110![]()
111
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT ROLENAME FROM ROLES");
112![]()
113
string[] roles = new string[reccount];
114![]()
115
int i = 0;
116![]()
117
while (reader.Read())
118![]()
119![]()
{
120![]()
121
roles[i] = reader.GetString(0);
122![]()
123
i++;
124![]()
125
}
126![]()
127
reader.Close();
128![]()
129
return roles;
130![]()
131
}
132![]()
133
134![]()
135
public override bool RoleExists(string roleName)
136![]()
137![]()
{
138![]()
139
db.AddParameter("@RID", roleName);
140![]()
141
object obj = db.ExecuteScalar("SELECT ROLENAME FROM ROLES WHERE ROLENAME=@RID");
142![]()
143
if (obj != null)
144![]()
145![]()
{
146![]()
147
return true;
148![]()
149
}
150![]()
151
else
152![]()
153![]()
{
154![]()
155
return false;
156![]()
157
}
158![]()
159
160![]()
161
}
162![]()
163
164![]()
165
#endregion
166![]()
167
168![]()
169![]()
Users and Roles#region Users and Roles
170![]()
171
172![]()
173
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
174![]()
175![]()
{
176![]()
177
foreach (string user in usernames)
178![]()
179![]()
{
180![]()
181
foreach (string role in roleNames)
182![]()
183![]()
{
184![]()
185
db.AddParameter("@UID", user);
186![]()
187
db.AddParameter("@RID", role);
188![]()
189
db.ExecuteNonQuery("INSERT INTO USERROLES(USERNAME,ROLENAME) VALUES(@UID,@RID)");
190![]()
191
}
192![]()
193
}
194![]()
195
}
196![]()
197
198![]()
199
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
200![]()
201![]()
{
202![]()
203
foreach (string user in usernames)
204![]()
205![]()
{
206![]()
207
foreach (string role in roleNames)
208![]()
209![]()
{
210![]()
211
db.AddParameter("@UID", user);
212![]()
213
db.AddParameter("@RID", role);
214![]()
215
db.ExecuteNonQuery("DELETE FROM USERROLES WHERE USERNAME=@UID AND ROLENAME=@RID");
216![]()
217
}
218![]()
219
}
220![]()
221
}
222![]()
223
224![]()
225
public override string[] GetRolesForUser(string username)
226![]()
227![]()
{
228![]()
229
db.AddParameter("@UID", username);
230![]()
231
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM USERROLES WHERE USERNAME=@UID");
232![]()
233
string[] roles = new string[reccount];
234![]()
235
236![]()
237
db.AddParameter("@UID", username);
238![]()
239
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT ROLENAME FROM USERROLES WHERE USERNAME=@UID");
240![]()
241
int i = 0;
242![]()
243
while (reader.Read())
244![]()
245![]()
{
246![]()
247
roles[i] = reader.GetString(0);
248![]()
249
i++;
250![]()
251
}
252![]()
253
reader.Close();
254![]()
255
return roles;
256![]()
257
}
258![]()
259
260![]()
261
public override string[] GetUsersInRole(string roleName)
262![]()
263![]()
{
264![]()
265
db.AddParameter("@RID", roleName);
266![]()
267
int reccount = (int)db.ExecuteScalar("SELECT COUNT(*) FROM USERROLES WHERE ROLENAME=@RID");
268![]()
269
string[] users = new string[reccount];
270![]()
271
272![]()
273
db.AddParameter("@RID", roleName);
274![]()
275
SqlDataReader reader = (SqlDataReader)db.ExecuteReader("SELECT USERNAME FROM USERROLES WHERE ROLENAME=@RID");
276![]()
277
int i = 0;
278![]()
279
while (reader.Read())
280![]()
281![]()
{
282![]()
283
users[i] = reader.GetString(0);
284![]()
285
i++;
286![]()
287
}
288![]()
289
reader.Close();
290![]()
291
return users;
292![]()
293
294![]()
295
}
296![]()
297
298![]()
299
public override bool IsUserInRole(string username, string roleName)
300![]()
301![]()
{
302![]()
303
db.AddParameter("@UID", username);
304![]()
305
db.AddParameter("@RID", roleName);
306![]()
307
object obj = db.ExecuteScalar("SELECT ROLENAME FROM USERROLES WHERE USERNAME=@UID AND ROLENAME=@RID");
308![]()
309
if (obj != null)
310![]()
311![]()
{
312![]()
313
return true;
314![]()
315
}
316![]()
317
else
318![]()
319![]()
{
320![]()
321
return false;
322![]()
323
}
324![]()
325
}
326![]()
327
328![]()
329![]()
测试自定义Provider
在你下载的页面里包含了四个Web窗体--default.aspx,Login.aspx,RoleManager.aspx和UserRoles.aspx。前面两个用于测试成员关系的Provider模型,后面两个用户测试角色的Provider模型。
我们使用了ASP.NET2.0提供的Membership和Roles的基本功能,这些类会使用我们自定义的Provider来完成相应的工作
总结
在本文我们我们可以看到自定义Membership和Role是多么的简单。你可以扩展该功能来适用你的应用程序,你还可以扩展诸如加密解密等功能