场景 你想阻止用户访问你网站的特定页面,除非用户已经注册并且使用了用户名和密码登陆。
解决方案
使用一个AccountController,AccountModels 和 几个MVC View,配合ASP.NET的 AuthorizeAttribute 特性,FormsAuthentication和Membership creation/validation
讨论
微软的MVC团队已经对账户controller做了很多的改进。它已经被更新用于Form验证,连同Membership 类去创建新的用户,验证存在的用户,创建cookie去检测用户登入的状态。
在MVC 3中 已经提供了几种默认的应用程序模板。如下图。
• Empty,一个空的模板将创建一些MVC需要的文件结构。
• Internet Application,一个因特网应用程序的默认的模板将要包含一些预配置:basic layout,一个AccountController包含了多个action(注册,登陆,修改密码等)。
• Intranet Application,内部网应用程序。他和第二个模板类似。但是他没有使用Membership 类,而是使用了windows身份验证。
对于大多数网站,我们默认应该使用第二个模板。如果你现在还没有这样做,你可以现在创建一个MVC 3 Internet Application。
这将生成AccountController, AccountModels 和几个Account的Views(用户注册,登陆,修改密码)。
为了组织用户访问特定的view,MVC 提供了一个AuthorizeAttribute 特性。打开AccountController你可以看到如下代码:
1
2
3
4
5
6
7
|
// GET: /Account/ChangePassword [Authorize] public ActionResult ChangePassword() { return View(); } |
它的意图是只有登陆用户才可以访问密码修改页面。
当一个用户访问页面/Account/ChangePassword,如果他没有预先登陆或注册。MVC将自动把请求转到登陆页面。否则MVC将转到转到changePassword页。对于未验证的用户的跳转页面是在web.config里配置的。
1
2
3
|
< authentication mode = "Forms" > < forms loginUrl = "~/Account/LogOn" timeout = "2880" /> </ authentication > |
如果用户从来都没有注册过,那么他可以在登陆页点击注册跳转到注册页面。这个页面包含以下信息:
• Username
• Email Address
• Password
AccountController中的Register action 接收一个RegisterModel 类型的参数。在AccountModels中有一个类型被定义为RegisterModel,它包含了注册页面上元素的变量(username,EmailAddress,Password)。
注册action:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
[HttpPost] public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { // Attempt to register the user MembershipCreateStatus createStatus; Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus); if (createStatus == MembershipCreateStatus.Success) { FormsAuthentication.SetAuthCookie( model.UserName, false /* createPersistentCookie */); return RedirectToAction("Index", "Home"); } else { ModelState.AddModelError("", ErrorCodeToString(createStatus)); } } // If we got this far, something failed, // redisplay form return View(model); |
上边的代码是自动生成的,他们做了三件重要的事。
- 通过Membership.CreateUser()方法创建了一个新的用户。
- 如果创建成功,设置一个cookie给user 确保他可以访问随后的页面。
- 如果创建成功,会跳转到主页(如果创建失败,将会把错误消息显示到指定的view)。
如果你已经安装了完整版本的visual studio和SQL Express。你可以在数据库里看到你创建的user。
方法是:在解决方案管理器里查看AppData下的ASPNETDB.MDF。直接打开就可以。下图是我刚刚创建的一个用户。
默认的数据库连接字符串在webconfig里。
1
2
3
4
|
< connectionStrings > < add name = "ApplicationServices" connectionString="data source=.SQLEXPRESS;Integrated Security = SSPI ;AttachDBFilename=|DataDirectory|aspnetdb.mdf; User Instance = true " providerName = "System.Data.SqlClient" /> </ connectionStrings > |
将来用户再次访问网站的时候,如果FormsAuthentication cookie仍然被保存(假设在登录的时候他选定了记住我选项,或者他没有关闭浏览器),他们就不需要再次登录。
如果cookie没有被保存。他就要被导航到登录页面了。一旦用户输入了登陆信息并且提交表单。AccountController将再通过Membership 类去验证用户。如下:
View Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
[HttpPost] public ActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) { if (Membership.ValidateUser(model.UserName, model.Password)) { FormsAuthentication.SetAuthCookie( model.UserName, model.RememberMe); if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/")) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } else { ModelState.AddModelError("", "The user name or password provided is incorrect."); } } // If we got this far, something failed, // redisplay form return View(model); } |
上边的代码页是自动生成的。做了三件事:
1. 通过 Membership.ValidateUser() 验证用户名和密码
2.如果登陆成功,使用FormsAuthentication.SetAuthCookie 来保存一个cookie。
3如果通过验证,会导航到主页,否则会在登陆页显示错误信息。
AuthorizeAttribute 特性可以进一步限定特定的用户组或者特定的用户才可以访问指定的action
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// Retrieve a list of all users to allow an admin // to manage them [Authorize(Roles = "Admin")] public ActionResult UserAdmin() { MembershipUserCollection users = Membership.GetAllUsers(); return View(users); } // Create some custom reports for me only [Authorize(Users = "Jamie")] public ActionResult JamieAdmin() { // Perform some logic to generate usage reports ... return View(); } |
以上简单的例子仅仅是怎样限制访问内容的开始。