员工身份(EIAM)

使用自定义ID Token与IDaaS集成实现单点登录

集成方案

# 场景描述

适用于第三方平台使用自己的认证方式登录后,通过生成自定义的ID Token,实现免密登录到已与IDaaS集成的各应用系统。

此场景下:

  • 第三方平台作为OIDC OP(OpenID Provider),生成用户的ID Token信息。
  • IDaaS平台作为OIDC RP(Relying Party),验签和匹配当前用户身份信息。若验签并匹配用户成功,单点登录进入到已与IDaaS平台集成的目标应用系统。

# 单点登录流程

用户先登录门户应用A,在通过门户应用A访问应用B实现单点登录

# 集成步骤

# IDaaS企业中心配置OIDC认证源

  1. 登录IDaaS企业中心,创建OIDC认证源

    配置名称 备注
    认证方式 选择“认证源发起认证”
    公钥格式 支持四种格式,基于JWKURL,PEM格式公钥,JSON格式公钥、证书格式公钥
    公钥 不同公钥格式对应不同的内容,可根据参考示例生成
    签名算法 固定“RS256”
    Audience 生成的id_token中的aud参数
    调用地址 用于接收认证源放返回id_token的回调地址,添加完后自动生成
    关联源属性 id_token解析后存在的属性key,默认为sub
    关联用户属性 IDaaS平台用户唯一属性

# 第三方平台生成ID Token

生成方式可参考示例代码

  • HEADER部分

    1. 参数示例
    { "kid": "14a0b7d31d5d284c549f9e3565fb136a", "alg": "RS256" }
    
    1
    1. 参数说明
    参数名 是否必填 描述
    kid 验证身份令牌签名时使用的秘钥id
    alg 签名算法
  • PAYLOAD部分

    1. 参数示例
    { "iss": "https://xxx.com ", "aud": "https://{your_domain}", "exp": 1655779413, "jti": "B6P99VAWZQZBGNa4avp29s", "iat": 1655779293, "nbf": 1655779173, "sub": "subject" }
    
    1
    1. 参数说明
    参数名 是否必填 描述
    iss 令牌颁发者,提供认证信息者的唯一标识, URI格式,一般为应用的域名
    aud 令牌接收者, 与OIDC身份提供商中配置的Audience一致
    exp 令牌的过期时间,时间戳(毫秒)
    iat 令牌的签发时间,时间戳(毫秒)
    sub 令牌主体,用户唯一标识
    jti 令牌的id

# 第三方平台调用IDaaS单点登录接口

  1. 基于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.com
      
      1
    • 返回示例

      HTTP Status: 302 REDIRECT [https://demo.com]
      
      1

# 参考示例

# 生成公私钥

  1. 在线生成pem格式的公私钥,可以参考:https://apiked.com/rsa

  2. 以生成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

示例代码如下:

	/**
     * 生成秘钥
     */
    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

# 生成用户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

示例代码如下:

	/**
     * 生成用户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