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. 1. 创建中间件:创建一个中间件,用于检查每个请求的JWT Token过期时间。

  2. 2. 读取Token:中间件读取请求头中的Authorization字段,获取JWT Token。

  3. 3. 检查过期时间:判断Token的过期时间,如果距离过期时间在一定范围内(如30分钟内),则生成一个新的JWT Token,并通过自定义的响应头(如X-Refresh-Token)返回给客户端。

  4. 4. 客户端更新Token:客户端检查响应头,如果存在X-Refresh-Token,则用新Token替换旧Token,并在后续请求中使用新Token。

  5. 5. 弊端:虽然实现起来相对简单,但安全性相对较低,因为Token的刷新是在客户端自动进行的,如果Token被盗用,攻击者可能会在Token过期前一直使用该Token

因为存在上述弊端,不推荐使用中间件自动刷新 Token,故不提供相关代码实现。

<think>好的,我现在需要回答用户关于JWT鉴权实现方法、示例和教程的问题。首先,我需要回忆JWT的基本概念和组成部分。JWT由头部、载荷和签名三部分组成,使用点号分隔。头部通常包含算法和类型,比如HS256和JWT类型。载荷包含声明,比如用户信息和过期时间。签名部分用于验证令牌的完整性。 接下来,用户需要具体的实现步骤和示例。我应该分步骤说明,从生成密钥、创建令牌、发送令牌到验证令牌的流程。需要提到常用的库,比如Java中的java-jwt,Python中的PyJWT,或者Node.js的jsonwebtoken。同时,结合用户提供的引用内容,特别是引用[3]提到的头部信息和算法声明,以及引用[2]中的验证代码示例。 然后,示例部分需要具体代码,比如使用Java生成和验证JWT,或者Python的例子。要确保代码语法正确,并解释关键部分,比如签名算法HMAC256的使用,密钥的管理,以及如何处理过期时间。还要注意安全性问题,比如密钥不能硬编码在代码中,应该使用环境变量或配置管理工具。 用户引用的资料中提到了ASP.NET CoreJWT配置,所以在可能的扩展部分可以提到不同框架的实现差异,但用户主要需要通用的实现方法,所以可能需要简要提及不同平台的库,但重点放在通用步骤上。 另外,需要指出常见问题,如令牌过期处理、签名验证失败的原因,以及如何防范令牌被篡改。同时,引用[1]提到JWT包含解析和验证的功能,所以需要强调验证步骤的重要性,比如解码和签名校验。 最后,生成相关问题,可能涉及安全优化、与其他鉴权方式的对比、刷新令牌机制等。确保问题覆盖用户可能感兴趣的扩展话题。</think>### JWT鉴权实现方法及示例 #### 一、JWT基础结构 JWT(JSON Web Token)由三部分组成,格式为`Header.Payload.Signature`,各部分通过Base64URL编码后用点号连接。例如: $$Header = \{"alg":"HS256","typ":"JWT"\}$$ $$Payload = \{"sub":"user123","exp":1717020000\}$$ #### 二、实现步骤 1. **生成密钥** 使用安全随机字符串(如HMAC-SHA256算法需至少32字节密钥),推荐通过环境变量或专用配置工具管理密钥[^1]。 2. **创建令牌** **Java示例(使用java-jwt库)**: ```java String token = JWT.create() .withClaim("userId", 123) .withExpiresAt(new Date(System.currentTimeMillis() + 3600_000)) .sign(Algorithm.HMAC256(ConfigUtil.sign)); // ConfigUtil.sign为密钥 ``` **Python示例(使用PyJWT)**: ```python import jwt token = jwt.encode( {"user_id": 123, "exp": datetime.utcnow() + timedelta(hours=1)}, "your-secret-key", # 从安全配置读取 algorithm="HS256" ) ``` 3. **发送令牌** 客户端需在请求头中携带令牌: ``` Authorization: Bearer <token> ``` 4. **验证令牌** **Java验证逻辑**: ```java DecodedJWT decoded = JWT.require(Algorithm.HMAC256(ConfigUtil.sign)) .build() .verify(token); // 自动验证签名和过期时间 String userId = decoded.getClaim("userId").asString(); ``` **Python验证逻辑**: ```python try: payload = jwt.decode(token, "your-secret-key", algorithms=["HS256"]) except jwt.ExpiredSignatureError: print("令牌已过期") ``` #### 三、安全注意事项 1. **密钥管理**:禁止硬编码密钥,应使用密钥管理系统(如Vault)或环境变量[^1] 2. **令牌过期**:建议设置较短有效期(如1小时),结合刷新令牌机制 3. **算法选择**:优先使用HMAC-SHA256或RS256等强加密算法[^3] #### 四、扩展应用场景 - 微服务间的身份传递 - 单点登录(SSO)系统 - 移动APP无状态认证
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值