.NET 7 Web API:通过请求头传递JWT实现接口访问授权
问题描述
目标: 从前端向后端传递JWT,以访问TopicController的接口。
我有一个连接React.ts前端的.NET 7 Web API,目前已将包含身份角色声明的JWT发送到前端并能成功接收,但后端处理请求时无法正常获取API访问权限。
网络请求中,目标接口返回两次204响应、两次302响应,随后出现两次来自.NET生成的登录检查接口的405响应。
在Swagger中授权功能正常,登录后无需在授权区域传递JWT即可访问接口。
React中的API调用:
const getTopics = () => { const url = API_ROUTES.TOPICS_GET_ALL_TOPICS; fetch(url, { method: "GET", headers: { 'Authorization': `bearer ${jwt}` } }) .then(res => res.json()) .then(topicsFromServer => { setTopics(topicsFromServer); }) .catch((error => { console.error(error); })) }
认证控制器中的登录方法(正确使用JWT后将重构):
[HttpPost] [Route("/account/login")] public async Task<IActionResult> Login(LoginDTO userLogin) { Microsoft.AspNetCore.Identity.SignInResult result = await _signInManager.PasswordSignInAsync(userLogin.Email, userLogin.Password, true, false); if (!result.Succeeded) return BadRequest(400); ApplicationUser user = await _userManager.FindByEmailAsync(userLogin.Email); string token = await CreateToken(user); LoginResult resultResponse = new LoginResult { succeeded = true, Data = new List<object> { token } }; return Ok(resultResponse); }
认证控制器中的创建Token方法:
private async Task<string> CreateToken(ApplicationUser user) { IList<Claim> claims = await _userManager.GetClaimsAsync(user); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( _configuration.GetSection("AppSettings:Token").Value!)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature); var token = new JwtSecurityToken( claims: claims, expires: DateTime.Now.AddDays(1), signingCredentials: creds ); var jwt = new JwtSecurityTokenHandler().WriteToken(token); return jwt; }
TopicController(仅登录且拥有User角色的用户可访问):
namespace SdeResearch.Api.Controllers { [ApiController] [Route("[controller]")] [Authorize(Roles = "User")] public class TopicController : ControllerBase { private readonly SdeResearchDbContext _db; public TopicController(SdeResearchDbContext db) { _db = db; } [HttpGet] [Route("/topic/get-all-topics")] public async Task<List<Topic>> GetTopicsAsync() { return await _db.Topics.ToListAsync(); } } }
Program.cs中的Cors策略:
builder.Services.AddCors(options => { options.AddPolicy("CORSPolicy", builder => { builder .AllowAnyMethod() .AllowAnyHeader() .WithOrigins("http://localhost:3000"); }); });
添加认证与授权配置:
builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, ValidateAudience = true, ValidateIssuer = true, ValidIssuer = builder.Configuration.GetSection("AppSettings:Issuer").Value!, ValidAudience = builder.Configuration.GetSection("AppSettings:Audience").Value!, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( builder.Configuration.GetSection("AppSettings:Token").Value!)), }; }); builder.Services.AddAuthorization();
问题排查与解决方案
1. 补全JWT的Issuer和Audience配置
你的CreateToken方法生成JWT时未设置发行方(Issuer)和受众(Audience),但认证配置中开启了这两项验证,会直接导致Token验证失败。修改CreateToken方法:
private async Task<string> CreateToken(ApplicationUser user) { IList<Claim> claims = await _userManager.GetClaimsAsync(user); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( _configuration.GetSection("AppSettings:Token").Value!)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature); var token = new JwtSecurityToken( issuer: _configuration.GetSection("AppSettings:Issuer").Value!, audience: _configuration.GetSection("AppSettings:Audience").Value!, claims: claims, expires: DateTime.Now.AddDays(1), signingCredentials: creds ); var jwt = new JwtSecurityTokenHandler().WriteToken(token); return jwt; }
2. 确保角色声明格式正确
ASP.NET Core的[Authorize(Roles = "User")]默认检查ClaimTypes.Role类型的声明,需确保Token中包含正确的角色声明。在CreateToken中添加角色获取逻辑:
// 在CreateToken方法中,获取用户角色并添加到claims集合 var roles = await _userManager.GetRolesAsync(user); foreach (var role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); }
3. 禁用未授权请求的自动跳转
出现302响应是因为.NET默认会将未授权请求重定向到登录页(Cookie认证行为),需在JWT配置中关闭此逻辑:
.AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, ValidateAudience = true, ValidateIssuer = true, ValidIssuer = builder.Configuration.GetSection("AppSettings:Issuer").Value!, ValidAudience = builder.Configuration.GetSection("AppSettings:Audience").Value!, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( builder.Configuration.GetSection("AppSettings:Token").Value!)), }; // 禁用重定向,直接返回401未授权 options.Events = new JwtBearerEvents { OnChallenge = context => { context.HandleResponse(); context.Response.StatusCode = StatusCodes.Status401Unauthorized; return Task.CompletedTask; } }; });
4. 正确启用CORS中间件
确保CORS中间件在认证中间件之前启用,否则跨域请求会被拦截:
// Program.cs中间件管道顺序 app.UseCors("CORSPolicy"); app.UseAuthentication(); app.UseAuthorization();
5. 明确指定认证方案
为避免Cookie认证干扰JWT认证,可在[Authorize]特性中明确指定JWT方案:
[Authorize(Roles = "User", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
内容的提问来源于stack exchange,提问作者Morgan Bradford
相关产品推荐
相关产品推荐

