• MVC源码学习之AuthorizeAttribute


    常见的Controller定义方式:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }

    如果对Action的操作需要权限管理的话,需要在Controller或者Action上面添加AuthorizeAttribute属性或者AllowAnonymousAttribute属性。

    AllowAnonymousAttribute的意思很明确,允许匿名访问。

    AuthorizeAttribute类型有几个属性:Roles,Users,TypeId。

    当用户请求一个Action时,会调用OnAuthorization方法。那么,我们来看看这个方法里面的代码是什么:

    public virtual void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }
    
        if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
        {
            throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
        }
    
        bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                                 || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);
    
        if (skipAuthorization)
        {
            return;
        }
    
        if (AuthorizeCore(filterContext.HttpContext))
        {
            HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
            cachePolicy.SetProxyMaxAge(new TimeSpan(0));
            cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
        }
        else
        {
            HandleUnauthorizedRequest(filterContext);
        }
    }

    中间有一段是判断Action或者Controller是否被属性AllowAnonymousAttribute描述,如果有,则返回。也就是说,无论Action或者是Controller哪一个上面被定义了[AllowAnonymous]属性,那么,这个Action就会跳过验证。

    如果没有的话,会判断函数AuthorizeCore(filterContext.HttpContext)执行的结果是否为true,这样,我们进一步看函数AuthorizeCore里面的代码:

    protected virtual bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }
    
        IPrincipal user = httpContext.User;
        if (!user.Identity.IsAuthenticated)
        {
            return false;
        }
    
        if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
        {
            return false;
        }
    
        if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
        {
            return false;
        }
    
        return true;
    }

    逻辑比较清晰,如果用户没有登录,返回false;如果授权用户组长度大于0,但同时不包含当前用户,返回false;如果授权角色组长度大于0,但同时不包含当前用户所在的任何角色,返回false;符合前面所有的条件,则返回true。

    到此,授权验证的上下逻辑算是比较明了。

    问题

    实际操作中出现了疑惑:

    如果在Controller那里定义了角色为"Users,Admin"的授权,而在Action处定义其他角色的Authorize,会怎么样呢?

    测试思路,自定义一个CustomAuthorize,override上述两个函数(这两个函数都是virtual的),代码复制微软的源码,加断点调试。(重写的代码就不展示了。大部分复制,小部分微调下。),控制器代码如下:

    [CustomAuthorize(Roles="Users,Admin")]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    
        [CustomAuthorize(Roles = "AAAAA")]
        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";
    
            return View();
        }
    }

    调试模式启动Home下面的About页面,在OnAuthorization里面加断点,可以看到如下数据:
    1. 在filterContext.ActionDescriptor和filterContext.ActionDescriptor.ControllerDescriptor里面可以看到各自的Roles内容
    2. base.Roles的值为"Users,Admin"。

    由此,我们可以得出:

    当Controller和Action都出现了角色授权时,多个授权不是合并,也不是取Action上面的,而是直接取Controller上面定义的角色,继而判断当前用户是否有授权。

    Users是不是也是这样的,有兴趣的童鞋可以自己调试看看o(∩_∩)o ~

  • 相关阅读:
    Android 架构:Android Jetpack 架构组件的学习和分析
    Android 看源码学 Binder
    Android Okhttp 源码分析(待完成)
    Android Glide 源码分析系列(待完成)
    界面2
    使用spring 4.0 + maven 构建超简单的web项目
    maven中跳过单元测试
    Hibernate+maven+eclipse 实现自动建表
    android开发学习---开发一个简易的短信发送器
    java面试题--实现一个百亿的计算器
  • 原文地址:https://www.cnblogs.com/icyJ/p/MVC_Authorize.html
Copyright © 2020-2023  润新知