现在NerdDinner范例程序可以让访问网站的任何人创建和编辑任何Dinner的信息。下面我们改变这些,仅仅注册和登录的用户才允许创建新的Dinner,并且增加限制,仅仅Dinner的主持人才允许编辑Dinner的详细信息。
为了实现上述功能,我们将使用认证和授权来保护应用程序。
理解认证和授权
认证是识别和验证访问应用程序的客户,简而言之,就是识别访问网站的终端用户是谁。
ASP.NET 支持多种方式来认证浏览器用户。对Internet应用程序而言,最通用的认证方法是Forms Authentication。Form Authentication允许开发人员创建一个HTML登录表单,连接数据库或其它密码表,验证用户提交的用户名/密码。如果用户名/密码是正确的, 开发人员接着让ASP.NET生成一个加密的HTTP cookie,识别用户随后的请求。我们将在NerdDinner范例程序中使用Form Authentication。
授权是判断是否一个验证的用户有权限访问一个特定的URL或资源,执行一些操作。例如,在NerdDinner范例程序中,我们将授权仅仅登录 的用户可以访问/Dinners/Create URL网址,并创建一个新的Dinner对象。我们也授权仅仅Dinner的主持人可以编辑该条记录,并拒绝其他人修改。
Forms Authentication和AccountController
在创建ASP.NET MVC应用程序时,ASP.NET MVC的默认Visual Studio 项目模板自动实现了Forms Authentication,也自动添加一个预先创建的账户登录实现,使得站点集成安全验证更加容易。
当未登录的用户访问NerdDinner范例程序时,默认的Site.Master Master 页面在右上角显示一个Log On(登录)链接。点击Log On 链接,引导用户到/Account/LogOn URL。
![](http://blog.entlib.com/userfiles/entlibforum/image/MVC3/MVC_NerdDinner_22_1.gif)
没有注册的访问者可点击Register注册链接,进入/Account/Register 地址,允许输入帐号的信息:
![](http://blog.entlib.com/userfiles/entlibforum/image/MVC3/MVC_NerdDinner_22_2.gif)
点击Register按钮在ASP.NET Membership 系统创建一个新的用户,并使用Forms Authentication认证用户。
当用户登录后,Site.Master更改右上角的输出为Welcome [username]!,以及一个Log Off的链接,一旦点击Log Off链接将退出登录。
上述的登录、退出和注册功能都在AccountController类中实现,该类是在创建ASP.NET MVC项目时自动创建的,相应的用户界面通过视图模板实现的,存放在\Views\Account目录:
![](http://blog.entlib.com/userfiles/entlibforum/image/MVC3/MVC_NerdDinner_22_3.gif)
AccountController类使用ASP.NET Forms Authentication系统生成加密的认证cookies,和ASP.NET Membership API来存放和验证用户名/密码。ASP.NET Membership API是可扩展的,并实现任何使用任何密码库。ASP.NET 内置的membership provider实现了将用户名/密码存放在SQL数据库,或者活动目录(Active Directory)中。
我们可以配置NerdDinner范例程序中的membership provider,打开根目录的web.config 配置文件,查找<membership>节点。当项目创建时,默认的web.config已经添加了,并注册了SQL Membership Provider,并配置了使用ApplicationServices连接字符串来指定数据库。
默认的ApplicationServices连接字符串(web.config配置文件中 的<connectionStrings>节点)配置使用SQL Express,指向ASPNETDB.MDF SQL Express数据库,该DB文件存放在App_Data目录。如果在Membership API第一次使用时,该数据库文件不存在,ASP.NET 将自动创建数据库,并准备合适的数据库schema。
![](http://blog.entlib.com/userfiles/entlibforum/image/MVC3/MVC_NerdDinner_22_4.gif)
如果不想使用SQL Express,而想使用SQL Server实例(或连接一个远程数据库),我们需要做的只是更新web.config 配置文件中的ApplicationServices连接字符串,并确保合适的membership schema在该数据库中已经正确地创建了。你可以在如下目录:
\Windows\Microsoft.NET\Framework\v2.0.50727\
运行 aspnet_regsql.exe 工具,添加合适的membership schema,以及其他ASP.NET 应用程序服务到数据库中。
使用[Authorize]过滤器对/Dinners/Create授权
对NerdDinner范例程序,我们不必写任何代码来实现安全认证和帐号管理实现。用户可以注册新帐号,登录/退出网站。现在,我们添加授权逻辑到范例应用程序中,并使用认证状态和访问者的用户名,来控制他们在站点中能做什么,和不能做什么。
首先,我们添加授权逻辑到DinnersController类的Create Action方法上。我们要求访问/Dinners/Create URL网址的用户必须登录,如果他们没有登录,将重定向到登录页面,以便他们可以登录。
实现这一逻辑非常简单,我们需要做的是添加 [Authorize] 过滤器属性到Create Action 方法上,如下所示:
//
// GET: /Dinners/Create
[Authorize]
public ActionResult Create() {
...
}
//
// POST: /Dinners/Create
[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinnerToCreate) {
...
}
ASP.NET MVC 支持创建action过滤器,实现可重用的逻辑应用到action方法上。[Authorize] 过滤器是ASP.NET MVC内置的action过滤器,开发人员可以定义授权规则应用到action方法和Controller 类上。
如果[Authorize] 没有带任何参数,则强制要求用户请求action方法时必须登录,否则重定向到登录页面。当重定向时,原来的请求URL会作为querystring参数 跟在URL后面(如,/Account/LogOn?ReturnUrl=%2fDinners%2fCreate),在用户登录完成 后,AccountController再次重定向用户返回开始请求的页面。
[Authorize] 过滤器也支持指定Users或Roles属性,该属性要求用户必须登录,此外还要求用户必须在允许的用户列表中或允许的角色成员。例如,如下代码仅允许2 个指定的用户,”EntLib.com”和”EntLibForum” 访问/Dinners/Create 路径:
[Authorize(Users="EntLib.com,EntLibForum")]
public ActionResult Create() {
...
}
将特定的用户名直接写在代码中不易于将来的代码维护,更好的办法是定义一个roles(角色),然后通过数据库或者活动目录(Active Directory)映射用户到角色中。ASP.NET 提供了一个内置的角色管理API和一组内置的Role provider(包括SQL 和活动目录),帮助实现用户/角色的映射。我们接着更新代码,仅允许admin角色的用户访问/Dinners/Create URL。
[Authorize(Roles="admin")]
public ActionResult Create() {
...
}
创建Dinners时,使用User.Identity.Name 属性
在查询当前登录的用户名时,我们可以使用Controller基类公开的User.Identity.Name属性。
之前,我们实现HTTP-POST 的Create() action 方法时,Dinner的HostedBy属性是让用户在页面手动输入的。这里,我们可以更新代码,使用User.Identity.Name属性给 HostedBy赋值,同时自动为支持人增加一条RSVP记录:
//
// POST: /Dinners/Create
[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
try
{
dinner.HostedBy = User.Identity.Name;
RSVP rsvp = new RSVP();
rsvp.AttendeeName = User.Identity.Name;
dinner.RSVPs.Add(rsvp);
dinnerRepository.Add(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
catch
{
ModelState.AddRuleViolations(dinner.GetRuleViolations());
// ViewData["countries"] = new SelectList(PhoneValidator.Countries, dinner.Country);
}
}
// return View(dinner);
// 使用ViewModel
return View(new DinnerFormViewModel(dinner));
}
因为我们给Create() 方法添加了[Authorize] 属性,ASP.NET MVC将确保仅登录的用户才允许访问/Dinners/Create 地址,并执行该方法。同时,User.Identity.Name属性将一定有一个有效的用户名。