基于IdentityServer4的.Net Core后台应用用户模拟Token获取问题
问题根因
你当前使用的ClientCredentials(客户端凭证)授权模式,设计目标就是无用户参与的服务间通信场景,签发的访问令牌天然只包含客户端本身的身份声明,不会携带任何终端用户的身份信息。因此服务端拿到这个令牌做权限校验时,只能识别出后台服务的身份,无法定位到任务对应的终端用户。
适配场景的实现路径
要实现后台任务代表关联用户发起API调用的效果,IdentityServer4 对应两种可落地的实现方案,可根据自身安全等级要求选择:
方案一:OAuth 2.0 On-Behalf-Of(OBO,代表用户)委托流程(生产环境优先推荐)
这个流程是OAuth标准专门为“无用户实时交互的服务端,代表已认证过的用户调用下游API”设计的,安全性最高,完全匹配你的后台任务场景。
核心流程逻辑
- 用户在初始的交互式前端登录环节,会获得一个绑定了自身完整身份、指向你的后台服务的短有效期访问令牌
- 后台服务在接收用户提交的任务时,将这个用户的原始访问令牌、任务关联的用户标识一起持久化存储
- 后台任务异步执行需要调用业务API时,取出对应任务关联的用户原始令牌,向IdentityServer发起令牌委托请求
- IdentityServer 校验原始令牌有效性、确认请求的后台服务具备委托调用权限后,会签发新的访问令牌——这个令牌会完整携带原始用户的身份声明(用户ID、角色、权限等),同时将受众标记为你指定的业务API,可直接通过API的身份校验。
服务端客户端配置调整
将原来的客户端配置改造为允许OBO委托授权:
new Client { ClientId = "background-service", ClientSecrets = { new Secret("your-encrypted-secret".Sha256()) }, // 替换原有的ClientCredentials授权类型,开启代表用户的JWT Bearer委托授权 AllowedGrantTypes = GrantTypes.JwtBearer, // 保留原有可访问的API作用域 AllowedScopes = { "api1" }, // 配置允许从原始用户令牌中复制需要的用户声明到新令牌 ClientClaimsPrefix = "", Claims = new List<ClientClaim> { new ClientClaim("sub", "*"), new ClientClaim("name", "*"), new ClientClaim("role", "*") } }
后台服务端的客户端代码调整
替换原有的客户端凭证令牌请求逻辑,使用IdentityModel提供的JWT Bearer请求方法:
var identityServerClient = new HttpClient(); // 拉取IdentityServer的公开发现文档,逻辑和原有实现一致 var disco = await identityServerClient.GetDiscoveryDocumentAsync("https://localhost:5001"); // 发起代表用户的令牌委托请求 var oboTokenResponse = await identityServerClient.RequestJwtBearerTokenAsync(new JwtBearerTokenRequest { Address = disco.TokenEndpoint, ClientId = "background-service", ClientSecret = "your-encrypted-secret", Scope = "api1", // 关键:填入当前待处理数据对应的用户原始访问令牌 Assertion = "任务关联的用户原始访问令牌" }); // 新生成的令牌自动携带完整用户声明,后续API调用逻辑和原有一致 var apiClient = new HttpClient(); apiClient.SetBearerToken(oboTokenResponse.AccessToken);
方案二:高信任场景下的客户端声明透传(仅适合完全可控的内网环境)
如果你的后台服务和IdentityServer属于同一个完全可信的内网边界,不需要引入原始用户令牌的存储开销,可以直接在客户端认证通过后,由后台服务传入需要关联的用户标识,由IdentityServer自动将对应用户的声明注入到生成的令牌中。
风险提示
这个方案的安全等级较低:如果后台服务被攻破,攻击者可以直接模拟任意用户身份发起请求,仅建议在内部封闭系统中使用,不要将相关服务暴露在公网环境。
实现要点
- 服务端保持原有的
ClientCredentials授权类型配置 - 自定义IdentityServer的令牌签发事件,当收到后台服务的客户端凭证请求时,读取请求中携带的
target_user_id参数,从用户存储中查询对应用户的所有身份声明,注入到最终签发的访问令牌中 - 后台服务发起客户端凭证请求时,在请求参数中传入当前处理任务对应的
target_user_id即可。
常见踩坑提示
- 不要为了简化流程使用资源所有者密码模式(ResourceOwnerPassword):这种模式要求后台服务存储用户的明文账号密码,一旦服务被攻破会导致全量用户凭据泄露,风险远高于OBO流程
- 严格控制令牌有效期:原始的用户访问令牌不要设置过长的过期时间,后台任务执行过程中如果遇到令牌过期,需要配套实现基于刷新令牌的续期逻辑,不要将长期有效的用户凭据持久化存储在后台服务中
- 严格收敛权限范围:给后台服务的委托权限,只开放它实际需要调用的API作用域,避免权限过度授予带来的安全风险
内容的提问来源于stack exchange,提问作者Sprotty
相关产品推荐
相关产品推荐

