我从 SPA 收到不记名 JWT 访问令牌,我需要验证此 JWT 令牌,但也需要使用其中的角色声明。角色声明当前使用字段“user-roles”:“admin,usertype2”。因此,角色类型在“用户角色”中以逗号分隔(如果这更容易,我也许可以让我们的 Okta 团队将其更改为字符串数组,但目前这就是实现)。我已经构建了一个自定义属性来尝试处理授权,但我想使用标准 MS 库来完成这项工作。我的program.cs中有以下内容:
builder.Services.AddAuthentication().AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "user-roles"
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRateLimiter();
app.UseStaticFiles();
app.UseCors("MyPolicy");
app.UseAuthorization();
app.MapControllers();
app.Run();
然后对于我的实施,我尝试了以下操作:
[HttpGet(Name = "GetUserAccount")]
[Authorize(Roles = "usertype1,usertype2")]
[EnableRateLimiting("api")]
public IActionResult Get()
{
User TestAccount = new User() {InternalUserNumber = 3, EmailAddress = "[email protected]"};
var json = _userAccountService.Read(TestAccount);
return Ok(json);
}
我认为在 AddJwtBearer() 函数的选项中设置 RoleClaimType = "user-roles" 将允许在声明中设置要查找的自定义字段,但似乎并没有这样做。我不确定如何设置自定义角色字段,也不清楚如何将字符串分割(),以便它可以检查这些角色实际上在声明中的相交。
为了使用属性通过 AddJwtBearer() 进行声明检查,可以使用以下方法:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.IdentityModel.Tokens;
namespace ARMS_API.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class OktaAllowedRoles : Attribute, IAuthorizationFilter
{
private readonly string[] _allowedRoles;
public OktaAllowedRoles(string[] AllowedRoles)
{
_allowedRoles = AllowedRoles;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var claim = context.HttpContext.User.Claims.Where(claim => claim.Type.ToString() == "roles").FirstOrDefault();
if(claim == null){
context.Result = new UnauthorizedObjectResult(string.Empty);
return;
}
else{
var ClaimRoles = claim.Value.Split(',');
if(ClaimRoles.Intersect(_allowedRoles!).ToArray().IsNullOrEmpty())
{
context.Result = new UnauthorizedObjectResult(string.Empty);
return;
}
}
}
}
}