问题描述
我有很多控制器,有很多动作.每个动作都有自己的角色(角色名称 = ControllerName.actionName).
I have many controllers with many actions. Each action has it's own Role ( Role name = ControllerName.actionName ).
在以前的版本中,我可以测试当前用户是否可以使用通用" AuthorizeAttribute 访问操作:
In previous versions I could test if the current user can acces an action or not using a "generic" AuthorizeAttribute :
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
string currentAction = actionContext.ActionDescriptor.ActionName;
string currentController = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName;
Roles = (currentController + "." + currentAction).ToLower();
base.OnAuthorization(actionContext);
}
使用asp.net 5版本,我发现我需要使用需求( 如何在 ASP.NET Core 中创建自定义 AuthorizeAttribute? ).问题是 AuthorizationContext 没有向我们提供有关用户尝试执行的操作的信息.
With the version asp.net 5, I've found that I need to use requirements ( How do you create a custom AuthorizeAttribute in ASP.NET Core? ). The problem is that the AuthorizationContext does not give us the information about the action that the user is trying to get to.
我不想在每个操作上都添加 Authorize 属性,有什么方法可以通过新框架实现我的要求?(我更喜欢避免使用 HttpContext.Current,它不太适合管道架构)
I don't want to put an Authorize attribute on each action, is there any way to achieve my requirement with the new framework ? ( I prefer to avoid using HttpContext.Current, it doesn't fit well in a pipeline architecture )
推荐答案
这里是执行自定义身份验证的一般过程.您的情况可能可以在第一步中完全解决,因为您可以为装饰您的角色添加声明
Here is the general process for enforcing custom authentication. Your situation may be able to solved completely in step one, since you could add a Claim for the Role that decorates your
1.验证为用户创建一个身份
1. authenticate by creating an identity for the user
编写中间件并通过 IApplicationBuilder.UseMiddleware<>
将其插入管道是完成自定义身份验证的方式.这是我们提取授权以后可能需要的任何信息的地方,并将其放入 ClaimsIdentity
中.我们在这里有一个 HttpContext
,因此我们可以从标头、cookie、请求的路径等中获取信息.这是一个示例:
Writing middleware and inserting it into the pipeline via IApplicationBuilder.UseMiddleware<>
is how custom authentication is done. This is where we extract whatever info may be later needed for authorization, and put it into an ClaimsIdentity
. We have an HttpContext
here so we can grab info from the header, cookies, requested path, etc. Here is an example:
public class MyAuthHandler : AuthenticationHandler<MyAuthOptions>
{
protected override Task<AuthenticationTicket> HandleAuthenticateAsync()
{
// grab stuff from the HttpContext
string authHeader = Request.Headers["Authorization"] ?? "";
string path = Request.Path.ToString() ?? "";
// make a MyAuth identity with claims specifying what we'll validate against
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Authentication, authHeader),
new Claim(ClaimTypes.Uri, path)
}, Options.AuthenticationScheme);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity),
new AuthenticationProperties(), Options.AuthenticationScheme);
return Task.FromResult(ticket);
}
}
public class MyAuthOptions : AuthenticationOptions
{
public const string Scheme = "MyAuth";
public MyAuthOptions()
{
AuthenticationScheme = Scheme;
AutomaticAuthentication = true;
}
}
public class MyAuthMiddleware : AuthenticationMiddleware<MyAuthOptions>
{
public MyAuthMiddleware(
RequestDelegate next,
IDataProtectionProvider dataProtectionProvider,
ILoggerFactory loggerFactory,
IUrlEncoder urlEncoder,
IOptions<MyAuthOptions> options,
ConfigureOptions<MyAuthOptions> configureOptions)
: base(next, options, loggerFactory, urlEncoder, configureOptions)
{
}
protected override AuthenticationHandler<MyAuthOptions> CreateHandler()
{
return new MyAuthHandler();
}
}
public static class MyAuthMiddlewareAppBuilderExtensions
{
public static IApplicationBuilder UseMyAuthAuthentication(this IApplicationBuilder app, string optionsName = "")
{
return app.UseMiddleware<MyAuthMiddleware>(
new ConfigureOptions<MyAuthOptions>(o => new MyAuthOptions()) { Name = optionsName });
}
}
要使用此中间件,请在路由之前的 Startup.Configure
中插入:app.UseMyAuthAuthentication();
To use this middleware insert this in Startup.Configure
prior to the routing: app.UseMyAuthAuthentication();
<强>2.授权通过强制执行身份要求
2. authorize by enforcing requirements on the identity
我们已经为用户创建了一个身份,但我们仍然需要强制执行它.为此,我们需要像这样编写一个 AuthorizationHandler
:
We've created an identity for the user but we still need to enforce it. To do this we need to write an AuthorizationHandler
like this:
public class MyAuthRequirement : AuthorizationHandler<MyAuthRequirement>, IAuthorizationRequirement
{
public override void Handle(AuthorizationContext context, MyAuthRequirement requirement)
{
// grab the identity for the MyAuth authentication
var myAuthIdentities = context.User.Identities
.Where(x => x.AuthenticationType == MyAuthOptions.Scheme).FirstOrDefault();
if (myAuthIdentities == null)
{
context.Fail();
return;
}
// grab the authentication header and uri types for our identity
var authHeaderClaim = myAuthIdentities.Claims.Where(x => x.Type == ClaimTypes.Authentication).FirstOrDefault();
var uriClaim = context.User.Claims.Where(x => x.Type == ClaimTypes.Uri).FirstOrDefault();
if (uriClaim == null || authHeaderClaim == null)
{
context.Fail();
return;
}
// enforce our requirement (evaluate values from the identity/claims)
if ( /* passes our enforcement test */ )
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
}
3.将需求处理程序添加为授权策略
我们的身份验证要求仍然需要添加到 Startup.ConfigureServices
中才能使用:
Our authentication requirement still needs to be added to the Startup.ConfigureServices
so that it can be used:
// add our policy to the authorization configuration
services.ConfigureAuthorization(auth =>
{
auth.AddPolicy(MyAuthOptions.Scheme,
policy => policy.Requirements.Add(new MyAuthRequirement()));
});
4.使用授权政策
最后一步是通过使用 [Authorize("MyAuth")]
装饰我们的操作或控制器来对特定操作强制执行此要求.如果我们有许多控制器,每个控制器都有许多需要强制执行的操作,那么我们可能想要创建一个基类并仅装饰单个控制器.
The final step is to enforce this requirement for specific actions by decorating our action or controller with [Authorize("MyAuth")]
. If we have many controllers, each with many action which require enforcement, then we may want to make a base class and just decorate that single controller.
你更简单的情况:
每个动作都有自己的角色(角色名称 = ControllerName.actionName>)
Each action has it's own Role ( Role name = ControllerName.actionName> )
如果您已经使用 [Authorize(Roles = "controllername.actionname")]
对所有操作进行了微调,那么您可能只需要上面的第 1 部分.只需添加一个对特定请求有效的新 Claim(ClaimTypes.Role, "controllername.actionname")
.
If you already have all your actions fine-tuned with [Authorize(Roles = "controllername.actionname")]
then you probably only need part #1 above. Just add a new Claim(ClaimTypes.Role, "controllername.actionname")
that is valid for the particular request.
这篇关于根据操作名称授权用户的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!