本系列将分析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. gitup问题的处理

    在使用本地建立git仓库后,准备使用 因为之前已经登录了,所以没有给登录的命令行 1.git init         命令初始化仓库 2.git add .     命令将所有的文件传输到git仓库 ...

  2. 第四届河南省ACM 节能 区间DP

    1001: 节 能 时间限制: 1 Sec  内存限制: 128 MB 提交: 21  解决: 9 [提交][状态][讨论版] 题目描述 Dr.Kong设计的机器人卡多越来越聪明.最近市政公司交给卡多 ...

  3. 基于TCP协议的socket编程

    什么是socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面, ...

  4. 安卓开发之mqtt协议

    首先物联网协议mqtt协议是基于tcp/ip协议的,使用了官方的mqttclient框架/**初始化mqttclient*/private void init() { try { //MQTT的连接设 ...

  5. 大数加法之C语言函数法(只有正数版)

         由于某些原因,我于今天2017-4-19将我的博文搬到博客园了,以后我就在这里扎根了.         之前想过在博客写文章方便日后复习,但一直未能实现,所以,现在这篇是我个人人生中第一篇博 ...

  6. JavaScript实现AOP(面向切面编程)

    什么是AOP? AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计.安全控制.异常处理等.把这些功能抽离出来之后, 再通过" ...

  7. json解析—Gson以及GsonFormat插件的运用

    最近开始慢慢做毕业设计了,遇到一个功能是获取天气预报的,我选择的是和风天气的api,返回的是JSON数据,所以遇到了解析JSON的问题 首先简单说下JSON,JSON(JavaScript Objec ...

  8. Java IO(3)非阻塞式输入输出(NIO)

    在上篇<Java IO(2)阻塞式输入输出(BIO)>的末尾谈到了什么是阻塞式输入输出,通过Socket编程对其有了大致了解.现在再重新回顾梳理一下,对于只有一个“客户端”和一个“服务器端 ...

  9. Struts2报错org.apache.jasper.JasperException: Invalid directive原因

    struts标签书写错误.<s:textfield name="us.username"/>对的<s:textfield name="us.userna ...

  10. VirtualBox 安装 Ubuntu 14.04 无法调节分辨率问题

    基础环境 宿主系统:Windows 10 虚拟机系统:Ubuntu 14.04-32bit.Ubuntu 14.04-64bit VirtualBox:5.2.0 r118431 (Qt5.6.2) ...