本系列将分析ASP.NET Core运行原理

在认证阶段通过用户令牌获取到用户的Claims,而授权就是对这些Claims的验证。

目录

  1. 授权核心

    1. AuthorizationOptions
    2. AuthorizationPolicy
    3. AuthorizationPolicyBuilder
  2. 执行授权
    1. AuthorizeFilter
    2. IPolicyEvaluator
    3. IAuthorizationService
  3. 总结

授权核心

services.AddAuthorization(opt => opt.AddPolicy("isAdmin", builder => builder.RequireUserName("admin")));

通过上面的代码,可以添加一个isAdmin的授权。

对于第一个参数opt:

public class AuthorizationOptions
{
private IDictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(); public void AddPolicy(string name, AuthorizationPolicy policy)
{
PolicyMap[name] = policy;
} public void AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy)
{
var policyBuilder = new AuthorizationPolicyBuilder();
configurePolicy(policyBuilder);
AddPolicy(name,policyBuilder.Build());
} public AuthorizationPolicy GetPolicy(string name)
{
return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null;
}
}

实际上,AuthorizationOptions相当于AuthorizationPolicy的集合

AuthorizationPolicy则是一个具体的授权策略对象

public class AuthorizationPolicy
{
public IReadOnlyList<IAuthorizationRequirement> Requirements { get; }
public IReadOnlyList<string> AuthenticationSchemes { get; }
}

AuthorizationPolicyBuilder通过Build方法可以构建一个AuthorizationPolicy,其内部有很多常用的添加IAuthorizationRequirement的方法:

public AuthorizationPolicy Build()
{
return new AuthorizationPolicy(this.Requirements, this.AuthenticationSchemes);
}
public AuthorizationPolicyBuilder RequireUserName(string userName)
{
this.Requirements.Add(new NameAuthorizationRequirement(userName));
} ... Require() ...

IAuthorizationRequirement是授权策略AuthorizationPolicy的一个授权条件,策略下的所有授权条件满足,则授权成功。

public interface IAuthorizationRequirement
{
} public class NameAuthorizationRequirement : IAuthorizationRequirement
{
public string RequiredName { get; }
}

IAuthorizationHandler是授权条件IAuthorizationRequirement的具体处理器,授权条件下的任意1个处理器授权成功,则授权成功。(默认情况下:AuthorizationOptions的InvokeHandlersAfterFailure = true)

public interface IAuthorizationHandler
{
Task HandleAsync(AuthorizationHandlerContext context);
} public abstract class AuthorizationHandler<TRequirement> : IAuthorizationHandler where TRequirement : IAuthorizationRequirement
{
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (TRequirement requirement in context.Requirements)
await HandleRequirementAsync(context, requirement);
} protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement);
} public class NameAuthorizationRequirement : AuthorizationHandler<NameAuthorizationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, NameAuthorizationRequirement requirement)
{
if (context.User?.Identities.Any(identity => identity.Name == requirement.RequiredName))
context.Succeed((IAuthorizationRequirement) requirement);
return Task.CompletedTask;
}
}

授权的最终实现代码在IAuthorizationHandler

执行授权

解释了授权策略的原理,再谈谈授权策略的触发。通常我们在MVC中使用授权功能,而触发授权也是在注册MVC代码中,一并注册了。

