zoukankan      html  css  js  c++  java
  • 艾伟_转载:[一步一步MVC]第二回:还是ActionFilter,实现对业务逻辑的统一Authorize处理 狼人:

    本系列文章导航

    [一步一步MVC]第一回:使用ActionSelector控制Action的选择

    [一步一步MVC]第二回:还是ActionFilter,实现对业务逻辑的统一Authorize处理

    [一步一步MVC]第三回:MVC范例大观园

    [一步一步MVC]第四回:漫谈ActionLink,有时“胡搅蛮缠”

    [一步一步MVC]第五回:让TagBuilder丰富你的HtmlHelper

    [一步一步MVC]第六回:什么是MVC(上)?

    由问题引出

    在ASP .NET MVC中,以友好的URL访问资源是MVC吸引眼球的特色之一,但是随之而来对于Authorize问题的处理变得令人令人头痛。例如假设我们有一个获取Book信息的Action,定义在BookController中:

    public 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
  • 相关阅读:
    234树(2)实现
    矩阵的坐标变换(转)
    OpenGL 学习笔记(3)绘制几何物体
    OpenGL 学习笔记(1)初始化窗体
    OpenGL学习笔记(5)3D基本概念
    矩阵的三维变换(转)
    OpenGL 学习笔记(2)创建第一个图形
    234树(1)概念
    OpenGL 学习笔记(4)顶点数组
    OpenGL学习笔记(6)第一个动画
  • 原文地址:https://www.cnblogs.com/waw/p/2157027.html
Copyright © 2011-2022 走看看