You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core API特定路由忽略JWT令牌过期的实现咨询

解决方案:让/refresh路由忽略JWT过期校验并从请求头获取令牌

当然可以实现你的需求!你现在的思路完全没问题——我们只需要调整两个关键点:从请求头获取过期JWT,以及让这个特定路由跳过令牌过期校验,同时保证令牌的签名有效性(防止伪造)。下面是两种具体的实现方案,你可以根据自己的代码风格选择:

方案1:手动解析令牌时忽略过期校验(适配你当前的代码结构)

这种方式不需要修改全局认证配置,直接在refresh接口里手动处理令牌解析,通过配置TokenValidationParameters来跳过过期校验,同时从请求头获取令牌:

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

[AllowAnonymous]
[HttpPost("refresh")]
public async Task<IActionResult> Refresh([FromBody] RefreshRequest refreshRequest)
{
    // 从Authorization请求头提取JWT令牌(处理Bearer前缀)
    var authHeader = Request.Headers["Authorization"].FirstOrDefault();
    if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
    {
        return BadRequest("请求头中未提供有效的JWT令牌");
    }
    var expiredToken = authHeader.Substring("Bearer ".Length);

    // 第一步:校验刷新令牌的有效性
    if (!_tokenService.RefreshTokenValid(refreshRequest.Refresh_Token))
    {
        return BadRequest("无效的刷新令牌");
    }

    var tokenHandler = new JwtSecurityTokenHandler();
    // 配置验证参数:忽略过期,但校验签名、发行者、受众(核心是防止伪造)
    var validationParams = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidIssuer = _configuration["Jwt:Issuer"], // 替换成你的JWT发行者配置
        ValidAudience = _configuration["Jwt:Audience"], // 替换成你的JWT受众配置
        ValidateLifetime = false, // 关键:跳过过期时间校验
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"])) // 你的签名密钥
    };

    try
    {
        // 解析令牌:即使过期也能获取用户Claims
        var claimsPrincipal = tokenHandler.ValidateToken(expiredToken, validationParams, out _);
        var userId = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        
        if (string.IsNullOrEmpty(userId))
        {
            return BadRequest("无法从令牌中识别用户");
        }

        // 第二步:根据用户ID获取用户信息
        var repoUser = await _repo.GetUserById(int.Parse(userId));
        if (repoUser == null)
        {
            return NotFound("用户不存在");
        }

        // 第三步:生成新的JWT和刷新令牌(建议同时更新刷新令牌,避免重复使用)
        var newToken = _tokenService.GenerateRefreshedJwtToken(repoUser);
        var newRefreshToken = _tokenService.GenerateNewRefreshToken(); // 假设你有生成新刷新令牌的方法
        
        // 更新数据库中的刷新令牌(旧的刷新令牌失效)
        _tokenService.UpdateRefreshToken(repoUser.Id, newRefreshToken);
        await _repo.SaveChangesAsync();

        var tokenUser = _mapper.Map(repoUser, new AuthRefreshedTokenUser(newToken) { Refresh_Token = newRefreshToken });
        return Ok(tokenUser);
    }
    catch (SecurityTokenException ex)
    {
        return BadRequest($"令牌解析失败:{ex.Message}");
    }
}

方案2:配置专属认证方案(更优雅的全局配置方式)

如果想让中间件自动处理令牌解析,可以给/refresh路由配置一个专属的JWT认证方案,跳过过期校验,这样代码会更简洁:

第一步:在Program.cs/Startup.cs中添加专属认证方案

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    // 默认的JWT认证方案(校验过期,用于其他接口)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            ValidateLifetime = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"]))
        };
    })
    // 为/refresh路由添加忽略过期的认证方案
    .AddJwtBearer("IgnoreExpiredJwt", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            ValidateLifetime = false, // 忽略过期校验
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"]))
        };
    });

第二步:修改refresh接口,使用专属认证方案

// 使用专属认证方案,不需要[AllowAnonymous],因为中间件会处理令牌解析
[HttpPost("refresh")]
[Authorize(AuthenticationSchemes = "IgnoreExpiredJwt")]
public async Task<IActionResult> Refresh([FromBody] RefreshRequest refreshRequest)
{
    // 校验刷新令牌
    if (!_tokenService.RefreshTokenValid(refreshRequest.Refresh_Token))
    {
        return BadRequest("无效的刷新令牌");
    }

    // 直接从User.Claims获取用户ID(中间件已解析令牌)
    var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (string.IsNullOrEmpty(userId))
    {
        return BadRequest("无法识别用户");
    }

    var repoUser = await _repo.GetUserById(int.Parse(userId));
    if (repoUser == null)
    {
        return NotFound("用户不存在");
    }

    // 生成新令牌和刷新令牌
    var newToken = _tokenService.GenerateRefreshedJwtToken(repoUser);
    var newRefreshToken = _tokenService.GenerateNewRefreshToken();
    _tokenService.UpdateRefreshToken(repoUser.Id, newRefreshToken);
    await _repo.SaveChangesAsync();

    var tokenUser = _mapper.Map(repoUser, new AuthRefreshedTokenUser(newToken) { Refresh_Token = newRefreshToken });
    return Ok(tokenUser);
}

重要注意事项

  • 一定要校验刷新令牌:刷新令牌是核心安全屏障,必须确保它和用户绑定、未被篡改、未过期,并且最好是一次性使用(用完就更新为新的)。
  • 不要完全跳过所有校验:即使忽略过期,也要校验令牌的签名、发行者和受众,防止伪造的令牌被用来刷新。
  • 更新刷新令牌:每次刷新JWT时,建议生成新的刷新令牌并替换数据库中的旧值,避免旧的刷新令牌被滥用。

内容的提问来源于stack exchange,提问作者Roy vd Wijk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 21:22:47