我想授权用户只查看他们自己的资源(例如:Audits
实体(。所以在AuditController
中,我有:
[MyAuthorize(Policy = nameof(ValidUserToSeeAuditAuthorizationHandler))]
[HttpGet]
public async Task<JsonResult<AuditView>> GetByIdAsync(Guid id)
{
// my business to fetch the audit info based by its id
// ...
return result;
}
然后我创建了我的Requirement
和AuthorizationHandler
类:
public class ValidUserToSeeAuditRequirment : IAuthorizationRequirement
{
public ValidUserToSeeAuditRequirment(Guid auditId)
{
auditId = auditId;
}
public Guid AuditId { get; }
}
public class ValidUserToSeeAuditAuthorizationHandler : AuthorizationHandler<ValidUserToSeeAuditRequirment>
{
private readonly AppUserManager _userManager;
private readonly IUnitOfWork _appDbContext;
public ValidUserToSeeAuditAuthorizationHandler(AppUserManager userManager, IUnitOfWork appDbContext)
{
_userManager = userManager;
_appDbContext = appDbContext;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidUserToSeeAuditRequirment requirement)
{
if (!context.User.IsAuthenticated())
{
context.Fail();
return Task.CompletedTask;
}
var theAudit = _appDbContext.Set<Audit>().SingleOrDefault(x => x.Id == requirement.AuditId);
var authenticatedUserId = Convert.ToInt32(context.User.GetSubjectId());
// If the authenticated user created the audit, then he/she is valid to see it
if (theAudit.SubjectauthenticatedUserId == authenticatedUserId)
{
// valid
context.Succeed(requirement);
return Task.CompletedTask;
}
// he/she is not authorized to see the resource (audit)
context.Fail();
return Task.CompletedTask;
}
}
但是在Startup
类中,我想要配置授权策略。如何配置Requirement
类以从控制器的操作方法获取用户输入参数?
services.AddAuthorization(options =>
{
// another policies
// ...
options.AddPolicy(name: nameof(ValidUserToSeeAuditAuthorizationHandler),
policy =>
{
policy.RequireAuthenticatedUser();
policy.AddRequirements(new ValidUserToSeeAuditRequirment( /****** HERE, how to pass the controller action method parameters ******/));
});
});
services.AddTransient<IAuthorizationHandler, ValidUserToSeeAuditAuthorizationHandler>();
您可以自定义AuthorizationPolicy
提供程序来获取参数。
public class CustomAuthorizepolicyProvider: DefaultAuthorizationPolicyProvider
{
public CustomAuthorizepolicyProvider(IOptions<AuthorizationOptions> options):base(options)
{
}
public override Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
if (policyName=="[specified plicyname]")
{
var authorizePolicy = new AuthorizationPolicyBuilder();
authorizePolicy.AddRequirements(new ValidUserToSeeAuditRequirment(/* give the parameter*/)).Build();
return Task.FromResult(authorizePolicy);
}
return base.GetPolicyAsync(policyName);
}
}
注入启动。注意:它是一个单例。
services.AddSingleton<IAuthorizationPolicyProvider,CustomAuthorizepolicyProvider>();
我最终得到了这个解决方案:
/// <summary>
///
/// </summary>
public class ValidUserToSeeAuditRequirment : IAuthorizationRequirement
{
}
/// <summary>
/// Only an Admin and the authorized user can see the Audit
/// </summary>
public class ValidUserToSeeAuditAuthorizationHandler : AuthorizationHandler<ValidUserToSeeAuditRequirment>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ValidUserToSeeAuditAuthorizationHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidUserToSeeAuditRequirment requirement)
{
// If he has the Admin Role, then he can see the Audit
if (context.User.HasClaim(x => x.Type.ToUpperInvariant() == "ROLE" && x.Value.ToUpperInvariant() == "ADMIN"))
{
context.Succeed(requirement);
return;
}
// Get the audit id from the Routing
var auditIdFromRoute = _httpContextAccessor.HttpContext.GetRouteData()?.Values["id"].ToString();
if (auditIdFromRoute is null || !Guid.TryParse(auditIdFromRoute, out Guid requestingAuditId))
{
context.Fail();
return;
}
// get the authenticated user
var userId = Convert.ToInt32(context.User.GetSubjectId());
// check if the user has authorized to see the audit
if(isUserAllowToSeeAudit(userId, requestingAuditId))
{
context.Succeed(requirement);
return;
}
context.Fail();
}
private bool isUserAllowToSeeAudit(int userId, Guid auditId)
{
// ...
}
我建议您使用IAuthorizationRequirement和AuthorizationHandler方法。AuthorizationHandler的实例(其中T是必需的(在您的启动中注册为singleton。因此,您可以将IHttpRequestAccessor注入处理程序,使其能够访问请求。这最终变成了类似的东西
public class YourAuthorizationHandler : AuthorizationHandler<YourAuthorizationRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
YourAuthorizationHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected async override Task HandleRequirementAsync(AuthorizationHandlerContext context, YourAuthorizationRequriement requirement)
{
var userIdInClaim = context.User.Claims.Where(claim => claimType == NameIdentifier).FirstOrDefault();
var request = _httpContextAccessor.HttpContext.Request;
request.EnableBuffering(); // allows the request to be read again
// read the request from request.Body assuming an HTTP POST. It will depend.
// do your logic checking the content here.
return context.Succeed(requirement); // Assuming things are what you want.
}
}
这并不像看上去那么复杂。
阅读以下ASP.NET Core 中的授权简介
您不需要向IAuthorizationRequirement
类添加任何额外的参数,也不需要在AuthorizationHandler
类中创建额外的构造函数。
您要查找的内容包含在处理程序的AuthorizationHandlerContext context
参数中。
特别是,您需要该参数的Resource
属性。该值将是特定于框架的,但通常情况下,如果您使用端点路由,您会执行以下操作:
if (context.Resource is HttpContext httpContext)
{
// Whatever you want to do with httpContext
}
使用传统路由或使用MVC的授权过滤器时,Resource的值是一个AuthorizationFilterContext
实例,在这种情况下,您可以执行以下操作:
if (context.Resource is AuthorizationFilterContext authFilterContext)
{
var httpContext = authFilterContext.HttpContext
// Whatever you want to do with httpContext
}