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

.NET Core 3.1中如何实现JWT存Cookie的登出方法?

JWT存Cookie场景下的登出实现方案

JWT本身是无状态令牌,一旦签发无法在服务端直接作废,所以登出的核心是让客户端停止携带有效令牌,同时可通过服务端黑名单机制进一步提升安全性,以下是具体实现步骤:

1. 客户端清除Cookie(必备步骤)

登出的核心操作是删除存储JWT的Cookie,确保后续请求不再携带有效令牌。注意Cookie的名称和配置要和登录时完全匹配,否则可能清除失败。

基础清除代码

[HttpPost("logout")]
public IActionResult Logout()
{
    // 清除存储JWT的Cookie,名称需与登录时一致
    Response.Cookies.Delete("JwtToken");
    
    // 如果使用了Refresh Token,同步清除对应的Cookie
    Response.Cookies.Delete("RefreshToken");
    
    return Ok("登出成功");
}

带配置匹配的清除(针对有特殊Cookie配置的场景)

如果登录时设置了Domain、Path等属性,删除时必须指定相同参数:

Response.Cookies.Delete("JwtToken", new CookieOptions
{
    Domain = "your-domain.com",
    Path = "/",
    SameSite = SameSiteMode.Strict
});

2. 服务端Token黑名单机制(可选,增强安全性)

如果担心已签发的JWT在过期前被滥用,可以实现黑名单存储(推荐用Redis,内存缓存仅适合单实例场景),登出时将令牌加入黑名单,后续认证时校验。

步骤1:配置缓存服务(以Redis为例)

在Program.cs中添加Redis缓存配置:

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
});

步骤2:登出时将Token加入黑名单

private readonly IDistributedCache _distributedCache;

public AuthController(IDistributedCache distributedCache)
{
    _distributedCache = distributedCache;
}

[HttpPost("logout")]
public async Task<IActionResult> Logout()
{
    var jwtToken = Request.Cookies["JwtToken"];
    if (!string.IsNullOrEmpty(jwtToken))
    {
        // 解析Token获取过期时间,设置黑名单有效期为Token剩余时长
        var tokenHandler = new JwtSecurityTokenHandler();
        var jwtSecurityToken = tokenHandler.ReadJwtToken(jwtToken);
        var expiration = jwtSecurityToken.ValidTo;
        
        await _distributedCache.SetStringAsync(
            $"Blacklist:{jwtToken}", 
            "invalid", 
            new DistributedCacheEntryOptions { AbsoluteExpiration = expiration }
        );
    }
    
    // 清除Cookie
    Response.Cookies.Delete("JwtToken");
    return Ok("登出成功");
}

步骤3:自定义校验逻辑验证黑名单

创建自定义JWT验证处理器,在默认校验前检查令牌是否在黑名单:

public class JwtBlacklistValidator : ISecurityTokenValidator
{
    private readonly JwtSecurityTokenHandler _innerHandler = new();
    private readonly IDistributedCache _cache;

    public JwtBlacklistValidator(IDistributedCache cache)
    {
        _cache = cache;
    }

    public bool CanValidateToken => true;
    public int MaximumTokenSizeInBytes => _innerHandler.MaximumTokenSizeInBytes;
    public bool CanReadToken(string securityToken) => _innerHandler.CanReadToken(securityToken);

    public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
    {
        // 先检查黑名单,存在则抛出异常
        if (_cache.GetString($"Blacklist:{securityToken}") != null)
        {
            throw new SecurityTokenInvalidException("令牌已失效");
        }
        
        // 执行默认JWT校验逻辑
        return _innerHandler.ValidateToken(securityToken, validationParameters, out validatedToken);
    }
}

然后在Program.cs中替换默认的JWT验证器:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        var jwtSettings = builder.Configuration.GetSection("JwtSettings");
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = jwtSettings["Issuer"],
            ValidateAudience = true,
            ValidAudience = jwtSettings["Audience"],
            ValidateLifetime = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]))
        };

        // 替换为自定义黑名单验证器
        options.SecurityTokenValidators.Clear();
        options.SecurityTokenValidators.Add(new JwtBlacklistValidator(builder.Services.BuildServiceProvider().GetRequiredService<IDistributedCache>()));
    });

3. 登录时的Cookie配置注意事项

为了安全,登录时设置Cookie需开启HttpOnly(防止XSS窃取)和Secure(生产环境仅HTTPS传输):

[HttpPost("login")]
public IActionResult Login(LoginRequest model)
{
    // 用户验证逻辑...
    
    // 生成JWT
    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecretKey"]);
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.Username) }),
        Expires = DateTime.UtcNow.AddHours(1),
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
    };
    var token = tokenHandler.CreateToken(tokenDescriptor);
    var tokenString = tokenHandler.WriteToken(token);
    
    // 写入Cookie
    Response.Cookies.Append("JwtToken", tokenString, new CookieOptions
    {
        HttpOnly = true,
        Secure = Environment.IsProduction(),
        SameSite = SameSiteMode.Strict,
        Expires = DateTime.UtcNow.AddHours(1)
    });
    
    return Ok("登录成功");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 02:18:20