本系列将分析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. ##4.Glance 镜像服务-- openstack pike

    ##4.Glance 镜像服务 openstack pike 安装 目录汇总 http://www.cnblogs.com/elvi/p/7613861.html ##.Glance 镜像服务.txt ...

  2. 为什么win记事本编辑的shell在linux中运行会报错

    结论:win记事本使用的格式有别于linux,二者不可混用.linux使用一个叫vi的编辑器. 解决办法:使用vi命令建立文件,在其中敲shell.命令:vi  > filename   (敲完 ...

  3. Android7.0 PowerManagerService 之亮灭屏(一)

    本篇从按下power按键后,按键事件从InputManagerService 传到PhoneWindowManager.java开始分析power 按键做屏幕亮灭过程的分析,关于power 按键的其他 ...

  4. pku 1330 Nearest Common Ancestors LCA离线

    pku 1330 Nearest Common Ancestors 题目链接: http://poj.org/problem?id=1330 题目大意: 给定一棵树的边关系,注意是有向边,因为这个WA ...

  5. CCF-201403-3-命令行选项

    问题描述 试题编号: 201403-3 试题名称: 命令行选项 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 请你写一个命令行分析程序,用以分析给定的命令行里包含哪些选项.每 ...

  6. javascript设计模式——策略模式

    前面的话 在程序设计中,常常遇到类似的情况,要实现某一个功能有多种方案可以选择.比如一个压缩文件的程序,既可以选择zip算法,也可以选择gzip算法.这些算法灵活多样,而且可以随意互相替换.这种解决方 ...

  7. VSCode插件及用户设置

    第一部分:插件 VSCode内置"emmet"插件,"convert to utf-8"等插件效果!十分强大!代码提示功能特别强悍! 插件地址:点击此处! 推荐 ...

  8. inotify软件部署及实时同步

    声明:博主使用的是CentOS6.9的系统 参考资料: https://github.com/rvoicilas/inotify-tools/wiki http://www.ibm.com/devel ...

  9. 并发与并行的区别 The differences between Concurrency and Parallel

    逻辑控制流 在程序加载到内存并执行的时候(进程),操作系统会通过让它和其他进程分时段占用CPU(CPU slices)让它产生自己独占CPU的假象(同时通过虚拟内存让它产生独占内存的假象).在CPU在 ...

  10. HDU1065 I Think I Need a Houseboat 【数学递推】

    I Think I Need a Houseboat Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Ja ...