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
相关产品推荐
相关产品推荐

