员工身份(EIAM)
使用自定义ID Token与IDaaS集成实现单点登录
集成方案
# 场景描述
适用于第三方平台使用自己的认证方式登录后,通过生成自定义的ID Token,实现免密登录到已与IDaaS集成的各应用系统。
此场景下:
- 第三方平台作为OIDC OP(OpenID Provider),生成用户的ID Token信息。
- IDaaS平台作为OIDC RP(Relying Party),验签和匹配当前用户身份信息。若验签并匹配用户成功,单点登录进入到已与IDaaS平台集成的目标应用系统。
# 单点登录流程
用户先登录门户应用A,在通过门户应用A访问应用B实现单点登录

- IDaaS单点登录地址:参考第三方平台调用IDaaS单点登录接口
- 应用B地址:第三方平台调用IDaaS单点登录接口中的redirect_to参数
- 仅支持单向sso,即应用A——>sso应用B,不能实现应用B——>应用A
- 此方案为自定义协议,不是国际标准协议,需应用方/第三方做好自身安全,避免因自身问题引发安全风险
# 集成步骤
# IDaaS企业中心配置OIDC认证源
登录IDaaS企业中心,创建OIDC认证源


配置名称 备注 认证方式 选择“认证源发起认证” 公钥格式 支持四种格式,基于JWKURL,PEM格式公钥,JSON格式公钥、证书格式公钥 公钥 不同公钥格式对应不同的内容,可根据参考示例生成 签名算法 固定“RS256” Audience 生成的id_token中的aud参数 调用地址 用于接收认证源放返回id_token的回调地址,添加完后自动生成 关联源属性 id_token解析后存在的属性key,默认为sub 关联用户属性 IDaaS平台用户唯一属性
# 第三方平台生成ID Token
生成方式可参考示例代码
HEADER部分
- 参数示例
{ "kid": "14a0b7d31d5d284c549f9e3565fb136a", "alg": "RS256" }1- 参数说明
参数名 是否必填 描述 kid 是 验证身份令牌签名时使用的秘钥id alg 是 签名算法 PAYLOAD部分
- 参数示例
{ "iss": "https://xxx.com ", "aud": "https://{your_domain}", "exp": 1655779413, "jti": "B6P99VAWZQZBGNa4avp29s", "iat": 1655779293, "nbf": 1655779173, "sub": "subject" }1- 参数说明
参数名 是否必填 描述 iss 是 令牌颁发者,提供认证信息者的唯一标识, URI格式,一般为应用的域名 aud 是 令牌接收者, 与OIDC身份提供商中配置的Audience一致 exp 是 令牌的过期时间,时间戳(毫秒) iat 是 令牌的签发时间,时间戳(毫秒) sub 是 令牌主体,用户唯一标识 jti 否 令牌的id
# 第三方平台调用IDaaS单点登录接口
基于OIDC认证源端发起认证,调用IDaaS提供的OIDC回调地址完成认证
请求说明
请求地址 https://{your_domain}/api/v1/openid/id_token/{idpId}
请求方式 GET
请求参数
参数名 中文名称 必须 类型 示例 id_token ID Token 必须 String redirect_to 重定向地址 可选 String 需要访问的目标应用系统的访问地址
参数为空时默认跳转IDaaS用户中心
可针对此地址配置白名单请求示例
https://{your_domain}/api/v1/openid/id_token/202208231445-0FE9-93C4AFCDA?id_token=eyJhbGciOiJJ9.eyJzdWIiOiJ6aG91eGNoIiwiaWQiOiIyMDIyMTIyMDE1zIjdlIZmFiMDVkNTg3YyJ9.bfCdG5PcttXzoEA8kuOf81SrQ&redirect_to=https://demo.com1返回示例
HTTP Status: 302 REDIRECT [https://demo.com]1
# 参考示例
# 生成公私钥
在线生成pem格式的公私钥,可以参考:https://apiked.com/rsa
以生成JSON格式公钥为例,生成一个RS256算法的密钥如下。
第三方依赖包:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.50</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.50</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>8.19</version> <!-- 使用最新版本 -->
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
示例代码如下:
/**
* 生成秘钥
*/
public static RSAKey generatorKeys() throws Exception {
String kid = UUID.randomUUID().toString().replaceAll("-", "");
RSAKey key = new RSAKeyGenerator(2048)
.keyUse(KeyUse.SIGNATURE)
.algorithm(new Algorithm("RS256"))
.keyID(kid)
.generate();
System.out.println("不对外提供json格式私钥为:"+key.toJSONString());
System.out.println("可对外提供json格式的公钥为:"+key.toPublicJWK().toJSONString());
return key;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 生成用户ID Token
根据用户唯一标识,生成id_token示例如下:
第三方依赖包:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.50</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.50</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>8.19</version> <!-- 使用最新版本 -->
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
示例代码如下:
/**
* 生成用户id_token
*/
public static String buildIdToken() throws Exception{
/**
* 1. 根据秘钥生成签名工具
*/
//json格式,parse方法参数为上一步生成的不对外提供json格式私钥
RSAKey rsaKey = (RSAKey)JWK.parse("{\"p\":\"yuaog5...nNgWLVg\",\"dp\":\"nhr2nPFE...LMP28KylCs0GdE\",\"alg\":\"RS256\",\"dq\":\"xdW66Lr10...6HZXFk\",\"n\":\"oGVHTUb9amuG...J8SAfBV7c49W0lSw\"}");
RSASSASigner rsassaSigner = new RSASSASigner(rsaKey);
/**
* 2. 建立header头
*/
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(rsaKey.getKeyID()).build();
/**
* 2. 建立payload载体
*/
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
// aud必填, IDaaS认证源中与 Audience 的配置保持一致
.audience("http://{your_domain}")
// iss必填, 必须是URI格式, 第三方应用域名
.issuer("http://xxx.com")
// sub必填, 根据平台配置,sub为用户的唯一标识
.subject("zhangsan")
// iat必填, token签发时间
.issueTime(new Date())
// exp token过期时间
.expirationTime(new Date(System.currentTimeMillis() + (1000 * 60 * 5)))
// 自定义属性,非必填
.claim("mobile", "18310773289")
.build();
/**
* 3. 建立签名
*/
SignedJWT signedJWT = new SignedJWT(header, claimsSet);
signedJWT.sign(rsassaSigner);
/**
* 4. 生成id_token
*/
String id_token = signedJWT.serialize();
System.out.println("id_token为:"+ id_token);
return id_token;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
