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

如何在登出后立即作废Identity Core生成的访问令牌,无需等待到期

.NET5 Identity Core 登出后立即作废AccessToken实现方案

方案1:JWT黑名单机制(可实现立即作废,推荐)

核心逻辑是给每个AccessToken生成唯一标识,登出时将该标识加入黑名单,每次验证token时先校验是否在黑名单内。

实现步骤:

  1. 生成AccessToken时添加JWT唯一标识声明(jti)
    在你的AccessTokenGenerator生成token的逻辑中,新增jti声明:
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
  1. 注册分布式缓存(单机可用内存缓存替代)
// 分布式部署用Redis,单机可使用services.AddMemoryCache();
services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = _configuration.GetConnectionString("RedisConnection");
});
  1. 扩展JWT验证逻辑,增加黑名单校验
    修改你的AddJwtBearer配置,添加OnTokenValidated事件:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters()
    {
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authenticationConfiguration.AccessTokenSecret)),
        ValidIssuer = authenticationConfiguration.Issuer,
        ValidAudience = authenticationConfiguration.Audience,
        ValidateIssuerSigningKey = true,
        ValidateIssuer = true,
        ValidateAudience = true,
        ClockSkew = TimeSpan.Zero
    };
    // 新增黑名单校验逻辑
    o.Events = new JwtBearerEvents
    {
        OnTokenValidated = async context =>
        {
            // 获取当前token的唯一标识
            var jti = context.Principal.FindFirstValue(JwtRegisteredClaimNames.Jti);
            var cache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
            // 校验是否在黑名单中
            var isBlacklisted = await cache.GetStringAsync($"jwt_blacklist:{jti}");
            if (!string.IsNullOrEmpty(isBlacklisted))
            {
                context.Fail("当前凭证已作废");
            }
        }
    };
});
  1. 登出接口新增黑名单写入逻辑
    登出时除了删除刷新令牌,将当前请求的AccessToken对应的jti写入缓存,缓存过期时间设置为AccessToken的剩余有效期,避免缓存冗余:
// 示例登出接口逻辑
[HttpPost("logout")]
[Authorize]
public async Task<IActionResult> Logout()
{
    var jti = User.FindFirstValue(JwtRegisteredClaimNames.Jti);
    var accessTokenExpireTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Exp)));
    // 写入黑名单,过期时间和token有效期一致
    await _distributedCache.SetStringAsync($"jwt_blacklist:{jti}", "1", new DistributedCacheEntryOptions
    {
        AbsoluteExpiration = accessTokenExpireTime
    });
    // 原有删除刷新令牌逻辑...
    return Ok();
}

方案2:缩短AccessToken有效期(改造成本最低)

如果对即时作废的要求没有极高,可将AccessToken有效期设置为3-5分钟,配合刷新令牌实现用户无感续期。此时即使登出后未作废AccessToken,最多也只能使用3-5分钟,风险窗口极小,几乎不需要修改现有逻辑。

方案3:SecurityStamp校验(适配多场景)

Identity Core自带SecurityStamp字段,可用于校验用户身份状态是否变更(登出、改密码、权限变更等):

  1. 生成AccessToken时添加SecurityStamp声明
new Claim("SecurityStamp", user.SecurityStamp.ToString())
  1. JWT验证时比对SecurityStamp
OnTokenValidated = async context =>
{
    var userId = context.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
    var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<User>>();
    var user = await userManager.FindByIdAsync(userId);
    var stampInToken = context.Principal.FindFirstValue("SecurityStamp");
    if (user == null || stampInToken != user.SecurityStamp)
    {
        context.Fail("当前凭证已作废");
    }
}
  1. 登出时更新用户的SecurityStamp
await _userManager.UpdateSecurityStampAsync(user);

该方案的优势是可以一次性覆盖登出、改密码、禁用用户等所有需要作废旧token的场景,缺点是每次验证需要查询数据库,性能略低。

内容的提问来源于stack exchange,提问作者Federico Alberti

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 14:54:03