[转]How do you create a custom AuthorizeAttribute in ASP.NET Core?
问:
I'm trying to make a custom authorization attribute in ASP.NET Core.
In previous versions it was possible to override bool AuthorizeCore(HttpContextBase httpContext).
But this no longer exists in AuthorizeAttribute.
What is the current approach to make a custom AuthorizeAttribute?
What I am trying to accomplish: I am receiving a session ID in the Header Authorization. From that ID I'll know whether a particular action is valid.
回答:
I'm the asp.net security person. Firstly let me apologise that none of this is documented yet outside of the musicstore sample or unit tests, and it's all still being refined in terms of exposed APIs.
We don't want you writing custom authorize attributes. If you need to do that we've done something wrong. Instead you should be writing authorization requirements.
Authorization acts upon Identities. Identities are created by authentication.
You say in comments you want to check a session ID in a header. Your session ID would be the basis for an identity. If you wanted to use the Authorize attribute you'd write an authentication middleware to take that header and turn it into an authenticated ClaimsPrincipal. You would then check that inside an authorization requirement. Authorization requirements can be as complicated as you like, for example here's one that takes a date of birth claim on the current identity and will authorize if the user is over 18;
public class Over18Requirement : AuthorizationHandler<Over18Requirement>, IAuthorizationRequirement
{
public override void Handle(AuthorizationContext context, Over18Requirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth))
{
context.Fail();
return;
}
var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);
int age = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-age))
{
age--;
}
if (age >= 18)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
}
}
Then in your ConfigureServices() function you'd wire it up
options.AddPolicy("Over18",
policy => policy.Requirements.Add(new Authorization.Over18Requirement()));
And finally apply it to a controller or action method with
[Authorize(Policy = "Over18")]
回答:
What is the current approach to make a custom AuthorizeAttribute
Easy: don't create your own AuthorizeAttribute.
For pure authorization scenarios (like restricting access to specific users only), the recommended approach is to use the new authorization block: https://github.com/aspnet/MusicStore/blob/master/src/MusicStore/Startup.cs#L118-L121
public class Startup {
public void ConfigureServices(IServiceCollection services) {
services.Configure<AuthorizationOptions>(options => {
options.AddPolicy("ManageStore", policy => policy.RequireClaim("Action", "ManageStore"));
});
}
}
public class StoreController : Controller {
[Authorize(Policy = "ManageStore"), HttpGet]
public async Task<IActionResult> Manage() { ... }
}
For authentication, it's best handled at the middleware level.
What are you trying to achieve exactly?
回答:
You can create your own AuthorizationHandler that will find custom attributes on your Controllers and Actions, and pass them to the HandleRequirementAsync method.
public abstract class AttributeAuthorizationHandler<TRequirement, TAttribute> : AuthorizationHandler<TRequirement> where TRequirement : IAuthorizationRequirement where TAttribute : Attribute
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement)
{
var attributes = new List<TAttribute>();
var action = (context.Resource as AuthorizationFilterContext)?.ActionDescriptor as ControllerActionDescriptor;
if (action != null)
{
attributes.AddRange(GetAttributes(action.ControllerTypeInfo.UnderlyingSystemType));
attributes.AddRange(GetAttributes(action.MethodInfo));
}
return HandleRequirementAsync(context, requirement, attributes);
}
protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable<TAttribute> attributes);
private static IEnumerable<TAttribute> GetAttributes(MemberInfo memberInfo)
{
return memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>();
}
}
Then you can use it for any custom attributes you need on your controllers or actions. For example to add permission requirements. Just create your custom attribute.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : AuthorizeAttribute
{
public string Name { get; }
public PermissionAttribute(string name) : base("Permission")
{
Name = name;
}
}
Then create a Requirement to add to your Policy
public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
//Add any custom requirement properties if you have them
}
Then create the AuthorizationHandler for your custom attribute, inheriting the AttributeAuthorizationHandler that we created earlier. It will be passed an IEnumerable for all your custom attributes in the HandleRequirementsAsync method, accumulated from your Controller and Action.
public class PermissionAuthorizationHandler : AttributeAuthorizationHandler<PermissionAuthorizationRequirement, PermissionAttribute>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement, IEnumerable<PermissionAttribute> attributes)
{
foreach (var permissionAttribute in attributes)
{
if (!await AuthorizeAsync(context.User, permissionAttribute.Name))
{
return;
}
}
context.Succeed(requirement);
}
private Task<bool> AuthorizeAsync(ClaimsPrincipal user, string permission)
{
//Implement your custom user permission logic here
}
}
And finally, in your Startup.cs ConfigureServices method, add your custom AuthorizationHandler to the services, and add your Policy.
services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("Permission", policyBuilder =>
{
policyBuilder.Requirements.Add(new PermissionAuthorizationRequirement());
});
});
Now you can simply decorate your Controllers and Actions with your custom attribute.
[Permission("AccessCustomers")]
public class CustomersController
{
[Permission("AddCustomer")]
IActionResult AddCustomer([FromBody] Customer customer)
{
//Add customer
}
}
回答:
The approach recommended by the ASP.Net Core team is to use the new policy design which is fully documented here. The basic idea behind the new approach is to use the new [Authorize] attribute to designate a "policy" (e.g. [Authorize( Policy = "YouNeedToBe18ToDoThis")] where the policy is registered in the application's Startup.cs to execute some block of code (i.e. ensure the user has an age claim where the age is 18 or older).
The shortcoming of this approach, however, is that it fails to provide a convenient solution for the most common need of simply asserting that a given controller or action requires a given claim type. In the case where an application may have hundreds of discrete permissions governing CRUD operations on individual REST resources ("CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder", etc.), the new approach either requires repetitive one-to-one mappings between a policy name and a claim name (e.g. options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));), or writing some code to perform these registrations at run time (e.g. read all claim types from a database and perform the aforementioned call in a loop). The problem with this approach for the majority of cases is that it's unnecessary overhead.
While it seems the ASP.Net Core Security team would have you believe you should never seek to create your own solution, in most cases this will be the most prudent option to start with.
The following is an implementation which relies upon action filters to provide a simple way to express a claim requirement for a given controller or action:
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] {new Claim(claimType, claimValue) };
}
}
public class ClaimRequirementFilter : IAsyncActionFilter
{
readonly Claim _claim;
public ClaimRequirementFilter(Claim claim)
{
_claim = claim;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
if (!hasClaim)
{
context.Result = new UnauthorizedResult();
}
else
{
await next();
}
}
}
[Route("api/resource")]
public class MyController : Controller
{
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[HttpGet]
public IActionResult GetResource()
{
return Ok();
}
}
[转]How do you create a custom AuthorizeAttribute in ASP.NET Core?的更多相关文章
- 002.Create a web API with ASP.NET Core MVC and Visual Studio for Windows -- 【在windows上用vs与asp.net core mvc 创建一个 web api 程序】
Create a web API with ASP.NET Core MVC and Visual Studio for Windows 在windows上用vs与asp.net core mvc 创 ...
- 004.Create a web app with ASP.NET Core MVC using Visual Studio on Windows --【在 windows上用VS创建mvc web app】
Create a web app with ASP.NET Core MVC using Visual Studio on Windows 在 windows上用VS创建mvc web app 201 ...
- [转]Writing Custom Middleware in ASP.NET Core 1.0
本文转自:https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/ One of the new ...
- [译]Writing Custom Middleware in ASP.NET Core 1.0
原文: https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/ Middleware是ASP. ...
- Part 13 Create a custom filter in AngularJS
Custom filter in AngularJS 1. Is a function that returns a function 2. Use the filter function to cr ...
- How could I create a custom windows message?
[问题] Our project is running on Windows CE 6.0 and is written in C++ . We have some problems with the ...
- [转]Create Custom Exception Filter in ASP.NET Core
本文转自:http://www.binaryintellect.net/articles/5df6e275-1148-45a1-a8b3-0ba2c7c9cea1.aspx In my previou ...
- [Angular] Create a custom validator for reactive forms in Angular
Also check: directive for form validation User input validation is a core part of creating proper HT ...
- [Angular] Create a custom validator for template driven forms in Angular
User input validation is a core part of creating proper HTML forms. Form validators not only help yo ...
随机推荐
- 前端开发面试题收集(js部分)
1.问:js中"1"+2+"3"+4 运算结果是? 答: js中,字符串和数值相加,得到的还是字符串,这里的结果1234也是字符串. 2.问:4+3+2+&qu ...
- 【Win10 应用开发】集成文件打开选择器
有朋友给老周提出建议:老周,能不能在写博客时讲一下有深度的小故事?技术文章谁不会写.讲一下对人生有启发性的故事会更好. 哎呀,这要求真是越来越高了.好吧,尽量吧,如果有小故事的话,老周在就每次写博客时 ...
- CRL快速开发框架系列教程八(使用CRL.Package)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- SQL Server 执行计划利用统计信息对数据行的预估原理二(为什么复合索引列顺序会影响到执行计划对数据行的预估)
本文出处:http://www.cnblogs.com/wy123/p/6008477.html 关于统计信息对数据行数做预估,之前写过对非相关列(单独或者单独的索引列)进行预估时候的算法,参考这里. ...
- 利用C#开发移动跨平台Hybrid App(一):从Native端聊Hybrid的实现
0x00 前言 前一段时间分别读了两篇博客,分别是叶小钗兄的<浅谈Hybrid技术的设计与实现>以及徐磊哥的<从技术经理的角度算一算,如何可以多快好省的做个app>.受到了很多 ...
- java占位符应用
(转载自:http://www.cnblogs.com/happyday56/p/3996498.html) String类的format()方法用于创建格式化的字符串以及连接多个字符串对象.熟悉C语 ...
- EnumHelper.cs枚举助手(枚举描述信息多语言支持)C#
C#里面经常会用到枚举类型,枚举是值类型对象,如果你想用枚举类型的多属性特性,或者你想在MVC页面上通过简单的值类型转换,将某字段值所代表的含义转换为文字显示,这时候必须要将枚举扩展,是它支持文本描述 ...
- 珍珠(bead)
题目描述 有n颗形状和大小都一致的珍珠,它们的重量都不相同.n为整数,所有的珍珠从1到n编号.你的任务是发现哪颗珍珠的重量刚好处于正中间,即在所有珍珠的重量中,该珍珠的重量列(n+1)/2位.下面给出 ...
- linux中输入输出和重定向问题
输入输出解释 当我们执行shell的时候,每个进程都和三个打开的文件有关系,并使用文件描述符来引用这些文件.但这些文件不容易记忆,所以shell给了相应的文件名: 0:输入文件-标准输入(它的命令是输 ...
- 应用.Net+Consul维护RabbitMq的高可用性
懒人学习的过程就是工作中老大让干啥让做啥就研究研究啥,国庆放假回来的周末老大通过钉钉给我布置了个任务, RabbitMQ高可用解决方案,我想说钉钉太坑了: 这是国庆过后9号周日晚上下班给的任务,我周一 ...