问:

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?的更多相关文章

  1. 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 创 ...

  2. 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 ...

  3. [转]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 ...

  4. [译]Writing Custom Middleware in ASP.NET Core 1.0

    原文: https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/ Middleware是ASP. ...

  5. 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 ...

  6. 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 ...

  7. [转]Create Custom Exception Filter in ASP.NET Core

    本文转自:http://www.binaryintellect.net/articles/5df6e275-1148-45a1-a8b3-0ba2c7c9cea1.aspx In my previou ...

  8. [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 ...

  9. [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 ...

随机推荐

  1. HTML自定义对象与属性(谷歌,火狐,IE9浏览器没问题)

    1.自定义标签 <zqz>asdas</zqz> <style> zqz{ color:red; } </style> 页面变色 2.自定义标签的hov ...

  2. H5 Notes:PostMessage Cross-Origin Communication

    Javascript中要实现跨域通信,主要有window.name,jsonp,document.domain,cors等方法.不过在H5中有一种新的方法postMessage可以安全实现跨域通信,并 ...

  3. 初步认识Node 之Node为何物

    很多人即便是在使用了Node之后也不知道它到底是什么,阅读完本文你应该会有一个初步的.具体的概念了.    Node的目标 提供一种简单的构建可伸缩网络程序的方法.那么,什么是可伸缩网络程序呢?可伸缩 ...

  4. Java jacob调用打印机打印word文档

    前面说了Java如何生成复杂的Word文档,今年记录下Java如何调用打印机打印word文档. 起初用的是自带的PrintJob,但是系统提供的打印机制并不成熟完整.网上的代码也是千篇一律,在我的打印 ...

  5. springboot(九):定时任务

    在我们的项目开发过程中,经常需要定时任务来帮助我们来做一些内容,springboot默认已经帮我们实行了,只需要添加相应的注解就可以实现 1.pom包配置 pom包里面只需要引入springboot ...

  6. 【无私分享:ASP.NET CORE 项目实战(第十章)】发布项目到 Linux 上运行 Core 项目

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win7)上用虚拟机建了个 CentOS7 ,来演示下 ...

  7. javascript中的递归函数

    正常的递归函数如下: function factorial(num){ ){ ; }else{ ); } } 这个函数表面看起来还ok,但如果我们执行下面代码就会出错. var jenny = fac ...

  8. hibernate 中根据id删除一条记录的语句

    qid name like content 1 A 1 the first text 2 B 2 the Second text 1 C 3 the Third text 如上表所示,当我们需要某个q ...

  9. html和html5详解

    最近看群里聊天聊得最火热的莫过于手机网站和html5这两个词.可能有人会问,这两者有什么关系呢?随着这移动互联网快速发展的时代,尤其是4G时代已经来临的时刻,加上微软对"XP系统" ...

  10. 301和302 Http状态有啥区别?

    301和302 Http状态有啥区别? 301,302 都是HTTP状态的编码,都代表着某个URL发生了转移,不同之处在于: 301 redirect: 301 代表永久性转移(Permanently ...