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

.NET Core中如何无异常优雅处理过期JWT令牌?

.NET Core JWT过期令牌异常原因及优雅处理方案

我在.NET Core中开发JWT令牌的生成与验证功能,使用的令牌生成代码如下:

string GenerateToken()
{
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(mySecret));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
    var claims = new Claim[] { 
        new(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()),
    };

    var tokenHandler = new JwtSecurityTokenHandler();

    var tokenDescriptor = new SecurityTokenDescriptor()
    {
        Subject = new ClaimsIdentity(claims),
        Expires = DateTime.UtcNow.AddSeconds(10),
        SigningCredentials = credentials,
        Audience = "audience",
        Issuer = "issuer"
    };

    var securityToken = tokenHandler.CreateToken(tokenDescriptor);
    var token = tokenHandler.WriteToken(securityToken);

    return token;
}

令牌验证代码:

void ValidateToken(string token)
{
    JwtSecurityTokenHandler validationHandler = new();
    var claimsPrincipal = validationHandler.ValidateToken(token, new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = "issuer",
        ValidAudience = "audience",
        IssuerSigningKey = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(mySecret)),
        ClockSkew = TimeSpan.Zero
    }, out SecurityToken securityToken);
}

当令牌过期时,会抛出如下异常:

IDX10223: Lifetime validation failed. The token is expired. ValidTo
(UTC): '6/9/2024 6:21:36 AM', Current time (UTC): '6/9/2024 6:21:38
AM'.

该问题在控制台应用和使用Microsoft.AspNetCore.Authentication.JwtBearer包的Web API中都会出现,Web API会返回401状态码。


一、异常抛出原因

  1. 生命周期验证强制开启:验证代码中设置了ValidateLifetime = true,会严格检查令牌的有效期,当令牌的ValidTo时间早于当前UTC时间时,直接触发生命周期验证失败。
  2. 无时钟偏差容忍:配置了ClockSkew = TimeSpan.Zero,默认JWT验证会允许几分钟的时钟偏差(避免服务器时间不一致导致的误判),但这里关闭了该容忍机制,令牌过期哪怕一秒都会立即抛出异常。
  3. Web API中间件默认行为:使用JwtBearer中间件时,框架会自动执行令牌验证流程,验证失败(包括过期)会直接抛出异常,并返回401未授权状态码。

二、优雅处理过期令牌的方案

1. 控制台应用(手动验证场景)

通过try-catch精准捕获SecurityTokenExpiredException,针对性处理过期情况,避免异常扩散:

void ValidateToken(string token)
{
    JwtSecurityTokenHandler validationHandler = new();
    try
    {
        var claimsPrincipal = validationHandler.ValidateToken(token, new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "issuer",
            ValidAudience = "audience",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(mySecret)),
            ClockSkew = TimeSpan.Zero
        }, out SecurityToken securityToken);
        
        // 验证成功后的业务逻辑
    }
    catch (SecurityTokenExpiredException)
    {
        // 令牌过期专属处理逻辑
        Console.WriteLine("令牌已过期,请重新获取");
    }
    catch (Exception ex)
    {
        // 处理其他验证失败场景(如签名无效、受众/发行者不匹配等)
        Console.WriteLine($"令牌验证失败:{ex.Message}");
    }
}

2. Web API场景(使用JwtBearer中间件)

方案一:通过JwtBearer事件自定义过期响应

在配置JwtBearer中间件时,注册OnAuthenticationFailed事件,捕获过期异常并返回友好格式化响应:

// 在Program.cs中配置认证服务
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "issuer",
            ValidAudience = "audience",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(mySecret)),
            ClockSkew = TimeSpan.Zero
        };

        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                if (context.Exception is SecurityTokenExpiredException)
                {
                    context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                    context.Response.ContentType = "application/json";
                    var response = new { Message = "令牌已过期,请重新登录" };
                    return context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
                }
                // 其他验证异常处理
                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                return context.Response.WriteAsync("令牌验证失败");
            }
        };
    });

方案二:自定义全局异常过滤器

创建全局异常过滤器,统一捕获SecurityTokenExpiredException并返回标准化响应:

public class JwtExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        if (context.Exception is SecurityTokenExpiredException)
        {
            context.Result = new JsonResult(new
            {
                StatusCode = StatusCodes.Status401Unauthorized,
                Message = "令牌已过期,请重新获取新令牌"
            })
            {
                StatusCode = StatusCodes.Status401Unauthorized
            };
            context.ExceptionHandled = true;
        }
    }
}

// 在Program.cs中注册过滤器
builder.Services.AddControllers(options =>
{
    options.Filters.Add<JwtExceptionFilter>();
});

内容的提问来源于stack exchange,提问作者Md. Mustafizur Rahman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:44:54