如何在 ASP.NET Core 5.0 中获取我的授权策略要求中的用户输入参数?
How to get user input parameters in my authorization policy requirement in ASP.NET Core 5.0?
我想授权用户只能查看他们自己的资源(例如: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
classes:
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
class我想配置授权策略。如何配置我的 Requirement
class 以从控制器的操作方法获取用户输入参数?
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>();
我建议您使用 IAuthorizationRequirement 和 AuthorizationHandler 方法。 AuthorizationHandler 的实例(其中 T 是要求)在您的启动中注册为单例。因此,您可以将 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.
}
}
这没有看起来那么复杂。
我最终得到了这个解决方案:
/// <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)
{
// ...
}
我想授权用户只能查看他们自己的资源(例如: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
classes:
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
class我想配置授权策略。如何配置我的 Requirement
class 以从控制器的操作方法获取用户输入参数?
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>();
我建议您使用 IAuthorizationRequirement 和 AuthorizationHandler 方法。 AuthorizationHandler 的实例(其中 T 是要求)在您的启动中注册为单例。因此,您可以将 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.
}
}
这没有看起来那么复杂。
我最终得到了这个解决方案:
/// <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)
{
// ...
}