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

ServiceStack OAuth场景下JWT刷新令牌用会话ID,如何映射至用户认证ID?

解决方案

1. 自定义JwtAuthProvider替换刷新令牌的sub声明

直接继承JwtAuthProvider并重写CreateJwtRefreshToken方法,将刷新令牌的主题(sub)设置为用户认证ID而非会话ID:

public class CustomJwtAuthProvider : JwtAuthProvider
{
    protected override string CreateJwtRefreshToken(IRequest req, string subject, TimeSpan expiresIn)
    {
        // 从当前会话中获取用户认证ID,替换原有的会话ID作为sub
        var userAuthId = req.GetSession().UserAuthId;
        return base.CreateJwtRefreshToken(req, userAuthId, expiresIn);
    }
}

之后在AppHost的配置中注册这个自定义Provider:

Plugins.Add(new AuthFeature(() => new CustomUserSession(),
    new IAuthProvider[] {
        new CustomJwtAuthProvider 
        { 
            // 配置你的JWT参数,比如AuthKey、ExpireTokensIn等
        },
        new YourOAuthProvider(...), // 注册你的OAuth提供程序
    }));

2. 存储会话ID与用户认证ID的映射关系

如果无法修改刷新令牌的sub,可以在会话创建时保存两者的映射,后续通过会话ID反向查询用户认证ID:

  • 在OAuth Provider的OnAuthenticatedAsync事件中保存映射:
public class CustomOAuthProvider : OAuthProvider
{
    public override async Task OnAuthenticatedAsync(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
    {
        await base.OnAuthenticatedAsync(authService, session, tokens, authInfo);
        // 用缓存存储会话ID到UserAuthId的映射,过期时间与会话一致
        var cache = authService.TryResolve<ICacheClient>();
        await cache.SetAsync($"SessionToUser:{session.Id}", session.UserAuthId, session.ExpiresAt);
    }
}
  • 在IUserSessionSourceAsync实现中处理映射查询:
public class CustomUserSessionSource : IUserSessionSourceAsync
{
    private readonly ICacheClient _cache;

    public CustomUserSessionSource(ICacheClient cache) => _cache = cache;

    public async Task<IUserSession> GetUserSessionAsync(string userAuthIdOrSessionId, IRequest req)
    {
        // 先尝试直接用传入ID作为UserAuthId查找会话
        var session = await FetchSessionByUserAuthId(userAuthIdOrSessionId);
        if (session != null)
            return session;

        // 若找不到,尝试将传入ID作为会话ID,查询映射的UserAuthId
        var mappedUserAuthId = await _cache.GetAsync<string>($"SessionToUser:{userAuthIdOrSessionId}");
        if (mappedUserAuthId != null)
            return await FetchSessionByUserAuthId(mappedUserAuthId);

        return null;
    }

    // 实现根据UserAuthId获取会话的业务逻辑
    private async Task<IUserSession> FetchSessionByUserAuthId(string userAuthId)
    {
        // 示例:从数据库或缓存中加载用户会话
        // var session = await Db.SingleByIdAsync<CustomUserSession>(userAuthId);
        // return session;
        ...
    }
}

3. 直接通过会话ID查询会话实例

如果使用ServiceStack内置的会话存储(如Redis、内存缓存),可以直接通过会话ID获取完整会话,再从中提取用户认证ID:

public class CustomUserSessionSource : IUserSessionSourceAsync
{
    private readonly ISessionFactory _sessionFactory;

    public CustomUserSessionSource(ISessionFactory sessionFactory) => _sessionFactory = sessionFactory;

    public async Task<IUserSession> GetUserSessionAsync(string userAuthIdOrSessionId, IRequest req)
    {
        // 优先尝试用UserAuthId查找
        var session = await FetchSessionByUserAuthId(userAuthIdOrSessionId);
        if (session != null)
            return session;

        // 尝试用会话ID获取会话实例,再提取UserAuthId查询
        var sessionFromId = await _sessionFactory.GetSessionAsync(req, userAuthIdOrSessionId);
        if (sessionFromId != null)
            return await FetchSessionByUserAuthId(sessionFromId.UserAuthId);

        return null;
    }

    private async Task<IUserSession> FetchSessionByUserAuthId(string userAuthId)
    {
        // 实现你的会话获取逻辑
        ...
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 18:44:52