在本系列的第一和 第二部分,我们介绍了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并增加如下的标记:
<
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
<
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,部分代码如下
(天天注:这里引用了更多的源代码)
using
BinaryIntellect.DataAccess;2

3
4

5
public
class
MyMembershipProvider:MembershipProvider6

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 Name24

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
else62

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
else110

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
#endregion184

185
186

187

Password Management#region Password Management188

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
else210

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
else232

233
return "";234

235
}236

237
#endregion238

239
240

241

Authentication#region Authentication242

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
else266

267

{268

269
return true;270

271
}272

273
}274

275
#endregion276

277
278

279

Provider Configuration#region Provider Configuration280

281
282

283
public override bool EnablePasswordReset284

285

{286

287
get288

289

{290

291
return false;292

293
}294

295
}296

297
298

299
public override bool EnablePasswordRetrieval300

301

{302

303
get 304

305

{306

307
return true;308

309
}310

311
}312

313
314

315
public override bool RequiresQuestionAndAnswer316

317

{318

319
get320

321

{322

323
return false;324

325
}326

327
}328

329
330

331
public override bool RequiresUniqueEmail332

333

{334

335
get336

337

{338

339
return false;340

341
}342

343
}344

345
#endregion346

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 | 获取属于某个角色且与指定的用户名相匹配的用户名的数组。 |
在我们的代码里,我们将重载上面列出的前面具有“*”标记的属性或者方法,其它的属性方法只简单的抛出一个“没有执行”的异常
下面列出了部分代码
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:RoleProvider28

29

{30

31
private DatabaseHelper db = null;32

33
34

35

General#region General36

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 Name52

53

{54

55
get56

57

{58

59
return "MyRolesProvider";60

61
}62

63
}64

65
#endregion66

67
68

69

Role Management#region Role Management70

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
else98

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
else152

153

{154

155
return false;156

157
}158

159
160

161
}162

163
164

165
#endregion166

167
168

169

Users and Roles#region Users and Roles170

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
else318

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是多么的简单。你可以扩展该功能来适用你的应用程序,你还可以扩展诸如加密解密等功能
本文详细介绍如何自定义ASP.NET的Membership和Role Providers,包括创建、更新、删除用户及角色的具体实现,展示了通过自定义数据库访问类实现数据操作的过程。
215

被折叠的 条评论
为什么被折叠?



