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

.Net Core实现JWT令牌内存缓存校验,解决注销后即时失效需求

实现JWT令牌注销后立即失效的高效方案

核心思路

借助JWT验证后的扩展事件,在令牌签名验证通过后,额外检查内存缓存中是否存在当前用户邮箱对应的有效令牌。若缓存中无对应条目或令牌不匹配,则直接拒绝请求,无需在每个API中编写重复检查逻辑。

步骤1:确保JWT包含邮箱声明

创建JWT令牌时,必须将用户邮箱ID作为ClaimTypes.Email声明加入令牌,这样后续才能从令牌中解析出邮箱ID用于缓存校验:

// 创建JWT令牌的示例代码
var claims = new List<Claim>
{
    new Claim(ClaimTypes.Email, ob.MailId), // 关键:添加邮箱声明
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
    // 其他业务声明...
};

var token = new JwtSecurityToken(
    issuer: configuration["Jwt:Issuer"],
    audience: configuration["Jwt:Audience"],
    claims: claims,
    expires: DateTime.Now.AddMinutes(30), // 与缓存过期时间保持一致
    signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"])), SecurityAlgorithms.HmacSha256)
);

// 存入内存缓存,同时设置与JWT一致的过期时间
_memoryCache.Set(ob.MailId, new JwtSecurityTokenHandler().WriteToken(token), new MemoryCacheEntryOptions
{
    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
});

步骤2:配置JWT验证事件添加缓存校验

在Program.cs的JWT认证配置中,添加OnTokenValidated事件处理逻辑,完成缓存校验:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        // 原有JWT验证参数配置(保持不变)
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };

        // 添加令牌验证后的缓存校验逻辑
        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = context =>
            {
                var memoryCache = context.HttpContext.RequestServices.GetRequiredService<IMemoryCache>();
                
                // 从令牌解析邮箱声明
                var emailClaim = context.Principal?.FindFirst(ClaimTypes.Email);
                if (emailClaim == null)
                {
                    context.Fail("缺少邮箱身份声明");
                    return Task.CompletedTask;
                }

                // 获取当前请求的令牌
                var requestToken = context.Request.Headers.Authorization.FirstOrDefault()?.Split(" ").Last();
                if (string.IsNullOrEmpty(requestToken))
                {
                    context.Fail("令牌为空");
                    return Task.CompletedTask;
                }

                // 校验缓存中是否存在匹配的有效令牌
                if (!memoryCache.TryGetValue(emailClaim.Value, out string cachedToken) 
                    || !string.Equals(cachedToken, requestToken, StringComparison.Ordinal))
                {
                    context.Fail("令牌已失效");
                }

                return Task.CompletedTask;
            }
        };
    });

步骤3:注销逻辑实现

用户注销时,只需从内存缓存中移除对应邮箱的令牌条目即可:

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly IMemoryCache _memoryCache;

    public AuthController(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    [HttpPost("logout")]
    [Authorize]
    public IActionResult Logout()
    {
        var email = User.FindFirst(ClaimTypes.Email)?.Value;
        if (!string.IsNullOrEmpty(email))
        {
            _memoryCache.Remove(email);
        }
        return Ok("注销成功");
    }
}

注意事项

  1. 缓存过期对齐:内存缓存的过期时间必须与JWT令牌的过期时间保持一致,避免缓存中残留过期令牌导致无效请求。
  2. 多服务器场景:如果是多服务器部署,内存缓存无法跨进程共享,需替换为分布式缓存(如Redis),确保所有节点的缓存状态一致。
  3. 响应规范:context.Fail()会自动触发ASP.NET Core返回401 Unauthorized响应,符合REST API的标准错误处理,无需手动抛出UnauthorizedAccessException(若强制抛出,可在context.Fail()后抛出,但不推荐破坏默认响应流程)。

全局授权(可选)

如果所有API都需要应用该校验,可全局配置授权策略,无需在每个API上添加[Authorize]特性:

builder.Services.AddControllers(options =>
{
    var authPolicy = new AuthorizationPolicyBuilder()
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser()
        .Build();
    options.Filters.Add(new AuthorizeFilter(authPolicy));
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 09:40:59