授权策略属性始终 returns 403 禁止使用 .net 核心身份和 JwtBearerAuthentication

Authorize Policy attribute always returns 403 forbidden using .net core Identity and JwtBearerAuthentication

this guide 之后,我能够使用

进行身份验证
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.AspNetCore.Authentication.JwtBearer

现在我正在尝试使用角色或声明来保护我的 api 端点。我都试过了,结果相同 (403)

仅使用 [Authorize] 即可。

我的代码目前看起来像这样:

Controller:

[Authorize(Policy = "RequireUserRole")]
// Also tried [Authorize(Roles="User")]
public string Get()
{   
  return "YO";
}

Startup:

services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<ApplicationContext>();
services.Configure<JWTSettings>(Configuration.GetSection("JWTSettings"));
services.AddAuthorization(options =>
{
        options.AddPolicy("RequireUserRole", policy => policy.RequireRole("User"));
});

...

app.UseIdentity();

var secretKey = Configuration.GetSection("JWTSettings:SecretKey").Value;
var issuer = Configuration.GetSection("JWTSettings:Issuer").Value;
var audience = Configuration.GetSection("JWTSettings:Audience").Value;
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
        AutomaticAuthenticate = true,
        AutomaticChallenge = true,
        TokenValidationParameters = new TokenValidationParameters
        {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,

                // Validate the JWT Issuer (iss) claim
                ValidateIssuer = true,
                ValidIssuer = issuer,

                // Validate the JWT Audience (aud) claim
                ValidateAudience = true,
                ValidAudience = audience,

                ValidateLifetime = true
        }
});

app.UseMvcWithDefaultRoute();

当我创建用户时,我将其分配给角色 "User"

await _userManager.AddToRoleAsync(user, "User");

角色关系创建成功,但到达端点时对角色的验证失败。

感谢任何帮助!

答案就在这个mdsn blog post:

Authorizing based on roles is available out-of-the-box with ASP.NET Identity. As long as the bearer token used for authentication contains a roles element, ASP.NET Core’s JWT bearer authentication middleware will use that data to populate roles for the user.

So, a roles-based authorization attribute (like [Authorize(Roles = "Manager,Administrator")] to limit access to managers and admins) can be added to APIs and work immediately.

所以我在我的访问令牌对象中添加了一个名为角色的元素:

private string GetAccessToken(string userRole)
{
    var payload = new Dictionary<string, object>
    {
        ...
        { "roles", userRole } 
    };
    return GetToken(payload);
}

我阅读了那个 msdn post 并在启动时添加了:

   internal class HasRoleRequerementAuthHandler : AuthorizationHandler<HasRoleRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasRoleRequirement requirement)
    { 

        if (context.User.HasClaim(c => c.Type == JwtRegisteredClaimNames.NameId))
            if (context.User.FindFirst(c => c.Type == JwtRegisteredClaimNames.Typ).Value == requirement.RoleName)
                context.Succeed(requirement);
        return Task.CompletedTask;

    }
}    
internal class HasRoleRequirement : IAuthorizationRequirement
{
    public readonly string RoleName;

    public HasRoleRequirement(string roleName)
    {
        RoleName = roleName;
    }
}

然后添加了策略:

       opt.AddPolicy("Test", builder =>
            {
                builder.Requirements.Add(new HasRoleRequirement("User"));
            });

我的令牌是这样生成的:

        var claims = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.NameId, user.Uid),
            new Claim(JwtRegisteredClaimNames.Typ, type.ToString())
        };


        var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);

        var tokenDesc = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.Now.AddDays(7),
            SigningCredentials = creds
        };

        var tokenHandler = new JwtSecurityTokenHandler();

        var token = tokenHandler.CreateToken(tokenDesc);
        return tokenHandler.WriteToken(token);

然后我可以使用策略 Auth header:

    [HttpGet("TestAuth")]
    [Authorize(Policy = "Test")]
    public IActionResult TestAuth()
    {
        return new OkResult();
    }