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
相关产品推荐
相关产品推荐

