由问题引出
在ASP .NET MVC中,以友好的URL访问资源是MVC吸引眼球的特色之一,但是随之而来对于Authorize问题的处理变得令人令人头痛。例如假设我们有一个获取Book信息的Action,定义在BookController中:
public class BookController : Controller
{
// Release : code01, 2009/04/22
// Author : Anytao, http://www.anytao.com
public ActionResult Index(int id)
{
Book model = (new IBookService()).GetBook(id);
return View(model);
}
}
那么,我们可以通过http://anytao.net/Book/index/1,来访问id为1的Book(例如该书是《你必须知道的.NET》,哈哈,广告嫌疑)。在没有任何特别处理的情况下,对于该书的访问是“不设防”的。任何用户可以通过http://anytao.net/Book/index/1实现对《你必须知道的.NET》信息的访问。那么访问的资源如果是http://anytao.net/Secret/index/1,显然我的秘密无一例外的对外公开了。
言之此处,我们的问题已经明白无疑,那么应该如何处理呢?我们可以很容易的想到通过以下的方式进行处理:
// Release : code02, 2009/04/22
// Author : Anytao, http://www.anytao.com
public ActionResult Index(int id)
{
if (new IAuthorizeService().IsBookAuthorized(id, User.Identity.Name))
{
Book model = (new IBookService()).GetBook(id);
return View(model);
}
else
{
return View("NotValid");
}
}
显然,我通过IsBookAuthorized对GetBook服务的访问有效性进行控制,通过User的Name在数据库或者其他资源存储进行查找, 然后根据IsBookAuthorized结果进行是否访问的控制,显然不合法的用户将被导航到NotValid页,提示你是非法用户。
这种方式显然是最容易想到的办法,而且也广泛存在于我们实际的应用中,例如NerdDinner范例中也是通过这种方式进行Authorize控制处理的,例如:
[Authorize]
public ActionResult Edit(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (!dinner.IsHostedBy(User.Identity.Name))
return View("InvalidOwner");
return View(new DinnerFormViewModel(dinner));
}
然而这种方式存在或多或少的问题,例如:
IsBookAuthorized将分散于不同的Action或者BLL层中,对于统一的Authorized管理带来问题。
实际的Authorized执行已经渗透到Action或者Serivce内部,我们更期待在Action调用之前对此已经进行了处理。