我有很多控制器有很多动作。每个操作都有自己的角色(角色名称= ControllerName.actionName)。
在以前的版本中,我可以测试当前用户是否可以使用“通用”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版本,我发现我需要使用需求(How do you create a custom AuthorizeAttribute in ASP.NET Core?)。问题是AuthorizationContext没有向我们提供有关用户试图访问的操作的信息。
我不想在每个动作上放置一个Authorize属性,有没有办法用新框架实现我的要求? (我更喜欢避免使用HttpContext.Current,它不适合管道架构)
答案 0 :(得分:43)
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. authenticate by creating an identity for the user
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 });
}
}
To use this middleware insert this in Startup.Configure
prior to the routing: app.UseMyAuthAuthentication();
2. authorize by enforcing requirements on the identity
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. add the requirement handler as an authorization policy
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. use the authorization policy
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.
Your simpler situation:
Each action has it's own Role ( Role name = 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.