public static IMvcBuilder AddMvc(this IServiceCollection services)
{
IMvcCoreBuilder builder = services.AddMvcCore();
builder.AddAuthorization();
} internal static void AddAuthorizationServices(IServiceCollection services)
{
services.AddAuthenticationCore();
services.AddAuthorization();
services.AddAuthorizationPolicyEvaluator();
services.TryAddEnumerable(ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
}

在MVC中,ApplicationModel用来描述MVC中的模型,而IApplicationModelProvider则是初始化MVC的模型:

public class ApplicationModel
{
public IList<ControllerModel> Controllers { get; } public IList<IFilterMetadata> Filters { get; }
}
public interface IApplicationModelProvider
{
int Order { get; }
void OnProvidersExecuting(ApplicationModelProviderContext context);
void OnProvidersExecuted(ApplicationModelProviderContext context);
}

其中AuthorizationApplicationModelProvider会初始化ApplicationModel的授权部分,注册到Filters属性上(AuthorizeFilter 和 AllowAnonymousFilter)。

public interface IAsyncAuthorizationFilter : IFilterMetadata
{
Task OnAuthorizationAsync(AuthorizationFilterContext context);
} public class AuthorizeFilter : IAsyncAuthorizationFilter
{
public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var policyEvaluator = GetRequiredService<IPolicyEvaluator>();
var authenticationResult = await policyEvaluator.AuthenticateAsync(effectivePolicy, context.HttpContext);
var authorizationResult = await policyEvaluator.AuthorizeAsync(effectivePolicy, authenticationResult, context.HttpContext, context);
if (authorizationResult.Challenged)
{
context.Result = (IActionResult) new ChallengeResult((IList<string>) effectivePolicy.AuthenticationSchemes.ToArray<string>());
}
else if (authorizationResult.Forbidden)
{
context.Result = (IActionResult) new ForbidResult((IList<string>) effectivePolicy.AuthenticationSchemes.ToArray<string>());
}
}
}

AuthorizeFilter的OnAuthorizationAsync方法会在Action执行前触发,内部调用IPolicyEvaluator执行

public interface IPolicyEvaluator
{
Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context);
Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource);
} public class PolicyEvaluator : IPolicyEvaluator
{
private readonly IAuthorizationService _authorization;
public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
{
}
public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
{
var result = await _authorization.AuthorizeAsync(context.User, resource, policy);
if (result.Succeeded) return PolicyAuthorizationResult.Success();
return (authenticationResult.Succeeded) ? PolicyAuthorizationResult.Forbid() : PolicyAuthorizationResult.Challenge();
}
}

在AuthenticateAsync方法中,将合并policy的所有scheme认证结果。

在AuthorizeAsync方法中,将调用IAuthorizationService来实现授权。

public interface IAuthorizationService
{
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
} public class DefaultAuthorizationService : IAuthorizationService
{
public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName)
{
var policy = await _policyProvider.GetPolicyAsync(policyName);
return await this.AuthorizeAsync(user, resource, policy);
} public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
var authContext = _contextFactory.CreateContext(requirements, user, resource);
var handlers = await _handlers.GetHandlersAsync(authContext);
foreach (var handler in handlers)
{
await handler.HandleAsync(authContext);
if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed)
break;
}
return _evaluator.Evaluate(authContext);
}
}

在IAuthorizationService类中,将调用policy的所有Requirement的Handle处理

总结

授权核心:AuthorizationOptionsAuthorizationPolicyAuthorizationPolicyBuilder

AuthorizationOptions 用于保存 AuthorizationPolicy

AuthorizationPolicyBuilder 用于创建 AuthorizationPolicy

AuthorizationPolicy 包含 IAuthorizationRequirement 和 AuthenticationSchemes

IAuthorizationRequirement 包含授权逻辑 IAuthorizationHandler

执行授权:AuthorizeFilterIPolicyEvaluatorIAuthorizationService

AuthorizeFilter的OnAuthorizationAsync方法会在Action执行前触发,内部调用IPolicyEvaluator执行

IPolicyEvaluator 先根据 Schemes 获取Claims,然后调用 IAuthorizationService 的授权方法

IAuthorizationService 调用 Requirement 对应的Handle授权逻辑

个人觉得源码的一个待优化的地方:在DefaultAuthorizationHandlerProviderGetHandlersAsync方法按需返回IAuthorizationHandler更合适。

本文链接:http://www.cnblogs.com/neverc/p/8204339.html

