青岛设计公司排名,长沙官网seo收费,网站设计作业多少钱,wordpress 后台加速经过前面几章的姗姗学步#xff0c;我们了解了在 ASP.NET Core 中是如何认证的#xff0c;终于来到了授权阶段。在认证阶段我们通过用户令牌获取到用户的Claims#xff0c;而授权便是对这些的Claims的验证#xff0c;如#xff1a;是否拥有Admin的角色#xff0c;姓名是否… 经过前面几章的姗姗学步我们了解了在 ASP.NET Core 中是如何认证的终于来到了授权阶段。在认证阶段我们通过用户令牌获取到用户的Claims而授权便是对这些的Claims的验证如是否拥有Admin的角色姓名是否叫XXX等等。本章就来介绍一下 ASP.NET Core 的授权系统的简单使用。 简单授权 在ASP.NET 4.x中我们通常使用Authorize过滤器来进行授权它可以作用在Controller和Action上面也可以添加到全局过滤器中。而在ASP.NET Core中也有一个Authorize特性但不是过滤器用法类似 [Authorize] // Controller级别public class SampleDataController : Controller{[Authorize] // Action级别public IActionResult SampleAction() {}
} IAllowAnonymous 在ASP.NET 4.x中我们最常用的另一个特性便是AllowAnonymous用来设置某个Controller或者Action跳过授权它在 ASP.NET Core 中同样适用 [Authorize]public class AccountController : Controller{[AllowAnonymous] public ActionResult Login() {} public ActionResult Logout() {}
} 如上LoginAction便不再需要授权同样在 ASP.NET Core 中提供了一个统一的IAllowAnonymous接口在授权逻辑中都是通过该接口来判断是否跳过授权验证的。 public interface IAllowAnonymous{
}[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple false, Inherited true)]public class AllowAnonymousAttribute : Attribute, IAllowAnonymous{
} IAuthorizeData 上面提到在 ASP.NET Core 中AuthorizeAttribute不再是一个MVC中的Filter了而只是一个简单的实现了IAuthorizeData接口的Attribute public interface IAuthorizeData{ string Policy { get; set; } Roles { get; set; } string AuthenticationSchemes { get; set; }
}[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple true, Inherited true)]public class AuthorizeAttribute : Attribute, IAuthorizeData{ public AuthorizeAttribute() { } public AuthorizeAttribute(string policy) {Policy policy;} public string Policy { get; set; } public string Roles { get; set; } public string AuthenticationSchemes { get; set; }
} 记得第一次在ASP.NET Core中实现自定义授权时按照以前的经验直接继承自AuthorizeAttribute然后准备重写OnAuthorization方法结果懵逼了。然后在MVC的源码中苦苦搜寻AuthorizeAttribute的踪迹却毫无所获后来才注意到它实现了IAuthorizeData接口该接口才是认证的源头而Authorize特性只是认证信息的载体并不包含任何逻辑。IAuthorizeData中定义的Policy, Roles, AuthenticationSchemes三个属性分别代表着 ASP.NET Core 授权系统中的三种授权方式。 基于角色的授权 基于角色的授权我们都比较熟悉使用方式如下 [Authorize(Roles Admin)] // 多个Role可以使用,分割public class SampleDataController : Controller{...
} 基于角色的授权的逻辑与ASP.NET 4.x类似都是使用我在《初识认证》中介绍的IsInRole方法来实现的。 基于Scheme的授权 对于AuthenticationScheme我在前面几章也都介绍过比如Cookie认证默认使用的AuthenticationScheme就是Cookies在JwtBearer认证中默认的Scheme就是Bearer。 当初在学习认证时还在疑惑如何在使用Cookie认证的同时又支持Bearer认证呢因为在认证中只能设置一个Scheme来执行当看到这里豁然开朗后面会详细介绍。 [Authorize(AuthenticationSchemes Cookies)] // 多个Scheme可以使用,分割public class SampleDataController : Controller{...
} 当我们的应用程序中同时使用了多种认证Scheme时AuthenticationScheme授权就非常有用在该授权模式下会通过context.AuthenticateAsync(scheme)重新获取Claims。 基于策略的授权 在ASP.NET Core中重新设计了一种更加灵活的授权方式基于策略的授权也是授权的核心。 在使用基于策略的授权时首先要定义授权策略而授权策略本质上就是对Claims的一系列断言。 public void ConfigureServices(IServiceCollection services){services.AddMvc();services.AddAuthorization(options {options.AddPolicy(EmployeeOnly, policy policy.RequireClaim(EmployeeNumber));});
} 如上我们定义了一个名称为EmployeeOnly的授权策略它要求用户的Claims中必须包含类型为EmployeeNumber的Claim。 其实基于角色的授权和基于Scheme的授权只是一种语法上的便捷最终都会生成授权策略后文会详解介绍。 然后便可以在Authorize特性中通过Policy属性来指定授权策略 [Authorize(Policy EmployeeOnly)]public class SampleDataController : Controller{} 授权策略详解 AddAuthorization 授权策略的定义使用了AddAuthorization扩展方法我们来看看它的源码 public static class AuthorizationServiceCollectionExtensions{ public static IServiceCollection AddAuthorization(this IServiceCollection services) { services.TryAdd(ServiceDescriptor.TransientIAuthorizationService, DefaultAuthorizationService());services.TryAdd(ServiceDescriptor.TransientIAuthorizationPolicyProvider, DefaultAuthorizationPolicyProvider());services.TryAdd(ServiceDescriptor.TransientIAuthorizationHandlerProvider, DefaultAuthorizationHandlerProvider());services.TryAdd(ServiceDescriptor.TransientIAuthorizationEvaluator, DefaultAuthorizationEvaluator());services.TryAdd(ServiceDescriptor.TransientIAuthorizationHandlerContextFactory, DefaultAuthorizationHandlerContextFactory());services.TryAddEnumerable(ServiceDescriptor.TransientIAuthorizationHandler, PassThroughAuthorizationHandler()); return services;} public static IServiceCollection AddAuthorization(this IServiceCollection services, ActionAuthorizationOptions configure) {services.Configure(configure); return services.AddAuthorization();}
} 首先是对授权进行配置的AuthorizationOptions然后在DI系统中注册了几个核心对象的默认实现我们一一来看。 AuthorizationOptions 对于Options模式大家应该都比较熟悉了AuthorizationOptions是添加和获取授权策略的入口点 public class AuthorizationOptions{ private IDictionarystring, AuthorizationPolicy PolicyMap { get; } new Dictionarystring, AuthorizationPolicy(StringComparer.OrdinalIgnoreCase); // 在上一个策略验证失败后是否继续执行下一个授权策略public bool InvokeHandlersAfterFailure { get; set; } true; public AuthorizationPolicy DefaultPolicy { get; set; } new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); public void AddPolicy(string name, AuthorizationPolicy policy) {PolicyMap[name] policy;} public void AddPolicy(string name, ActionAuthorizationPolicyBuilder configurePolicy) { var policyBuilder new AuthorizationPolicyBuilder();configurePolicy(policyBuilder);AddPolicy(name,policyBuilder.Build());} public AuthorizationPolicy GetPolicy(string name) { return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null;}
} 首先是一个PolicyMap字典我们定义的策略都保存在其中AddPolicy方法只是简单的将策略添加到该字典中而其DefaultPolicy属性表示默认策略初始值为“已认证用户”。 在AuthorizationOptions中主要涉及到AuthorizationPolicy和AuthorizationPolicyBuilder两个对象。 AuthorizationPolicy 在 ASP.NET Core 中授权策略具体表现为一个AuthorizationPolicy对象 public class AuthorizationPolicy{ public AuthorizationPolicy(IEnumerableIAuthorizationRequirement requirements, IEnumerablestring authenticationSchemes) {} public IReadOnlyListIAuthorizationRequirement Requirements { get; } public IReadOnlyListstring AuthenticationSchemes { get; } public static AuthorizationPolicy Combine(params AuthorizationPolicy[] policies) { return Combine((IEnumerableAuthorizationPolicy)policies);} public static AuthorizationPolicy Combine(IEnumerableAuthorizationPolicy policies) { foreach (var policy in policies){builder.Combine(policy);} return builder.Build();} public static async TaskAuthorizationPolicy CombineAsync(IAuthorizationPolicyProvider policyProvider, IEnumerableIAuthorizeData authorizeData) { foreach (var authorizeDatum in authorizeData){any true; var useDefaultPolicy true; if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy)){policyBuilder.Combine(await policyProvider.GetPolicyAsync(authorizeDatum.Policy));useDefaultPolicy false;} var rolesSplit authorizeDatum.Roles?.Split(,); if (rolesSplit ! null rolesSplit.Any()){policyBuilder.RequireRole(rolesSplit.Where(r !string.IsNullOrWhiteSpace(r)).Select(r r.Trim()));useDefaultPolicy false;} var authTypesSplit authorizeDatum.AuthenticationSchemes?.Split(,); if (authTypesSplit ! null authTypesSplit.Any()){ foreach (var authType in authTypesSplit){ if (!string.IsNullOrWhiteSpace(authType)){policyBuilder.AuthenticationSchemes.Add(authType.Trim());}}} if (useDefaultPolicy){policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync());}} return any ? policyBuilder.Build() : null;}
} 如上Combine方法通过调用AuthorizationPolicyBuilder来完成授权策略的合并而CombineAsync则是将我们上面介绍的IAuthorizeData转换为授权策略因此上面说基于角色/Scheme的授权本质上都是基于策略的授权。 对于AuthenticationSchemes属性我们在前几章介绍认证时经常看到用来表示我们使用哪个认证Scheme来获取用户的Claims如果指定多个则会合并它们的Claims其实现下一章再来介绍。 而Requirements属性则是策略的核心了每一个Requirement都代表一个授权条件我们就先来了解一下它。 IAuthorizationRequirement Requirement使用IAuthorizationRequirement接口来表示 public interface IAuthorizationRequirement{
} IAuthorizationRequirement接口中并没有任何成员在 ASP.NET Core 中内置了一些常用的实现 AssertionRequirement 使用最原始的断言形式来声明授权策略。 DenyAnonymousAuthorizationRequirement 用于表示禁止匿名用户访问的授权策略并在AuthorizationOptions中将其设置为默认策略。 ClaimsAuthorizationRequirement 用于表示判断Cliams中是否包含预期的Claims的授权策略。 RolesAuthorizationRequirement 用于表示使用ClaimsPrincipal.IsInRole来判断是否包含预期的Role的授权策略。 NameAuthorizationRequirement用于表示使用ClaimsPrincipal.Identities.Name来判断是否包含预期的Name的授权策略。 OperationAuthorizationRequirement用于表示基于操作的授权策略。 其逻辑也都非常简单我就不再一一介绍只展示一下RolesAuthorizationRequirement的代码片段 public class RolesAuthorizationRequirement : AuthorizationHandlerRolesAuthorizationRequirement, IAuthorizationRequirement{ public IEnumerablestring AllowedRoles { get; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement) {... if (requirement.AllowedRoles.Any(r context.User.IsInRole(r))){context.Succeed(requirement);} return Task.CompletedTask;}
} 其AllowedRoles表示允许授权通过的角色而它还实现了IAuthorizationHandler接口用来完成授权的逻辑。 public interface IAuthorizationHandler{ Task HandleAsync(AuthorizationHandlerContext context);
} AuthorizationRequirement并不是一定要实现IAuthorizationHandler接口后文会详细介绍。 AuthorizationPolicyBuilder 在上面已经多次用到AuthorizationPolicyBuilder它提供了一系列创建AuthorizationPolicy的快捷方法 public class AuthorizationPolicyBuilder{ public AuthorizationPolicyBuilder(params string[] authenticationSchemes); public AuthorizationPolicyBuilder(AuthorizationPolicy policy); public IListIAuthorizationRequirement Requirements { get; set; } public IListstring AuthenticationSchemes { get; set; } public AuthorizationPolicyBuilder AddAuthenticationSchemes(params string[] schemes); public AuthorizationPolicyBuilder AddRequirements(params IAuthorizationRequirement[] requirements); public AuthorizationPolicyBuilder RequireAssertion(FuncAuthorizationHandlerContext, bool handler); public AuthorizationPolicyBuilder RequireAssertion(FuncAuthorizationHandlerContext, Taskbool handler) {Requirements.Add(new AssertionRequirement(handler)); return this;} public AuthorizationPolicyBuilder RequireAuthenticatedUser() {Requirements.Add(new DenyAnonymousAuthorizationRequirement()); return this;} public AuthorizationPolicyBuilder RequireClaim(string claimType); public AuthorizationPolicyBuilder RequireClaim(string claimType, params string[] requiredValues); public AuthorizationPolicyBuilder RequireClaim(string claimType, IEnumerablestring requiredValues) {Requirements.Add(new ClaimsAuthorizationRequirement(claimType, requiredValues)); return this;} public AuthorizationPolicyBuilder RequireRole(params string[] roles); public AuthorizationPolicyBuilder RequireRole(IEnumerablestring roles) {Requirements.Add(new RolesAuthorizationRequirement(roles)); return this;} public AuthorizationPolicyBuilder RequireUserName(string userName) {Requirements.Add(new NameAuthorizationRequirement(userName)); return this;} public AuthorizationPolicy Build(); public AuthorizationPolicyBuilder Combine(AuthorizationPolicy policy);
} 在上面介绍的几个Requirement除了OperationAuthorizationRequirement外都有对应的快捷添加方法由于OperationAuthorizationRequirement并不属于基于资源的授权所以不在这里其用法留在其后续章节再来介绍。 整个授权策略的内容也就这么多并不复杂整个结构大致如下 基于策略的授权进阶 在上一小节我们探索了一下授权策略的源码现在就来实战一下。 我们使用AuthorizationPolicyBuilder可以很容易的在策略定义中组合我们需要的Requirement public void ConfigureServices(IServiceCollection services){ var commonPolicy new AuthorizationPolicyBuilder().RequireClaim(MyType).Build();services.AddAuthorization(options {options.AddPolicy(User, policy policy.RequireAssertion(context context.User.HasClaim(c (c.Type EmployeeNumber || c.Type Role))));options.AddPolicy(Employee, policy policy.RequireRole(Admin).RequireUserName(Alice).RequireClaim(EmployeeNumber).Combine(commonPolicy));});
} 如上如果需要我们还可以定义一个公共的策略对象然后在策略定义中直接将其合并进来。 自定义策略 当内置的Requirement不能满足我们的需求时我们也可以很容易的定义自己的Requirement public class MinimumAgeRequirement : AuthorizationHandlerNameAuthorizationRequirement, IAuthorizationRequirement{ public MinimumAgeRequirement(int minimumAge) {MinimumAge minimumAge;} public int MinimumAge { get; private set; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, NameAuthorizationRequirement requirement) { if (context.User ! null context.User.HasClaim(c c.Type ClaimTypes.DateOfBirth){ var dateOfBirth Convert.ToDateTime(context.User.FindFirst(c c.Type ClaimTypes.DateOfBirth).Value); int calculatedAge DateTime.Today.Year - dateOfBirth.Year; if (dateOfBirth DateTime.Today.AddYears(-calculatedAge)){calculatedAge--;} if (calculatedAge requirement.MinimumAge){context.Succeed(requirement);}} return Task.CompletedTask;}
} 然后就可以直接在AddPolicy中使用了 services.AddAuthorization(options
{options.AddPolicy(Over21, policy policy.Requirements.Add(new MinimumAgeRequirement(21)));
}); 我们自定义的 Requirement 若想得到 ASP.NET Core 授权系统的执行除了上面示例中的实现IAuthorizationHandler接口外也可以单独定义AuthorizationHandler这样可以更好的使用DI系统并且还可以定义多个Handler下面就来演示一下。 多Handler模式 授权策略中的多个Requirement它们属于 的关系只用全部验证通过才能最终授权成功。但是在有些场景下我们可能希望一个授权策略可以适用多种情况比如我们进入公司时需要出示员工卡才可以被授权进入但是如果我们忘了带员工卡可以去申请一个临时卡同样可以授权成功: public class EnterBuildingRequirement : IAuthorizationRequirement{
}public class BadgeEntryHandler : AuthorizationHandlerEnterBuildingRequirement
{ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EnterBuildingRequirement requirement) { if (context.User.HasClaim(c c.Type ClaimTypes.BadgeId){context.Succeed(requirement);} else{ // context.Fail();} return Task.CompletedTask;}
}public class HasTemporaryStickerHandler : AuthorizationHandlerEnterBuildingRequirement
{ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EnterBuildingRequirement requirement) { if (context.User.HasClaim(c c.Type ClaimTypes.TemporaryBadgeId){context.Succeed(requirement);} return Task.CompletedTask;}
} 如上我们定义了两个Handler但是想让它们得到执行还需要将其注册到DI系统中 services.AddSingletonIAuthorizationHandler, BadgeEntryHandler();
services.AddSingletonIAuthorizationHandler, HasTemporaryStickerHandler(); 此时在我们的应该程序中使用EnterBuildingRequirement的授权时将会依次执行这两个Handler。而在上面介绍AuthorizationOptions时提到它还有一个InvokeHandlersAfterFailure属性在这里就派上用场了只有其为true时默认为True才会在当前 AuthorizationHandler 授权失败时继续执行下一个 AuthorizationHandler。 在上面的示例中我们使用context.Succeed(requirement)将授权结果设置为成功而失败时并没有做任何标记正常情况下都是这样做的。但是如果需要我们可以通过调用context.Fail()方法显式的将授权结果设置为失败那么不管其他 AuthorizationHandler 是成功还是失败最终结果都将是授权失败。 总结 ASP.NET Core 授权策略是一种非常强大、灵活的权限验证方案能够满足大部分的授权场景。通过本文对授权策略的详细介绍我想应该能够灵活的使用基于策略的授权了但是授权策略到底是怎么执行的呢在下一章中就来完整的探索一下 ASP.NET Core 授权系统的执行流程。 相关文章 ASP.NET Core 认证与授权[4]:JwtBearer认证 ASP.NET Core 认证与授权[2]:Cookie认证 ASP.NET Core 认证与授权[3]:OAuth OpenID Connect认证 Asp.Net Core 2.0 多角色权限认证 asp.net core 2.0 web api基于JWT自定义策略授权 原文http://www.cnblogs.com/RainingNight/p/authorization-in-asp-net-core.html .NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com