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

IdentityServer4 PersistedGrantStore:刷新令牌存储异常排查

解决IdentityServer4中Refresh Token未存储导致刷新失败的问题

看起来你遇到的核心问题是:Refresh Token虽然被返回给客户端,但没有正确存入你的自定义IPersistedGrantStore,导致刷新时系统找不到对应的记录。结合日志信息,我帮你梳理下关键排查点和修复方案:

1. 先明确IdentityServer4存储Refresh Token的逻辑

IdentityServer4中,Refresh Token是作为PersistedGrant对象存储的,它的Type字段值为"refresh_token"。特别要注意的是:

  • DefaultRefreshTokenStore会对原始的Refresh Token值做SHA-256哈希处理,将哈希结果作为PersistedGrant的Key
  • 原始的Refresh Token字符串会存在PersistedGrant的Data字段中
  • 查询Refresh Token时,系统会把客户端传入的Refresh Token值哈希后,用这个哈希值去匹配PersistedGrant的Key

你的日志里显示:

Grant retrieved: /jjI7S51px7ApIt+zVxiNDOUe5Y16kYfNTSp2f37WnU=
DefaultRefreshTokenStore -> refresh_token grant with value: 4cadcc4c0d5b7e77e78e353a773c838d7f66e48507ea5303f9349c86555ed896 not found in store.

这说明系统用哈希后的Refresh Token值(4cadcc4c0d5b7e77e78e353a773c838d7f66e48507ea5303f9349c86555ed896)去查询,但你的数据库里没有这个Key的记录。

2. 排查你的IPersistedGrantStore实现

检查StoreAsync方法

你需要确保这个方法没有过滤掉Type = "refresh_token"的PersistedGrant对象。很多人会不小心只处理access_token类型,导致Refresh Token被遗漏。

错误示例(过滤了refresh_token):

public async Task StoreAsync(PersistedGrant grant)
{
    // 错误:只存储access token,忽略了refresh token
    if (grant.Type != "access_token") return;
    
    // 存入数据库的逻辑...
}

正确实现应该完整存储所有类型的PersistedGrant:

public async Task StoreAsync(PersistedGrant grant)
{
    // 直接将传入的grant对象完整存入数据库,不要做类型过滤
    var existing = await _context.PersistedGrants.FirstOrDefaultAsync(g => g.Key == grant.Key);
    if (existing != null)
    {
        _context.PersistedGrants.Remove(existing);
    }
    _context.PersistedGrants.Add(grant);
    await _context.SaveChangesAsync();
}

检查GetAsync方法

确保这个方法是根据传入的key参数(也就是哈希后的Refresh Token值)去精确匹配PersistedGrant的Key字段,而不是去匹配Data字段(原始Refresh Token)。

正确示例:

public async Task<PersistedGrant> GetAsync(string key)
{
    // 根据key(哈希值)查询对应的PersistedGrant
    return await _context.PersistedGrants.FirstOrDefaultAsync(g => g.Key == key);
}

3. 验证数据库记录

直接查询你的PersistedGrants表,看看是否存在Type = "refresh_token"的记录:

  • 如果没有:说明StoreAsync方法根本没存储Refresh Token,回到第一步检查是否有类型过滤逻辑
  • 如果有:对比记录的Key是否和日志中找不到的那个哈希值一致,如果不一致,说明你在存储时篡改了Key值,要确保完全保留IdentityServer传入的PersistedGrant对象的所有字段

4. 额外确认客户端配置

虽然你已经拿到了Refresh Token,但还是可以确认下客户端的AllowOfflineAccess是否设置为true,这是返回Refresh Token的前提条件:

new Client
{
    ClientId = "your-client-id",
    // 其他配置...
    AllowOfflineAccess = true,
    AllowedScopes = { "openid", "profile", "api1", IdentityServerConstants.StandardScopes.OfflineAccess }
}

按照以上步骤排查,应该能解决Refresh Token未存储导致的刷新失败问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:29:51