基于路由值的全局资源授权
Global Resource Authorization based on route value
我正在开发 ASP.Net Core 3.1 网络 API。
API 个用户来自 Azure AD。
用户如果有许可证就可以访问API,每个用户可以分配给多个许可证,同一个许可证可以分配给多个用户。
客户希望我使用 <api_url>/{licenseId}/controller/action
.
这样的模板构建 API 路由
我的控制器是这样的:
[Authorize]
[Route("{licenseId}/Foo")]
public class FooController : ControllerBase
{
如果我将许可证视为一种资源,我可以使用详述的基于资源的授权here。
它有效,但我发现自己复制并粘贴了所有操作的身份验证检查。
有没有更好的方法来使用路由值授权用户?
到目前为止我得到的是:
public class LicenseRequirement : IAuthorizationRequirement
{
public Guid LicenseId { get; private set; }
public LicenseRequirement(Guid licenseId)
{
LicenseId = licenseId;
}
}
public class LicenseAuthorizationHandler : AuthorizationHandler<LicenseRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<LicenseAuthorizationHandler> _logger;
private readonly DBContext _db;
public LicenseAuthorizationHandler(DBContext context, ILogger<LicenseAuthorizationHandler> logger, IHttpContextAccessor httpContextAccessor)
{
_logger = logger;
_db = context;
_httpContextAccessor = httpContextAccessor;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, LicenseRequirement requirement)
{
var userId = new Guid(context.User.GetUserId());
var licenseId = _httpContextAccessor.HttpContext.GetRouteData().Values["licenseId"];
if (await _db.ApiUsers.SingleOrDefaultAsync(x => x.LicenseId == new Guid(licenseId as string) && x.UserId == userId) is ApiUser user)
context.Succeed(requirement);
}
}
现在我有点卡住了,因为我不知道如何在 Startup.cs
中设置它并将其用作属性或区域过滤器,在运行时使用 licenseId
创建这些要求路线的价值。
我发现 IAuthorizationFilter
实施起来非常简单。当我昨天尝试时,我找不到 RouteValues
但它们在那里:
public class LicenseAuthorizationFilter : IAuthorizationFilter
{
private readonly ILogger<LicenseAuthorizationFilter> _logger;
private readonly DBContext _db;
public LicenseAuthorizationFilter(DBContext context, ILogger<LicenseAuthorizationFilter> logger)
{
_logger = logger;
_db = context;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var userId = new Guid(context.HttpContext.User.GetUserId());
var licenseId = new Guid(context.HttpContext.Request.RouteValues["licenseId"] as string);
if (!(_db.ApiUsers.SingleOrDefault(x => x.LicenseId == licenseId && x.UserId == userId) is ApiUser user))
{
context.Result = new ForbidResult();
}
}
}
public class LicenseAuthorizationAttribute : TypeFilterAttribute
{
public LicenseAuthorizationAttribute() : base(typeof(LicenseAuthorizationFilter))
{ }
}
控制器可以巧妙地变成:
[Authorize]
[LicenseAuthorization]
[Route("{licenseId}/Items")]
public class ItemsController : ControllerBase
{
[...]
}
我正在开发 ASP.Net Core 3.1 网络 API。
API 个用户来自 Azure AD。 用户如果有许可证就可以访问API,每个用户可以分配给多个许可证,同一个许可证可以分配给多个用户。
客户希望我使用 <api_url>/{licenseId}/controller/action
.
我的控制器是这样的:
[Authorize]
[Route("{licenseId}/Foo")]
public class FooController : ControllerBase
{
如果我将许可证视为一种资源,我可以使用详述的基于资源的授权here。 它有效,但我发现自己复制并粘贴了所有操作的身份验证检查。
有没有更好的方法来使用路由值授权用户?
到目前为止我得到的是:
public class LicenseRequirement : IAuthorizationRequirement
{
public Guid LicenseId { get; private set; }
public LicenseRequirement(Guid licenseId)
{
LicenseId = licenseId;
}
}
public class LicenseAuthorizationHandler : AuthorizationHandler<LicenseRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<LicenseAuthorizationHandler> _logger;
private readonly DBContext _db;
public LicenseAuthorizationHandler(DBContext context, ILogger<LicenseAuthorizationHandler> logger, IHttpContextAccessor httpContextAccessor)
{
_logger = logger;
_db = context;
_httpContextAccessor = httpContextAccessor;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, LicenseRequirement requirement)
{
var userId = new Guid(context.User.GetUserId());
var licenseId = _httpContextAccessor.HttpContext.GetRouteData().Values["licenseId"];
if (await _db.ApiUsers.SingleOrDefaultAsync(x => x.LicenseId == new Guid(licenseId as string) && x.UserId == userId) is ApiUser user)
context.Succeed(requirement);
}
}
现在我有点卡住了,因为我不知道如何在 Startup.cs
中设置它并将其用作属性或区域过滤器,在运行时使用 licenseId
创建这些要求路线的价值。
我发现 IAuthorizationFilter
实施起来非常简单。当我昨天尝试时,我找不到 RouteValues
但它们在那里:
public class LicenseAuthorizationFilter : IAuthorizationFilter
{
private readonly ILogger<LicenseAuthorizationFilter> _logger;
private readonly DBContext _db;
public LicenseAuthorizationFilter(DBContext context, ILogger<LicenseAuthorizationFilter> logger)
{
_logger = logger;
_db = context;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var userId = new Guid(context.HttpContext.User.GetUserId());
var licenseId = new Guid(context.HttpContext.Request.RouteValues["licenseId"] as string);
if (!(_db.ApiUsers.SingleOrDefault(x => x.LicenseId == licenseId && x.UserId == userId) is ApiUser user))
{
context.Result = new ForbidResult();
}
}
}
public class LicenseAuthorizationAttribute : TypeFilterAttribute
{
public LicenseAuthorizationAttribute() : base(typeof(LicenseAuthorizationFilter))
{ }
}
控制器可以巧妙地变成:
[Authorize]
[LicenseAuthorization]
[Route("{licenseId}/Items")]
public class ItemsController : ControllerBase
{
[...]
}