【ASP.NET Core】运行原理(4):授权的更多相关文章

  1. ASP.NET Core 运行原理解剖[5]:Authentication

    在现代应用程序中,认证已不再是简单的将用户凭证保存在浏览器中,而要适应多种场景,如App,WebAPI,第三方登录等等.在 ASP.NET 4.x 时代的Windows认证和Forms认证已无法满足现 ...

  2. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...

  3. ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行

    ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行 核心框架 ASP.NET Core APP 创建与运行 总结 之前两篇文章简析.NET Core 以及与 .NET Framew ...

  4. ASP.NET Core 运行原理解剖[1]:Hosting

    ASP.NET Core 是新一代的 ASP.NET,第一次出现时代号为 ASP.NET vNext,后来命名为ASP.NET 5,随着它的完善与成熟,最终命名为 ASP.NET Core,表明它不是 ...

  5. ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

    在上一章中,我们介绍了 ASP.NET Core 的启动过程,主要是对 WebHost 源码的探索.而本文则是对上文的一个补充,更加偏向于实战,详细的介绍一下我们在实际开发中需要对 Hosting 做 ...

  6. ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

    在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展.通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监 ...

  7. ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界

    HttpContext是ASP.NET中的核心对象,每一个请求都会创建一个对应的HttpContext对象,我们的应用程序便是通过HttpContext对象来获取请求信息,最终生成响应,写回到Http ...

  8. ASP.NET Core 运行原理剖析

    1. ASP.NET Core 运行原理剖析 1.1. 概述 1.2. 文件配置 1.2.1. Starup文件配置 Configure ConfigureServices 1.2.2. appset ...

  9. ASP.NET Core 运行原理剖析 (转载)

    1.1. 概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管.以初始化过程中触发HttpAppl ...

随机推荐

  1. Windos系统git提交

    一.$ git status   //查看当前项目下所有文的状态,如果第一次,你会发现都红颜色的,因为它还没有交给git/github管理. 二.$ git add .   //(.)点表示当前目录下 ...

  2. oracle中 常用的 join on 相关和 集合运算的总结

    sql常用联合查询的 join on . left join(左连接) . right join (右连接).inner join (等值连接)以及常用的集合运算有:union.unionall.mi ...

  3. gis电子地图开发公司面临的挑战和机遇

    从上个世纪90年代开始电子地图应用就已经收到人们的关注,但是由于时代的局限性和市场经济发展的不成熟.地理信息系统系统的应用并没有得到很好的利用.只有少数的国家机构和军事系统才能够使用这些应用.随着技术 ...

  4. 在Github上面搭建一个自己域名的Hexo博客

    前言 在一次看到别人的博客主页,觉得设计很漂亮.但是由于自己对于前台这块没什么办法,煞是羡慕.偶然中发现这种样式是在Github上面搭建的,使用的是Next主题.于是便想自己也搭建一个,于是便去就去查 ...

  5. Python--Pycharm backup_ver1.py 控制台一直Backup FAILED

    1.windows不自带zip,需自行安装,http://gnuwin32.sourceforge.net/packages/zip.htm 2.安装后,要配置环境变量:PATH 3.简明Python ...

  6. 自学OpenCV时遇到的一些错误(捂脸ing,当年确实好多不懂...)

    6.8/14 opencv环境配置好了, 属性也设置对了, 但是使用VideoCapture类的时候, 还是出错.  右键可以查看其定义, 但是就是 出错.   漏掉了#include "s ...

  7. Backtrack无线攻防(很任性的一篇)

    首先你得有一个backtrack操作系统 然后还得花钱买一个无线网卡,最好是Intel的,还有要是USB接口的,可能是因为其他接口我不会接.

  8. quick-cocos2d-x教程1:在window上创建第一个项目文件夹,并制作helloworld

    说明:此教程是针对cocos2dx 2.0系列的,3.0的版本号,如今还没有公布出来. 1)首先从github.com把这个项目下载到本地.然后装到d盘的根文件夹,并设置文件夹路径为d:\quick- ...

  9. eclipse 鲜为人知的调试技巧,你用过多少

    今天在OSChina上看到了篇调试技巧,的确对于调试非常有帮助,而且大部分我们都没实用过,我们常常使用的调试是F5678四个键,假设你还想提高调试效率你能够尝试着用一用,写过代码做过项目的人都知道调试 ...

  10. POJ 2533 Longest Ordered Subsequence(DP 最长上升子序列)

    Longest Ordered Subsequence Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 38980   Acc ...