专栏名称: dotNET跨平台
专注于.NET Core的技术传播。在这里你可以谈微软.NET,Mono的跨平台开发技术。在这里可以让你的.NET项目有新的思路,不局限于微软的技术栈,横跨Windows,Linux 主流平台
目录
相关文章推荐
程序员的那些事  ·  热搜第一!DeepSeek百万年薪招AI人才 ... ·  昨天  
程序员的那些事  ·  美国人下载 DeepSeek,最高判 20 ... ·  3 天前  
程序员小灰  ·  如何用DeepSeek来变现?90%的人都不知道 ·  3 天前  
OSC开源社区  ·  萨姆·奥特曼:OpenAI在开源问题上一直处 ... ·  5 天前  
程序员的那些事  ·  奥特曼被逼急:深夜上线 ... ·  5 天前  
51好读  ›  专栏  ›  dotNET跨平台

ASP.NET Core中 JWT 实现无感刷新Token

dotNET跨平台  · 公众号  ·  · 2025-01-18 08:00

正文

在 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');






请到「今天看啥」查看全文