ASP.NET Core中 JWT 实现无感刷新Token
在 Web 应用开发中,用户登录状态的管理至关重要。为了避免用户频繁遇到登录过期的问题,我们可以通过实现 JWT(JSON Web Token)刷新机制来提升用户体验
推荐: 使用 Refresh Token(双 Token 机制)
1. 生成和使用双 Token
通常会生成两种 Token:访问 Token (Access Token) 和 刷新 Token (Refresh Token)。
- • 访问 Token:用于客户端与服务器之间的身份验证,有效期较短(例如 30 分钟),以提高安全性。
- • 刷新 Token:用于获取新的访问 Token,有效期较长(例如 7 天),存储在客户端 。
Tokenservice.cs:
// 生成 JWT Access Token 和 Refresh Token public (string AccessToken, string RefreshToken) GenerateTokens(string userId, string userName) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(_configuration["JwtSettings:Secret"]); // 生成 Access Token var accessTokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.NameIdentifier, userId), new Claim(ClaimTypes.Name, userName) }), Issuer = _configuration["JwtSettings:Issuer"], Audience = _configuration["JwtSettings:Audience"], Expires = DateTime.UtcNow.AddMinutes(double.Parse(_configuration["JwtSettings:ExpireMinutes"])), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var accessToken = tokenHandler.CreateToken(accessTokenDescriptor); // 生成 Refresh Token var refreshTokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.NameIdentifier, userId) }), Issuer = _configuration["JwtSettings:Issuer"], Audience = _configuration["JwtSettings:Audience"], Expires = DateTime.UtcNow.AddDays(7), // Refresh Token 有效期 7 天 SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var refreshToken = tokenHandler.CreateToken(refreshTokenDescriptor); return (tokenHandler.WriteToken(accessToken), tokenHandler.WriteToken(refreshToken)); } // 根据旧 JWT 令牌换取新 JWT 令牌 public string ExchangeJwtToken(string oldToken) { if (!ValidateJwtToken(oldToken)) { thrownew InvalidOperationException("Invalid or expired token"); } var principal = ParseJwtToken(oldToken); var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; var userName = principal.FindFirst(ClaimTypes.Name)?.Value; if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(userName)) { thrownew InvalidOperationException("Invalid token claims"); } return GenerateJwtToken(userId, userName); }
AuthController.cs
private readonly TokenService _tokenService; privatereadonly IAppUser _appUser; public AuthController(IConfiguration configuration, IAppUser appUser, TokenService tokenService) { _tokenService = tokenService; _appUser = appUser; } [HttpPost("login")] [AllowAnonymous] public IActionResult Login([FromBody] LoginRequest loginRequest) { var token = _tokenService.GenerateTokens(loginRequest.Username, loginRequest.Username); return Ok(new { token.RefreshToken,token.AccessToken }); }
2. 前端请求拦截器自动刷新 Token
在前端应用中,可以使用请求拦截器来自动处理 Token 刷新逻辑。当访问 Token 过期时,拦截器会自动调用刷新接口获取新的访问 Token,并重新发起请求
// 请求拦截器 axios.interceptors.request.use( config => { const token = localStorage.getItem('accessToken'); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }, error => { returnPromise.reject(error); } ); axios.interceptors.response.use( response => response, async error => { const originalRequest = error.config; if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; const refreshToken = localStorage.getItem('refreshToken'); const res = await axios.post('/api/auth/refresh-token', { refreshToken }); localStorage.setItem('accessToken', res.data.accessToken); returnaxios(originalRequest); } returnPromise.reject(error); } );
3. 后端提供刷新 Token 的接口
专门的接口来处理刷新 Token 的请求。该接口会验证刷新 Token 的有效性,并返回新的访问 Token
// 刷新 Access Token [HttpPost("refresh")] public IActionResult RefreshToken([FromBody] RefreshTokenRequest request) { try { var validatetoken = _tokenService.ValidateJwtToken(request.RefreshToken); if (validatetoken) { return Ok(_tokenService.ExchangeJwtToken(request.RefreshToken)); } return Unauthorized("Invalid refresh token"); } catch (Exception ex) { return Unauthorized("Invalid refresh token"); } }
不推荐:中间件自动刷新Token
- 1. 创建中间件:创建一个中间件,用于检查每个请求的JWT Token过期时间。
- 2. 读取Token:中间件读取请求头中的Authorization字段,获取JWT Token。
- 3. 检查过期时间:判断Token的过期时间,如果距离过期时间在一定范围内(如30分钟内),则生成一个新的JWT Token,并通过自定义的响应头(如X-Refresh-Token)返回给客户端。
- 4. 客户端更新Token:客户端检查响应头,如果存在X-Refresh-Token,则用新Token替换旧Token,并在后续请求中使用新Token。
- 5. 弊端:虽然实现起来相对简单,但安全性相对较低,因为Token的刷新是在客户端自动进行的,如果Token被盗用,攻击者可能会在Token过期前一直使用该Token
因为存在上述弊端,不推荐使用中间件自动刷新 Token,故不提供相关代码实现。
龙腾一族至尊龙骑

浙公网安备 33010602011771号