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

如何在OpenIddict登录流程中内部生成令牌以调用其他API?

解决OpenIddict手动生成访问令牌的问题

我搭建了OpenIddict服务器,登录流程里需要调用受保护的用户服务(存储所有可登录用户),但该服务要求有效访问令牌。尝试用IOpenIddictTokenManager创建令牌却失败——生成的令牌没有实际有效负载,payload返回null,数据库里对应字段也为空。使用的是ClientCredentials模式的客户端,换其他客户端结果一致。

原代码:

var app = await _applicationManager.FindByClientIdAsync("<clientId>");
var token = await _tokenManager.CreateAsync(new OpenIddictTokenDescriptor()
{
    ApplicationId = await _applicationManager.GetIdAsync(app),
    CreationDate = DateTime.UtcNow,
    ExpirationDate = DateTime.UtcNow.AddMinutes(1),
    Type = "access_token",
    Subject = "<subject>"
});

var payload = await _tokenManager.GetPayloadAsync(token);

问题原因

IOpenIddictTokenManager仅负责令牌元数据的持久化(比如存数据库记录),不会自动生成签名的JWT令牌或加密的参考令牌,所以你拿到的只是令牌的基础记录,没有可用于请求服务的实际令牌内容。

正确解决方案

方法一:使用IOpenIddictTokenService生成令牌(推荐)

利用OpenIddict内置服务生成符合规范的令牌,自动处理签名和元数据存储:

using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;

// 获取目标客户端
var client = await _applicationManager.FindByClientIdAsync("<clientId>");
if (client == null)
{
    throw new InvalidOperationException("指定客户端不存在");
}

// 构建令牌请求上下文
var context = new OpenIddictServerCreateTokenContext(new OpenIddictRequest
{
    ClientId = await _applicationManager.GetClientIdAsync(client),
    ClientSecret = await _applicationManager.GetClientSecretAsync(client),
    GrantType = OpenIddictConstants.GrantTypes.ClientCredentials,
    Scope = "api" // 替换为用户服务要求的权限范围
}, new ClaimsPrincipal());

// 添加自定义主体声明
context.Principal.AddClaim(OpenIddictConstants.Claims.Subject, "<subject>");

// 生成令牌
await _tokenService.CreateTokenAsync(context);

// 获取有效访问令牌
var accessToken = context.Token;
// 获取令牌负载(声明集合)
var payload = context.Principal.Claims.ToDictionary(c => c.Type, c => c.Value);

方法二:手动生成JWT令牌(自定义场景)

如果需要完全自定义令牌内容,可直接用System.IdentityModel.Tokens.Jwt生成符合要求的JWT:

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

// 获取客户端密钥
var clientSecret = await _applicationManager.GetClientSecretAsync(client);
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(clientSecret));

// 构建令牌声明
var claims = new List<Claim>
{
    new Claim(OpenIddictConstants.Claims.Subject, "<subject>"),
    new Claim(OpenIddictConstants.Claims.ClientId, "<clientId>"),
    new Claim(JwtRegisteredClaimNames.Iss, "https://your-openiddict-server.com"), // 替换为你的服务器地址
    new Claim(JwtRegisteredClaimNames.Aud, "https://your-user-service.com") // 替换为用户服务的受众地址
};

// 创建JWT令牌
var jwtToken = new JwtSecurityToken(
    issuer: "https://your-openiddict-server.com",
    audience: "https://your-user-service.com",
    claims: claims,
    expires: DateTime.UtcNow.AddMinutes(1),
    signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
);

// 生成字符串格式的访问令牌
var accessToken = new JwtSecurityTokenHandler().WriteToken(jwtToken);

注意事项

  • 确保客户端已配置有效的ClientSecret,并启用ClientCredentials授权类型,同时包含用户服务所需的权限范围。
  • 若使用参考令牌模式,需确保OpenIddict已配置对应的存储和验证逻辑,此时生成的令牌为随机字符串,需通过OpenIddict验证端点解析负载。
  • 生成的令牌需与用户服务的验证配置匹配(签名算法、颁发者、受众等),否则会被用户服务拒绝。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:23:18