如何获取参数: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内如何获取参数的更多相关文章

  1. (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性

    转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...

  2. ASP.NET MVC 3 Model【通过一简单实例一步一步的介绍】

    今天主要讲Model的两个方面: 1. ASP.Net MVC 3 Model 简介 通过一简单的事例一步一步的介绍 2. ASP.Net MVC 3 Model 的一些验证 MVC 中 Model ...

  3. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

  4. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper

    前言:之前学习过很多的Bootstrap组件,博主就在脑海里构思:是否可以封装一套自己Bootstrap组件库呢.再加上看到MVC的Razor语法里面直接通过后台方法输出前端控件的方式,于是打算仿照H ...

  5. 一步一步搭框架(asp.netmvc+easyui+sqlserver)-03

    一步一步搭框架(asp.netmvc+easyui+sqlserver)-03 我们期望简洁的后台代码,如下: using System; using System.Collections.Gener ...

  6. 一步一步实现MVC5+EF6+Bootstarp+Autofac+NoSql实现OADemo 之登陆(一) 验证码 Captcha 之大插件小用

    不知何年何月才能完成OADemo啊,总之还是一步一步来吧,这段时间开始着手了,先做登陆.  前段时间研究了一下在CentOS7下安装Mysql和Memcached服务,并测试了用C#操作,结果还行. ...

  7. 一步一步学习SignalR进行实时通信_1_简单介绍

    一步一步学习SignalR进行实时通信\_1_简单介绍 SignalR 一步一步学习SignalR进行实时通信_1_简单介绍 前言 SignalR介绍 支持的平台 相关说明 OWIN 结束语 参考文献 ...

  8. 简单实例一步一步帮你搞清楚MVC3中的路由以及区域

    我们都知道MVC 3 程序的所有请求都是先经过路由解析然后分配到特定的Controller 以及 Action 中的,为什么这些知识讲完了Controller Action Model 后再讲呢?这个 ...

  9. 一步一步自定义SpringMVC参数解析器

    随心所欲,自定义参数解析器绑定数据. 题图:from Zoommy 干货 SpringMVC解析器用于解析request请求参数并绑定数据到Controller的入参上. 自定义一个参数解析器需要实现 ...

随机推荐

  1. c#提出中文首字母

           ; i < len; i++)             {                 myStr += getSpell(strText.Substring(i, ));   ...

  2. 再造轮子之网易彩票-第二季(IOS 篇 by sixleaves)

    02-彩票项目第二季 2.封装SWPTabBar方式一 接着我们思考如何进行封装.前面已经将过了为什么要封装, 和封装达到的效果.这里我们主要有两种封装方式,分别是站在不同的角度上看待问题.虽然角度不 ...

  3. setOpaque(true);设置控件不透明

    setOpaque(true);设置控件不透明setOpaque(false);设置控件透明

  4. Linux 通过HTTP进行域名更新

    一.3322动态域名更新接口 接口地址 API URL http://members.3322.net/dyndns/update HTTP请求 GET /dyndns/update?hostname ...

  5. poj 2096 Collecting Bugs(期望 dp 概率 推导 分类讨论)

    Description Ivan is fond of collecting. Unlike other people who collect post stamps, coins or other ...

  6. Android开发中用到的框架、库介绍

    Android开发中用到的框架介绍,主要记录一些比较生僻的不常用的框架,不断更新中...... 网路资源:http://www.kuqin.com/shuoit/20140907/341967.htm ...

  7. android——屏幕适配大全(转载)

    http://my.oschina.net/u/2008084/blog/496161 一.适配可行性 早在Android设计之初就考虑到了这一点,为了让app适应标准or山寨屏幕,google已经有 ...

  8. jQuery中的trigger和triggerhandler区别

    $("form :input").blur(function(){ // }).keyup(function(){ $(this).triggerHandler("blu ...

  9. js简单排序

    简单的排序功能 HTML代码: <body> <input id="btn1" type="button" value="排序&qu ...

  10. SQL Server -SET QUOTED_IDENTIFIER

    SET QUOTED_IDENTIFIER ON SQL SERVER的联机丛书的解释: “当 SET QUOTED_IDENTIFIER 为 ON 时,标识符可以由双引号分隔,而文字必须由单引号分隔 ...