[一步一步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
时间: 2024-10-13 20:53:24

[一步一步MVC]第二回:还是ActionFilter,实现对业务逻辑的统一Authorize处理 OnActionExecuting内如何获取参数的相关文章

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

转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csdn不审核,这个也难怪人,但自己比较忙没办法.分享继续,今天谈ASP.NET MVC 6. 我蛮喜欢Ruby On Rails 这种约定胜于配置的框架,在.NET 有ASP.NET MVC 和Java有Play! Framework .  ASP.NET MVC 版本基本上每年一更新,从不让你失望.我

【FunnyBear的Java之旅 - Spring篇】7步初探 Spring MVC

本文将介绍如何搭建一个简单的Spring MVC项目,其中包含依赖注入,视图控制,数据库连接,等关键功能. 在开始阅读前,你需要:MySQL Server, Eclipse EE,Maven Eclipse插件,Spring Eclipse插件,基本的Java Core和Servlet知识. 最终的项目结构如下图所示 1. 新建工程. 这里总的思路是,新建一个普通的Dynamic Web Project,然后把它转换成Maven工程,最后添加对Spring Framework的引用. 1.1 "

C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(二)

前言:上篇介绍了下封装BootstrapHelper的一些基础知识,这篇继续来完善下.参考HtmlHelper的方式,这篇博主先来封装下一些常用的表单组件.关于BootstrapHelper封装的意义何在,上篇评论里面已经讨论得太多,这里也不想过多纠结.总之一句话:凡事有得必有失,就看你怎么去取舍.有兴趣的可以看看,没兴趣的权当博主讲了个“笑话”吧. 本文原创地址:http://www.cnblogs.com/landeanfen/p/5746166.html BootstrapHelper系列

一步一步搭建开发框架(一)依赖抽象编程

1,从一开始学.NET变学了最简单的框架,三层框架,多层框架应用的也比较广泛.自从从事SharePoint开发以后,对.NET方面的知识都渐渐的遗忘了,ASP.NET和ASP.NET MVC也感觉越来越陌生,但一直感觉框架很优美,设计的很合理.晚上无聊的时候还是学一下!本来有一些疑惑的地方,写一写博客,从头到尾缕一遍,也会特别清晰! 2,常见的三层框架Dal层,Bll层,UI层! 3,(1)第一种变化:现在有这么一种情况,Dal层和数据库连接有可能用ORM框架,也有可能使用原生的ADO.NET!

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

一步一步搭框架(asp.netmvc+easyui+sqlserver)-03 我们期望简洁的后台代码,如下: using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using formula; using System.Data; namespace demo.Areas.basic.Controllers { public class

教你一步一步部署.net免费空间OpenShift系列之一------帐号注册和验证

前几天有博友发布了一篇文章<一键部署mono 免费空间支持ASP.NET MVC 再也不担心伙食费换空间了>,支持MVC3和域名绑定,觉得不错,于是自己实践了一下,发现自己实际遇到的问题真不少,而且网上的关于此空间的帖子要么千篇一律,要么语焉不详,现总结为图文教程系列 帐号注册和验证 打开https://www.openshift.com/products/pricing,出现三种选择,前2种是免费的,建议选择第二个. 点击Create one跳转到创建用户界面 看到如下信息,填写邮箱和密码,

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

不知何年何月才能完成OADemo啊,总之还是一步一步来吧,这段时间开始着手了,先做登陆.  前段时间研究了一下在CentOS7下安装Mysql和Memcached服务,并测试了用C#操作,结果还行. 今天做一个简单的基于Bootstarp的响应式登陆页面(其实是在网上下的模板),不管是登陆还是注册吧,都会用到验证码,以前是用GDI绘出来的,觉得太丑了,百度的关于.net的验证码绝大多数也是用的这种方法,最后试了一下captcha,觉得还挺好看的,所以就试着用用. nugit控制台install-

一步一步学习SignalR进行实时通信_5_Hub

原文:一步一步学习SignalR进行实时通信_5_Hub 一步一步学习SignalR进行实时通信\_5_Hub SignalR 一步一步学习SignalR进行实时通信_5_Hub 前言 Hub命名规则 Hub封装好的常用方法 Hub常用方法解释 保持状态 前后台交互 结束语 参考文献 前言 上一讲,我们简单的介绍了下Hub的配置以及实现方法,这一将我希望把更多的细节梳理清楚,不至于让大家在细节上面摸不着头脑,理解深了,那么做项目自然就会相对轻松一些. Hub命名规则 Hub与Persistent

一步一步来

一步一步来. CSS不能编程? 用Less.Sass.Stylus.甚至直接用Absurd,框架除了Bootstrap还有很多. JS写多了很麻烦?jQuery. 移动开发?Zepto.js. 结构不好?找框架,Backbone.js是MVC,AngularJS和Ember.js是MVVM,Twitter还弄了个事件驱动框架Flight. 库多了要优化加载?RequireJS.代码质量成问题?Jasmine.QUnit.Mocha做单元测试. 各种浏览器都要测?用Karma.测试通过了部署还有问