如何使用JWT实现React到ASP.NET Core Web API的用户授权及Bearer令牌使用
问题解答
核心疑问答复
- 关于请求是否需要每次携带
Bearer令牌:所有需要授权访问的接口(登录、公开帖子列表等公开接口除外)都需要在请求头携带Authorization: Bearer 令牌,JWT本身是无状态认证机制,后端不存储用户会话信息,只能通过每次请求携带的令牌识别用户身份。 - 关于后端是否需要每次校验令牌:必须每次校验,防止令牌伪造、篡改、过期后被恶意使用,ASP.NET Core已提供开箱即用的JWT校验中间件,无需手动在每个接口写校验逻辑。
优化方案
1. 后端改造(移除多余接口,用官方认证中间件)
第一步:配置JWT认证中间件
在.NET 6+的Program.cs中添加如下配置(旧版Startup.cs对应放到ConfigureServices和Configure方法中):
// 加到var builder = WebApplication.CreateBuilder(args);之后 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { var jwtConfig = builder.Configuration.GetSection("JwtTokenConfig").Get<JwtTokenConfig>(); options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, // 必须开启令牌有效期校验 ValidateIssuerSigningKey = true, ValidIssuer = jwtConfig.Issuer, ValidAudience = jwtConfig.Audience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.Secret)), // 允许的服务器时间偏移 ClockSkew = TimeSpan.FromSeconds(30) }; }); // 加到var app = builder.Build();之后,app.MapControllers()之前 app.UseAuthentication(); app.UseAuthorization();
第二步:改造业务接口
给需要授权的接口添加[Authorize]特性,直接从上下文获取用户ID,无需前端传参:
[HttpPost("create")] [Authorize] // 加了这个特性,请求必须带有效JWT才能访问 public async Task<IActionResult> CreateComment([FromBody] CreateCommentDto dto) { // 直接从当前用户Claims拿ID,不需要前端传UserId var userId = User.FindFirstValue("id"); var comment = new Comment { Content = dto.Content, PostId = dto.PostId, UserId = userId }; // 保存到数据库逻辑 return Ok(); } // 对应CreateCommentDto定义 public class CreateCommentDto { public string Content { get; set; } public int PostId { get; set; } }
你原来的/api/AppUsers/user接口完全可以删除,不需要再使用。
2. 前端改造(移除多余请求,自动加认证头)
第一步:封装通用请求工具
新建request.js,自动携带JWT令牌:
const baseUrl = 'https://localhost:44366/api'; export default async function request(url, options = {}) { const jwt = localStorage.getItem('jwt'); const headers = { 'Content-Type': 'application/json', ...options.headers }; // 有令牌就自动加Authorization头 if (jwt) { headers['Authorization'] = `Bearer ${jwt}`; } const response = await fetch(`${baseUrl}/${url}`, { ...options, headers, credentials: 'include' }); return response.json(); }
第二步:改造评论提交逻辑
删除多余的获取用户ID请求,直接提交评论:
const submitComment = async (e, postId) => { e.preventDefault(); const data = { Content: text, PostId: postId }; // 用封装的request方法,自动带认证头 await request('Comments/create', { method: 'POST', body: JSON.stringify(data) }).catch(err => console.error('Error:', err)); setText(''); }
注意事项
- 你当前写的
GetPrincipalFromToken方法中关闭了ValidateLifetime,仅在刷新令牌的场景下可以这么用,正常接口校验必须开启有效期,防止过期令牌被滥用。 - 建议补充令牌过期刷新逻辑:当接口返回401状态码时,调用刷新令牌接口拿新的Access Token,重试原请求,避免用户频繁重新登录。
- JWT存储在localStorage存在XSS攻击风险,高安全要求的场景可以改为存储在带HttpOnly属性的Cookie中。
内容的提问来源于stack exchange,提问作者Kristiyann
相关产品推荐
相关产品推荐

