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

OpenIddict 2迁移至4:如何设置刷新与访问令牌格式?

OpenIddict 4 替换自定义令牌格式的实现方案

核心变更说明

OpenIddict 3及以上版本重构了令牌生成与验证的扩展模型,原OpenIddict 2中直接设置AccessTokenFormat/RefreshTokenFormat的方式已被废弃,转而采用**令牌生成器(IOpenIddictTokenGenerator)和令牌验证器(IOpenIddictTokenValidator)**作为核心扩展点,针对JWT令牌也提供了更简洁的自定义配置方式。

方案1:自定义JWT令牌(适配原JWT格式逻辑)

如果你的JWTAccessTokenFormat/JWTRefreshTokenFormat是基于JWT的自定义签名、受众设置或声明处理,可按以下方式替换:

基础配置与JWT签名设置

直接在AddServer配置链中指定签名密钥,并按需添加自定义受众:

services.AddOpenIddict()
    .AddServer(options =>
    {
        // 配置基础端点与授权流程
        options.SetTokenEndpointUris("/connect/token");
        options.AllowPasswordFlow();
        options.AllowRefreshTokenFlow();

        // 启用JWT令牌格式,设置签名密钥
        options.UseJsonWebTokens();
        options.AddSigningKey(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenKey)));

        // 直接设置受众(对应原代码中的SystemHostname和AlternativeSystemHostname)
        options.SetAudiences(OrbitConfiguration.SystemHostname, OrbitConfiguration.AlternativeSystemHostname);
    });

自定义令牌声明或内容

如果需要更复杂的自定义逻辑(比如添加额外声明),替换默认的IOpenIddictTokenGenerator:

public class CustomJwtTokenGenerator : OpenIddictTokenGenerator
{
    private readonly OrbitConfiguration _orbitConfig;

    public CustomJwtTokenGenerator(
        IOpenIddictTokenGeneratorCache cache,
        IOptionsMonitor<OpenIddictServerOptions> options,
        OrbitConfiguration orbitConfig)
        : base(cache, options)
    {
        _orbitConfig = orbitConfig;
    }

    public override async ValueTask<OpenIddictTokenDescriptor> GenerateAsync(OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
    {
        var tokenDescriptor = await base.GenerateAsync(descriptor, cancellationToken);
        
        // 添加自定义受众或其他声明
        tokenDescriptor.Audiences.Add(_orbitConfig.SystemHostname);
        tokenDescriptor.Audiences.Add(_orbitConfig.AlternativeSystemHostname);
        tokenDescriptor.Payload.Add("custom_claim", "custom_value");

        return tokenDescriptor;
    }
}

注册自定义生成器:

services.AddOpenIddict()
    .AddServer(options =>
    {
        // 其他配置...
        options.Services.AddTransient<IOpenIddictTokenGenerator, CustomJwtTokenGenerator>();
    });

方案2:完全自定义非JWT令牌格式

如果你的令牌格式不是标准JWT,需要复用原JWTAccessTokenFormat/JWTRefreshTokenFormat的完整逻辑,可通过实现IOpenIddictTokenGenerator和IOpenIddictTokenValidator来替换:

自定义令牌生成器

public class CustomTokenGenerator : IOpenIddictTokenGenerator
{
    private readonly string _tokenKey;
    private readonly OrbitConfiguration _orbitConfig;

    public CustomTokenGenerator(IOptions<YourTokenKeySettings> tokenKeySettings, OrbitConfiguration orbitConfig)
    {
        _tokenKey = tokenKeySettings.Value.Key;
        _orbitConfig = orbitConfig;
    }

    public async ValueTask<string> GenerateAsync(OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
    {
        // 从描述符中获取认证票据
        var ticket = descriptor.Properties.Get<AuthenticationTicket>(OpenIddictServerConstants.Properties.Ticket);
        if (ticket == null)
        {
            throw new InvalidOperationException("无法获取认证票据");
        }

        // 根据令牌类型选择对应的自定义格式处理
        var format = descriptor.Type == OpenIddictConstants.TokenTypes.AccessToken
            ? new JWTAccessTokenFormat(_tokenKey, _orbitConfig.SystemHostname, _orbitConfig.AlternativeSystemHostname)
            : new JWTRefreshTokenFormat(_tokenKey, _orbitConfig.SystemHostname, _orbitConfig.AlternativeSystemHostname);

        // 复用原逻辑生成令牌
        return format.Protect(ticket);
    }
}

自定义令牌验证器

public class CustomTokenValidator : IOpenIddictTokenValidator
{
    private readonly string _tokenKey;
    private readonly OrbitConfiguration _orbitConfig;

    public CustomTokenValidator(IOptions<YourTokenKeySettings> tokenKeySettings, OrbitConfiguration orbitConfig)
    {
        _tokenKey = tokenKeySettings.Value.Key;
        _orbitConfig = orbitConfig;
    }

    public async ValueTask<OpenIddictTokenValidationResult> ValidateAsync(OpenIddictTokenValidationContext context, CancellationToken cancellationToken)
    {
        // 根据令牌类型选择对应的自定义格式处理
        var format = context.TokenTypeHint == OpenIddictConstants.TokenTypes.AccessToken
            ? new JWTAccessTokenFormat(_tokenKey, _orbitConfig.SystemHostname, _orbitConfig.AlternativeSystemHostname)
            : new JWTRefreshTokenFormat(_tokenKey, _orbitConfig.SystemHostname, _orbitConfig.AlternativeSystemHostname);

        // 复用原逻辑解析令牌
        var ticket = format.Unprotect(context.Token);
        if (ticket == null)
        {
            return OpenIddictTokenValidationResult.Failed(new InvalidOperationException("无效的令牌格式"));
        }

        // 将解析结果映射到OpenIddict验证结果
        var result = new OpenIddictTokenValidationResult
        {
            Token = new OpenIddictToken
            {
                Type = context.TokenTypeHint,
                Subject = ticket.Principal.FindFirst(ClaimTypes.NameIdentifier)?.Value,
                Audiences = new HashSet<string> { _orbitConfig.SystemHostname, _orbitConfig.AlternativeSystemHostname }
                // 按需映射其他令牌属性
            }
        };

        return result;
    }
}

注册自定义实现

services.AddOpenIddict()
    .AddServer(options =>
    {
        // 其他配置...
        options.Services.AddTransient<IOpenIddictTokenGenerator, CustomTokenGenerator>();
        options.Services.AddTransient<IOpenIddictTokenValidator, CustomTokenValidator>();
    });

注意事项

  • 若仅需设置JWT受众,无需替换令牌生成器,直接调用options.SetAudiences()即可。
  • OpenIddict 4默认刷新令牌为加密随机字符串,若需刷新令牌也用JWT,需添加options.UseJsonWebTokens().AllowRefreshTokenFlow()配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 15:33:17