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

ASP.NET Core 6中JWT令牌自定义生命周期验证与更新方案咨询

问题场景

在ASP.NET Core 6应用中,已配置JWT认证如下:

services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateActor = true,
            ValidateAudience = true,            
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            // ...
        };
    })
;

需求是实现:用户无请求时令牌有效期2小时;用户发起请求时,将令牌有效期更新为当前UTC时间+2小时。计划把令牌和过期时间存储在数据库,每次请求校验并更新未过期令牌的时间。

调研的两种方案存在问题:

  • TokenValidationParameters的LifetimeValidator委托无法注入应用服务(如DbContext),且不适宜在此更新令牌时间;
  • 自定义实现ISecurityTokenValidator的类,其ValidateLifetime方法无法注入服务,依赖注入问题无法解决。

请问如何实现JWT令牌的自定义生命周期验证与更新?


解决方案

可以通过自定义JWT事件处理结合依赖注入的方式实现,核心思路是绕过TokenValidationParameters的局限,在认证流程的合适阶段获取服务并完成校验与更新:

1. 调整JWT认证配置,替换默认生命周期校验

修改AddJwtBearer配置,关闭默认的生命周期校验,注册OnTokenValidated事件——该事件在令牌通过基础签名、受众等校验后触发,且能通过HttpContext获取依赖注入的服务:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateActor = true,
            ValidateAudience = true,
            ValidateLifetime = false, // 关闭默认生命周期校验,改用自定义逻辑
            ValidateIssuerSigningKey = true,
            // 补充配置:ValidIssuer、ValidAudience、IssuerSigningKey等必填项
        };

        // 注册令牌验证后的自定义事件
        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = async context =>
            {
                // 从HttpContext的服务容器中获取DbContext
                var dbContext = context.HttpContext.RequestServices.GetRequiredService<YourDbContext>();
                
                // 从令牌中提取唯一标识jti(需在生成令牌时添加该Claim)
                var tokenId = context.Principal.FindFirstValue(JwtRegisteredClaimNames.Jti);
                if (string.IsNullOrEmpty(tokenId))
                {
                    context.Fail("无效的令牌标识");
                    return;
                }

                // 查询数据库中的令牌记录
                var tokenRecord = await dbContext.TokenRecords
                    .FirstOrDefaultAsync(t => t.TokenId == tokenId);
                if (tokenRecord == null)
                {
                    context.Fail("令牌未在系统中注册");
                    return;
                }

                // 校验令牌是否已过期
                if (tokenRecord.ExpiresAt < DateTime.UtcNow)
                {
                    context.Fail("令牌已过期");
                    return;
                }

                // 更新令牌过期时间为当前UTC+2小时
                tokenRecord.ExpiresAt = DateTime.UtcNow.AddHours(2);
                await dbContext.SaveChangesAsync();
            }
        };
    });

2. 数据库令牌记录表设计

创建存储令牌信息的实体类,用于关联JWT与自定义过期时间:

public class TokenRecord
{
    public int Id { get; set; }
    public string TokenId { get; set; } // 对应JWT的jti声明
    public string UserId { get; set; } // 关联用户ID
    public DateTime ExpiresAt { get; set; } // 自定义的过期时间
    public DateTime CreatedAt { get; set; } // 令牌创建时间
    public byte[] RowVersion { get; set; } // 乐观锁字段,避免并发更新冲突
}

3. 关键注意事项

  • 生成JWT时必须添加jti(JWT唯一ID)声明,用于关联数据库记录;
  • 关闭默认ValidateLifetime后,需确保自定义逻辑覆盖所有过期校验场景,防止非法令牌通过认证;
  • 高并发场景下,建议添加Redis缓存,将令牌的过期时间缓存5-10秒,减少数据库写操作压力;
  • 可通过RowVersion字段实现乐观锁,避免多请求同时更新令牌时间导致的数据冲突。

内容的提问来源于stack exchange,提问作者Volodymyr V.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 16:35:14