You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET中JWT自定义角色结合Authorize的权限验证异常问题

问题描述

给AddVoertuig接口添加[Authorize(Roles)]权限限制后,请求返回Login?ReturnUrl跳转链接,已确认Cookie中包含正确的角色信息,且JWT和CSRF令牌已设置为SameSiteMode.None、Secure=true,JWT令牌也配置了HttpOnly保障安全。相关代码如下:

目标端点代码

[HttpPost("AddVoertuig")]
[Authorize(Roles = "Particulier,Zakelijk,Wagenparkbeheerder")]
public async Task<IActionResult> AddVoertuig([FromBody] Voertuig voertuig)
{
    if (voertuig == null)
    {
        return BadRequest("Er is iets fout gegaan tijdens het toevoegen. Probeer het opnieuw!");
    }

    _context.Voertuigen.Add(voertuig);
    await _context.SaveChangesAsync();
    return CreatedAtAction(nameof(GetVoertuig), new { Id = voertuig.VoertuigID }, voertuig);
}

前端请求代码

try {
    const resultaat = await fetchCsrf("http://localhost:5202/api/VoertuigBeheer/AddVoertuig", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        credentials: "include",
        body: JSON.stringify(voertuigData)
    });
    if (resultaat.ok) {
        alert("Voertuig toegevoegd!");
        formulier.reset();
    } else {
        alert("Er is iets fout gegaan tijdens het toevoegen van het voertuig. Probeer het opnieuw!");
    }
} catch (error) {
    console.error("Fout: ", error);
}

认证配置代码

builder.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = builder.Configuration["Jwt:Issuer"],
                        ValidAudience = builder.Configuration["Jwt:Audience"],
                        IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(
                            System.Text.Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])
                        ),
                    };

                    options.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = context =>
                        {
                            var token = context.Request.Cookies["jwtToken"];
                            if (!string.IsNullOrEmpty(token))
                            {
                                context.Token = token;
                            }
                            return Task.CompletedTask;
                        }
                    };
                });
解决方案

你遗漏了以下关键配置和步骤:

  • 修改JWT Bearer的Challenge行为
    默认情况下,ASP.NET Core在JWT认证失败时会自动跳转Cookie认证的登录页面(也就是你看到的Login?ReturnUrl)。需要在JWT Bearer的事件配置中覆盖这个行为,直接返回401未授权响应:
options.Events = new JwtBearerEvents
{
    OnMessageReceived = context =>
    {
        var token = context.Request.Cookies["jwtToken"];
        if (!string.IsNullOrEmpty(token))
        {
            context.Token = token;
        }
        return Task.CompletedTask;
    },
    OnChallenge = context =>
    {
        // 阻止默认跳转,返回401 JSON响应
        context.HandleResponse();
        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        context.Response.ContentType = "application/json";
        return context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(
            new { message = "无权访问该接口" }
        ));
    }
};
  • 确保角色声明映射正确
    检查JWT令牌中的角色字段是否和ASP.NET Core默认的角色声明类型匹配。如果你的JWT里角色字段是role而非默认的微软声明格式,需要在TokenValidationParameters中配置:
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
    // 其他配置...
    RoleClaimType = "role" // 替换为你JWT中实际的角色字段名
}
  • 验证中间件顺序
    在Program.cs中,中间件必须按以下顺序注册:
app.UseAuthentication(); // 先执行认证
app.UseAuthorization();  // 再执行授权
// 后续是其他中间件,比如跨域、端点路由等
app.UseCors("你的跨域策略名");
app.UseEndpoints(endpoints => { ... });
  • 跨域请求需允许携带凭证
    如果前端和API不在同一域名,必须在跨域配置中启用AllowCredentials(),否则Cookie无法被携带到API请求中:
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowFrontend", policy =>
    {
        policy.WithOrigins("http://localhost:你的前端端口")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials(); // 必须开启这个配置
    });
});

内容的提问来源于stack exchange,提问作者Scott van Duin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:27:04