[一步一步MVC]第二回:还是ActionFilter,实现对业务逻辑的统一Authorize处理 OnActionExecuting内如何获取参数
如何获取参数:http://www.cnblogs.com/anytao/archive/2009/04/23/anytao-mvc-02-actionauthorize.html
由问题引出
在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调用之前对此已经进行了处理。
思考的瞬间
那么,统一的处理该如何着手实现更优雅的、更统一的Authorize处理呢?显然MVC自带的Authorize特性,为我们提供了可选择的思路:
[Authorize(Users = "Anytao")]
public ActionResult Edit(int id)
{
return View();
}
Authorize标记通过对于Users或者Roles的定义,来对Edit Action的执行进行“预”Authorize授权,那么登陆用户为Anytao的用户才有权对BookController Edit进行访问,否则将无权访问。显然,这种方式对于满足我们
- 统一Authorize处理
- 在Action调用之前进行授权验证
的目标是统一的。所以,我们可以借助这种方式实现自定义的统一Authorize处理方案。
统一Authorize解决方案
有了指导方针,我们就可以有的放肆了,我们的方案同样是应用ActionFilter实现对Authorize处理,上次的范例是{[一步一步MVC]第四回:使用ActionSelector控制Action的选择}。显然我们可以在OnActionExecuting事件中对Action进行“预”处理,将关于Authorize的验证过程统一在OnActionExecuting中进行,就可以对标记的Action实现调用之前的过滤了,所以我们首先实现一个AuthorizeAttributeBase,例如:
// Release : code03, 2009/04/22
// Author : Anytao, http://www.anytao.com
public abstract class AuthorizeAttributeBase : ActionFilterAttribute
{
public AuthorizeAttributeBase()
{
} public AuthorizeAttributeBase(string key)
{
Key = key;
} public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Authorize handler
} public string Key { get; set; } protected abstract bool IsAuthorized(int id);
}
而具体的验证则在具体实现类中,例如我们对Book的验证:
// Release : code04, 2009/04/22
// Author : Anytao, http://www.anytao.com
public class BookAuthorizeAttribute : AuthorizeAttributeBase
{
protected override bool IsAuthorized(int id)
{
return (new IAuthorizeService()).IsBookAuthorized(id);
}
}
对于验证的处理必须解决两方面的问题:
- 在AuthorizeAttributeBase中获取待过滤Action中的参数(Index(int id)),一般而言我们需要对id进行验证,那么传入id的值该如何处理。
- 在AuthorizeAttributeBase对于非法用户的处理,一般而言就是导航到NotValid页面。
在OnActionExecuting中获取Action参数
我们采用的方法是通过filterContext的ActionParameters来获取参数值,通过参数的Key来获取其值,例如:
if (filterContext.ActionParameters.ContainsKey(key))
{
value = int.Parse(filterContext.ActionParameters[key].ToString());
}
在OnActionExecuting中导航到不同的View
这也是一个简单的处理,我们只要指定好filterContext的Result为指定的ViewResult即可实现我们的目标:
filterContext.Result = new ViewResult{
ViewName = "NotValid"
};
解决了上述问题,就基本实现了对Authorize进行统一处理的目标,至于具体的Authorize逻辑,不同的业务可以在不同的业务层进行封装。例如对于Book资源的处理可以统一在IBookService中,对于User资源的处理可以统一在IUserService中(不过显然我们已经有了MVC自带的Authorize,不必重复),对于其他的资源也相应的处理在不同的业务层中。
下面是AuthorizeAttributeBase和BookAuthorizeAttribute的完整代码:
// Release : code03, 2009/04/22
// Author : Anytao, http://www.anytao.com
public abstract class AuthorizeAttributeBase : ActionFilterAttribute
{
public AuthorizeAttributeBase()
{
} public AuthorizeAttributeBase(string key)
{
Key = key;
} public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string key = string.IsNullOrEmpty(Key) ? "id" : Key; int id; if (filterContext.ActionParameters.ContainsKey(key))
{
if (!int.TryParse(filterContext.ActionParameters[key].ToString(), out id))
{
id = 0;
}
}
else
{
id = 0;
} if (id > 0)
{
if (IsAuthorized(id))
{
base.OnActionExecuting(filterContext);
}
else
{
filterContext.Result = new ViewResult{
ViewName = "NotValid"
}; }
}
else
{
filterContext.Result = new ViewResult{
ViewName = "NotValid"
};
}
} public string Key { get; set; } protected abstract bool IsAuthorized(int id);
}
接下来就是如何应用了。
在Controller中应用统一Authorize处理
下面是我们的应用,还是对于http://anytao.net/Book/index/1的访问,我们可以像下面这样应用:
// Release : code05, 2009/04/22
// Author : Anytao, http://www.anytao.com
[BookAuthorize(Key="id")]
public ActionResult Index(int id)
{
Book model = (new IBookService()).GetBook(id); return View(model);
}
对比前后的两种方案,我想孰优孰劣显而易见。BookAuthorize显然以更优雅的方式实现了对于Authorize这回事儿的处理,也基本达到了原来的目标。我们的验证逻辑没有散落在系统四处,如何同时需要对Book的Index进行多个逻辑的验证,我们的方式也变得很简单,例如:
// Release : code05, 2009/04/22
// Author : Anytao, http://www.anytao.com
[BookAuthorize(Key="id"), TaskAuthorize(Key="id")]
public ActionResult Index(int id)
{
Book model = (new IBookService()).GetBook(id); return View(model);
}
不过,我们需要对id的复用进行一点思考,不过那已经是另外一回儿事儿了。对本文而言,我已经达到了目标。当然,这也许不是最好的方案,所以我期待您的更好方案,因为技术需要切磋和共享。
又是一个小技巧,希望给你帮助。
代码下载[anytao_mvc_actionauthorize],更多关注,尽在anytao.net/blog
[一步一步MVC]第二回:还是ActionFilter,实现对业务逻辑的统一Authorize处理 OnActionExecuting内如何获取参数的更多相关文章
- (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性
转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...
- ASP.NET MVC 3 Model【通过一简单实例一步一步的介绍】
今天主要讲Model的两个方面: 1. ASP.Net MVC 3 Model 简介 通过一简单的事例一步一步的介绍 2. ASP.Net MVC 3 Model 的一些验证 MVC 中 Model ...
- C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)
前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...
- C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper
前言:之前学习过很多的Bootstrap组件,博主就在脑海里构思:是否可以封装一套自己Bootstrap组件库呢.再加上看到MVC的Razor语法里面直接通过后台方法输出前端控件的方式,于是打算仿照H ...
- 一步一步搭框架(asp.netmvc+easyui+sqlserver)-03
一步一步搭框架(asp.netmvc+easyui+sqlserver)-03 我们期望简洁的后台代码,如下: using System; using System.Collections.Gener ...
- 一步一步实现MVC5+EF6+Bootstarp+Autofac+NoSql实现OADemo 之登陆(一) 验证码 Captcha 之大插件小用
不知何年何月才能完成OADemo啊,总之还是一步一步来吧,这段时间开始着手了,先做登陆. 前段时间研究了一下在CentOS7下安装Mysql和Memcached服务,并测试了用C#操作,结果还行. ...
- 一步一步学习SignalR进行实时通信_1_简单介绍
一步一步学习SignalR进行实时通信\_1_简单介绍 SignalR 一步一步学习SignalR进行实时通信_1_简单介绍 前言 SignalR介绍 支持的平台 相关说明 OWIN 结束语 参考文献 ...
- 简单实例一步一步帮你搞清楚MVC3中的路由以及区域
我们都知道MVC 3 程序的所有请求都是先经过路由解析然后分配到特定的Controller 以及 Action 中的,为什么这些知识讲完了Controller Action Model 后再讲呢?这个 ...
- 一步一步自定义SpringMVC参数解析器
随心所欲,自定义参数解析器绑定数据. 题图:from Zoommy 干货 SpringMVC解析器用于解析request请求参数并绑定数据到Controller的入参上. 自定义一个参数解析器需要实现 ...
随机推荐
- c#提出中文首字母
; i < len; i++) { myStr += getSpell(strText.Substring(i, )); ...
- 再造轮子之网易彩票-第二季(IOS 篇 by sixleaves)
02-彩票项目第二季 2.封装SWPTabBar方式一 接着我们思考如何进行封装.前面已经将过了为什么要封装, 和封装达到的效果.这里我们主要有两种封装方式,分别是站在不同的角度上看待问题.虽然角度不 ...
- setOpaque(true);设置控件不透明
setOpaque(true);设置控件不透明setOpaque(false);设置控件透明
- Linux 通过HTTP进行域名更新
一.3322动态域名更新接口 接口地址 API URL http://members.3322.net/dyndns/update HTTP请求 GET /dyndns/update?hostname ...
- poj 2096 Collecting Bugs(期望 dp 概率 推导 分类讨论)
Description Ivan is fond of collecting. Unlike other people who collect post stamps, coins or other ...
- Android开发中用到的框架、库介绍
Android开发中用到的框架介绍,主要记录一些比较生僻的不常用的框架,不断更新中...... 网路资源:http://www.kuqin.com/shuoit/20140907/341967.htm ...
- android——屏幕适配大全(转载)
http://my.oschina.net/u/2008084/blog/496161 一.适配可行性 早在Android设计之初就考虑到了这一点,为了让app适应标准or山寨屏幕,google已经有 ...
- jQuery中的trigger和triggerhandler区别
$("form :input").blur(function(){ // }).keyup(function(){ $(this).triggerHandler("blu ...
- js简单排序
简单的排序功能 HTML代码: <body> <input id="btn1" type="button" value="排序&qu ...
- SQL Server -SET QUOTED_IDENTIFIER
SET QUOTED_IDENTIFIER ON SQL SERVER的联机丛书的解释: “当 SET QUOTED_IDENTIFIER 为 ON 时,标识符可以由双引号分隔,而文字必须由单引号分隔 ...