OpenIddict 2.0与.NET 6登出时无法失效Access/Refresh Token求助
解决方案
一、先把RevokeAsync的用法改对
你之前的代码根本没关联到实际存在的令牌,直接new空对象调用RevokeAsync肯定没用。正确的操作是先找到用户对应的有效令牌,再执行撤销:
// 获取当前登录用户的ID var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userId == null) return; // 拉取该用户所有未被撤销的Access/Refresh Token var tokens = await _tokenManager.FindBySubjectAsync(userId); foreach (var token in tokens.Where(t => t.Status != OpenIddictConstants.Statuses.Revoked)) { await _tokenManager.RevokeAsync(token); }
这样才能真正把数据库里的令牌状态改成Revoked。
二、让令牌验证配置真正生效
你配置了EnableTokenEntryValidation但没起作用,大概率是验证中间件的配置顺序错了,或者没关联正确的存储。检查这几点:
- 验证配置必须和服务器端用同一个DbContext,确保API能读到数据库里的令牌状态。
- 中间件顺序要对,
AddOpenIddict().AddValidation()的配置要放在AddAuthorization()之前。
修正后的Startup配置示例:
services.AddOpenIddict() .AddValidation(options => { options.UseLocalServer(); options.UseAspNetCore(); // 必须指定和服务器端相同的DbContext options.UseEntityFrameworkCore().UseDbContext<YourDbContext>(); options.EnableTokenEntryValidation(); options.EnableAuthorizationEntryValidation(); });
注意:开启这个选项会让每次API请求都查数据库,性能会受影响,高并发场景建议结合下面的黑名单方案。
三、JWT即时失效的高效方案:令牌黑名单
JWT是自包含令牌,本身没法主动失效,除非等它自己过期。如果要高效实现即时失效,用黑名单是最优解:
- 登出时,把要撤销的Access Token的
jti(JWT唯一ID)存入Redis或内存缓存,过期时间设置成和令牌本身的过期时间一致。 - 在API的授权过滤器里,解析JWT的
jti,检查是否在黑名单里,在的话直接拒绝访问。
示例代码:
// 登出时添加到黑名单 var jwtToken = await _tokenManager.FindByIdAsync(targetTokenId); if (jwtToken != null && jwtToken.Payload.TryGetValue("jti", out var jti)) { var expiryTime = jwtToken.ExpiresUtc?.DateTime ?? DateTime.UtcNow.AddMinutes(30); await _distributedCache.SetStringAsync($"blacklist:{jti}", "revoked", new DistributedCacheEntryOptions { AbsoluteExpiration = expiryTime }); } // 自定义授权过滤器检查黑名单 public class JwtBlacklistFilter : IAsyncAuthorizationFilter { private readonly IDistributedCache _cache; public JwtBlacklistFilter(IDistributedCache cache) { _cache = cache; } public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { var authHeader = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault(); if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ")) return; var token = authHeader.Split(" ").Last(); var handler = new JwtSecurityTokenHandler(); if (handler.ReadToken(token) is JwtSecurityToken jwtToken) { var jti = jwtToken.Claims.FirstOrDefault(c => c.Type == "jti")?.Value; if (!string.IsNullOrEmpty(jti) && await _cache.GetStringAsync($"blacklist:{jti}") != null) { context.Result = new UnauthorizedResult(); } } } }
四、关于.NET Core登出失效Access Token的通用方法
.NET Core本身没有内置的Access Token即时失效机制——这是JWT无状态特性决定的。通用方案就两种:
- 令牌状态校验:每次请求去数据库查令牌是否有效(比如OpenIddict的
EnableTokenEntryValidation),缺点是性能损耗大。 - 黑名单机制:用缓存存已撤销的令牌ID,验证时查黑名单,性能更好,适合高并发场景。
内容的提问来源于stack exchange,提问作者Abhirup Mukherjee
相关产品推荐
相关产品推荐

