自定义门户

最后更新:2022-11-15

政企通常希望有自己的个性门户, 除了在控制台的“个性定制”能修改LOGO和名称外, 同时, 还希望通过代码, 能进一步集成到自己深度定制开发的门户中。 本文档覆盖了后者需要调用的API接口及能力。

1. 术语定义

参考:IDP中产品术语介绍

2. 什么是自定义门户

门户一般指企业员工统一进入业务系统的入口系统网站,简称portal。门户一般带有企业自主风格的统一登录界面,相对于使用IDaaS提供的统一登录页,一般都有改造登录页的需求。

注意:这并不意味着自定义门户登录界面就一定都是自己做,也有少数需要使用IDaaS统一登录页的情况。

3. 自定义门户和业务系统接入IDaaS区别

业务系统,即SP, 一般仅会接入IDaaS的单点登录,和退出功能。
自定义门户portal则不同,除了接入IDaaS单点登录(可选)和退出,还需要获取用户在IDaaS平台上授权的应用单点登录地址,以支持在自己的门户网站上展示。而获取用户在IDaaS平台上授权的应用列表,则必须使用用户的访问令牌access_token

业务系统和IDaaS对接单点登录时,需要对接用户的身份令牌(id_token)。 门户和IDaaS对接时,除了对接用户的身份令牌,主要需要获取到用户的访问令牌(access_token),凭此来调用IDaaS用户相关的API。

4. 场景描述

企业的门户接入IDaaS登录,退出,以及获取用户授权的应用列表接口能力,实现企业用户从门户登录后,能直接单点登录到各个业务系统。当用户从门户退出时,退出IDaaS相关的用户会话,(可能伴随以及其他已经登录的业务系统),又重新回到门户的登录页。

5. 接入流程

接入顺序主要为:
创建OAuth2应用 -> 对接IDaaS登录 -> 调用IDaaS获取用户授权的应用接口 > 对接IDaaS退出 > 测试
对于门户来说,对接IDaaS登录可分为:

  • 方式一:直接调用IDaaS登录接口

  • 方式二:使用IDaaS统一登录页两种情况(推荐)

  • 方式三:无密码,使用API获取用户token

以下为门户接入IDaaS时使用IDaaS的登录接口的完整时序图

5.1. 创建OAuth2的门户应用

使用管理员登录IDaaS平台,在添加应用,标准协议中选择OAuth2模板,点击添加, 比如XX门户, 如下图
image.png

Redirect URI:授权码模式下,门户需要进行接收IDaaS产生的临时code地址,凭借临时code,门户可再次通过授权码得到用户的访问令牌access_token
GrantType: 此处选择 authorization_code

5.2. 对接IDaaS登录

门户接入IDaaS登录时,可分为两种情况:
方式一(推荐):办公门户使用IDaaS的统一登录页。用户登录时直接引导到IDaaS的统一登录页,后续门户获取IDaaS用户访问令牌access_token
方式二:办公门户使用自己的登录页,用户登录时调用IDaaS的接口进行登录,IDaaS返回用户的访问令牌access_token
方式三:大屏等门户使用IDaaS的登录接口,但提供的是应用的AK,而不是用户的密码。 通过此种方式可以跳过access_token, 直接得到用户的应用列表。

注意:无论选择哪种,其最终目的都是为了获取到用户的访问令牌access_token

5.2.1. 方式一:使用IDaaS统一登录页登录

使用IDaaS统一登录页登录时,其流程为标准的Oauth2授权码模式获取用户访问令牌access_token。
注意调用此接口,使用的client_id和client_secret为步骤1创建OAuth2应用的client_id和client_secret,在查看详情中展示的client_id,client_secret
image.png
详细获取流程如下
第一步: 构造Authorize URL,获取用户授权code
门户可通过一个按钮或其他方式,触发浏览器打开Authorize URL,用户登录成功后,会跳转到回调地址Redirect URI,并把code参数一同转发过去。
Authorize URL 格式为
http(s)://{IDaaS_server}/oauth/authorize?response_type=code&scope=read&client_id={client_id}&redirect_uri={redirect_uri}&state={state}

参数名称

参数类型

是否必须

示例

备注

response_type

text

code

响应类型,固定
code

scope

text

read

授权范围,固定read

client_id

text

1d0f8349ad981fe9a306e39d86a3a124uHW6fd0sC7U

OAuth2应用详情Client Id

redirect_uri

text

http%3A%2F%2Foa.com%2Fcallback

OAuth2应用详情
Redirect URI
注意该地址需要进行url_encode

state

text

10ff0be64971c07f893afc332877f68arS8FH2iyZni_idp

随机值,若为门户发起,可自定义随机一个字符串,若为IDP发起,也是随机一个值,但会带上_idp后缀

补充说明
浏览器访问完整地址示例如下

http://{IDaaS_server}/oauth/authorize?response_type=code&scope=read&client_id=1d0f8349ad981fe9a306e39d86a3a124uHW6fd0sC7U&redirect_uri=http%3A%2F%2Foa.com%2Fcallback&state=10ff0be64971c07f893afc332877f68arS8FH2iyZni_idp

访问完成后,会跳转至IDaaS登录页。用户登录成功后,会跳转到回调地址Redirect URI,并把code参数一同转发过去。

访问Authorize URL获取code.gif

第二步:code 换取 access_token
门户接收到code后,可凭此code换取用户的访问令牌access_token
详细地址如下:
http(s)://{IDaaS_server}/oauth/token?grant_type=authorization_code&code={code}&client_id={client_id}&client_secret={client_secret}&redirect_uri={redirect_uri}
请求方法为POST

参数名称

参数类型

是否必须

示例

备注

grant_type

text

authorization_code

响应类型,固定
authorization_code

code

text

WgWQe6

为第一步用户登录成功后IDaaS回调至Redirect URI上请求参数code值

client_id

text

1d0f8349ad981fe9a306e39d86a3a124uHW6fd0sC7U

OAuth2应用详情
Client Id

client_secret

text

vdDtBPNiHRCz8S267fURHYnr2bMlgL7ahqrpgrDscG

OAuth2应用详情
Client Secret

redirect_uri

text

http%3A%2F%2Foa.com%2Fcallback

OAuth2应用详情
Redirect URI
注意该地址需要进行url_encode

补充说明
curl请求示例:

curl -X POST 'http://{IDaaS_server}/oauth/token?grant_type=authorization_code&code=dIKvfA&client_id=1d0f8349ad981fe9a306e39d86a3a124uHW6fd0sC7U&client_secret=vdDtBPNiHRCz8S267fURHYnr2bMlgL7ahqrpgrDscG&redirect_uri=http%3A%2F%2Foa.com%2Fcallback'

成功时响应

{
  "access_token":"eyJhbGciO...",
  "token_type":"bearer",
  "refresh_token":"eyJhbGciOiJIUzI1...",
  "expires_in":7199,
  "scope":"read",
  "jti":"17147278-7f3e-45f2-be6f-8105c4334a30"
}

失败时响应:
client_id或client_secret错误

{
  "error":"invalid_client",
  "error_description":"Bad client credentials"
}

code错误

{
  "error":"invalid_grant",
  "error_description":"Invalid authorization code: dIKvfA"
}

code失效

{
  "error":"invalid_grant",
  "error_description":"authorization code expired: WgWQe6"
}

5.2.2. 方式二:调用IDaaS的接口进行登录

当使用门户有自己的登录页,建议使用此种方式。
其流程为标准的OAuth2密码模式(password)获取用户访问令牌access_token。
注意调用此接口,使用的client_id和client_secret为步骤1创建OAuth2应用的API Key和API Secret,在详情API处复制
image.png

请求地址: /oauth/token
请求方法: POST
接口描述:第三方/门户系统调用IDAAS登录接口,进行认证用户,IDAAS返回认证结果,若成功,返回用户的access_token,(access_token为调用IDAAS用户相关API的接口凭证)失败时将返回对应错误。

请求参数
Headers

参数名称

参数值

是否必须

示例

备注

Content-Type

application/x-www-form-urlencoded

Body

参数名称

参数类型

是否必须

示例

备注

client_id

text

0d855656533d0e6988c8ea35c0a52phKHIFq55p

应用APIKey

client_secret

text

KWsRAi675ZSYbycbMg7ofmo61ov8hQst6kOi8F

应用APISecret

grant_type

text

password

授权类型,固定password

scope

text

read

授权范围,固定read

username

text

zhangsan

用户名

password

text

123456

密码(明文)

返回数据

名称

类型

是否必须

默认值

备注

其他信息

access_token

string

必须

用户令牌

token_type

string

必须

令牌类型

expires_in

number

必须

有效期,单位s

scope

string

必须

授权范围

jti

string

非必须

若令牌格式为jwt,代表令牌id

补充说明
curl请求示例:

curl -X POST 'http://{IDaaS_server}/oauth/token?client_id=0d8e9ae61c23533d0e6988c8ea35c0a52phKHIFq55p&client_secret=KWsRAiZhEajM5ZSYbycbMg7ofmo61ov8hQst6kOi8F&grant_type=password&scope=read&username=zhangsan&password=123456' -H 'content-type: application/x-www-form-urlencoded'

认证成功时返回格式如下:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ....",
    "token_type": "bearer",
    "expires_in": 43199,
    "scope": "read",
    "jti": "b9c7214b-d900-41c6-9fa0-e76293a1e70d"
}

认证失败时返回格式如下:
client_id或client_secret错误

{
"error": "invalid_client",
"error_description": "Bad client credentials"
}

用户名或密码错误

{
    "error": "invalid_grant",
    "error_description": "Bad credentials"
}

5.2.3. 方式三:使用IDaaS无密码登录

在门户为大屏等应用的形势下, 通常会使用应用作为一个整体登录的形式, 这样就不需要提交每个用户的密码了;
使用互信应用插件
适用于无密码登录场景,使用秘钥加密唯一标识,到IDaaS换取用户access_token
支持的算法:SM2、RSA、AES
支持的唯一标识:用户名、手机号、邮箱、外部ID
详细流程如下:
第一步:上传插件(插件请联系客服获取):

第二步:创建应用:

第三步:获取应用密钥:
获取秘钥 无需授权、启用应用、查看应用详细。
创建应用时,会自动生成 SM2密钥对、RSA密钥对、AES秘钥,使用对应秘钥调用认证接口
SM2:使用 idsmanager-sm2 包生成
RSA:使用 idsmanager-oidc 包生成(长度2048)
AES:随机生成(长度32)

第四步:调用接口:
请求地址: /api/public/bff/v1.2/application/plugin_mutualtrust/login
请求方法: POST
接口描述:通过解析加密值得到用户唯一标识,找到用户生成access_token返回。
请求参数
Headers

参数名称

参数值

是否必须

示例

备注

Content-Type

application/json

Body

参数名称

参数类型

是否必须

示例

备注

algorithmType

text

SM2

encryptedIdentity值的加密算法
值为:SM2、RSA、AES

encryptedIdentity

text

1637835035000_zhangsan

用户唯一标识加密后的值。
格式:13位时间戳+下划线+唯一标识,截取时使用第一个下划线分割,如果时间间隔大于10分钟则返回错误
例如:1637835035000_zhangsan

identityType

text

USERNAME

用户的身份类型:
USERNAME (用户名)
PHONE_NUMBER(用户手机号)
EXTERNAL_ID(用户外部ID)
EMAIL (用户邮箱)

purchaseId

text

xxxx_plugin_mutualtrust

应用ID
步骤3应用详细中获取

返回数据

名称

类型

是否必须

默认值

备注

其他信息

success

booliean

必须

响应结果

code

string

必须

响应码

message

string

必须

调用失败时返回错误信息

data

Object

必须

有效期,单位s

└access_token

string

必须

用户的token

响应码

响应码

响应信息

备注

200

成功

500

系统繁忙

请联系管理员

400100

无效的algorithmType

根据接口规范修改

400101

无效的encryptedIdentity

加密算法或加密内部不正确

400102

无效的encryptedIdentity,已过期

timestamp已超过10分钟或加密服务器与验证服务器时间差大于10分钟

400103

无效的用户唯一标识

用户在系统中未找到

400104

无效的identityType

根据接口规范修改

400105

无效的purchaseId

需要管理员在应用详细中查看

400106

无效的purchaseId,应用未启用

应用未启用需要管理员在应用管理中启用

400107

无效的用户唯一标识,用户已禁用

账户禁用需要管理员在管理页面启用

400108

无效的用户唯一标识,用户已锁定

账户锁定需要管理员在管理页面解锁

补充说明
请求示例:

POST /api/public/bff/v1.2/application/plugin_mutualtrust/login
Host: {Host}
Content-Type: application/json

{
	"algorithmType":"SM2",
"encryptedIdentity":"BLTCpPB5wsl/ws2QlJrkijihIqQxg0CQzM2qVdetncw/sM0Edd2kLKi84QM/dqMyKISmTBEP53hvvjbHtR7tLzWvkSrMv2OkCYCs50LkjY4JiVVT9M+/PjvVpBcNij7g714+imp8Pdg=",
	"identityType":"USERNAME",
	"purchaseId":"zhangdhplugin_mutualtrust2"
}

调用成功响应:

{
    "success": true,
    "code": "200",
    "message": null,
    "data": {
        "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZW50ZXJwcmlzZV9tb2JpbGVfcmVzb3VyY2UiLCJiZmZfYXBpX3Jlc291cmNlIl0sImV4cCI6MTYzNzg2ODI1MCwidXNlcl9uYW1lIjoiemhhbmdkaCIsImp0aSI6IjY4YjBiZmFkLWEzYTItNGRlYi1hZTg4LWYxNGY0YjRjMzM0MSIsImNsaWVudF9pZCI6Ijk2NzRhYjczYjZjOTc4YzE1ZTBiNThjNTY2NmJhNTVlWlJ4SVpqcUNCMzciLCJzY29wZSI6WyJyZWFkIl19.BUznu8_oNGcVcQpMmVNO6bEn7sO11RdzocSS3HrfbYg"
    }
}

调用失败响应:

{
    "success": true,
    "code": "400100",
    "message": "无效的algorithmType"
}

第五步:加密代码示例:
SM2方式:
Github demo工程:https://github.com/aliyun-idaas/idp4-developer-sm2-utils

public static void main(String[] args) throws Exception {
    //SM2的加密与解密
    String publicKey = "BMzrnltiDD/CCfr6r90Rf/qHn8Wc0LqPGm4rkSezjgEOUaxD5eNoBQw4xbdKLIwUj7UpfAtjw6ooH3Duu2qY+Cw=";
    String encodeParam = SM2Encrypt.encryptUseBase64(publicKey, System.currentTimeMillis() + "_zhangsan", true);
    System.out.println("加密后: " + encodeParam);
}

RSA方式:

package com.idaas.controller;

import org.jose4j.json.JsonUtil;
import org.jose4j.jwk.RsaJsonWebKey;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.security.PublicKey;

public class RSADemo {

    public static void main(String[] args) throws Exception {
        //RSA公钥字符串 注册应用时提供
        String publicKeyString = "{\"kty\":\"RSA\",\"kid\":\"2929530392857633439\",\"alg\":\"RS256\",\"n\":\"zhJ0pd63SMsgnoCnk_smt3-ePdjiEjJtveqH2UFLgGomaweA54qpTquYe_XyemoCeegRpwOJFd44NtdJgCMkoIXqVTkrLnEvaK-rSqAkPfsWvwv2QUiicnsb1hpXQv8rOIhQ9Txuae92vp4ZV9XZmf3phTD-hg8YZw1bjkUbyua_veh6oGphAvZoHntFJIrKIyf_oftwGtz9xpiLzbX3saOMq2NoDhUslVT4p2wNN3hVFKRwrCqxOcwTW0sARRAWm-jPJwhQuVa4i01QnHhN4KalHRPX4YVaLOPp57_ajIFOQCd6MFvTGW3i05GdAEbajNQEuYlrugUk-YXOnzDcqQ\",\"e\":\"AQAB\"}";
        PublicKey publicKey = new RsaJsonWebKey(JsonUtil.parseJson(publicKeyString)).getPublicKey();
        //身份证号
        String usernameBefore = System.currentTimeMillis() + "_zhangsan";
        //加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] bytes = cipher.doFinal(usernameBefore.getBytes("utf-8"));
        //转为base64(jdk默认)
        BASE64Encoder encoder = new BASE64Encoder();
        //最终传递的参数
        String username = encoder.encode(bytes);
        System.out.println(username);
    }
}

POM引用
        <!-- https://mvnrepository.com/artifact/org.bitbucket.b_c/jose4j -->
        <dependency>
            <groupId>org.bitbucket.b_c</groupId>
            <artifactId>jose4j</artifactId>
            <version>0.7.6</version>
        </dependency>

AES方式:

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class xxxx {

    public static void main(String[] args) throws Exception {
        String username = System.currentTimeMillis() + "_zhangsan";
        String key = "XrYOSEYikzf1aww9Szp3QnZ4dB3LkkGq";

        byte[] raw = key.getBytes("UTF-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(username.getBytes("UTF-8"));
                //转为base64(jdk默认)
        BASE64Encoder encoder = new BASE64Encoder();
        System.out.println(encoder.encode(encrypted));
    }
}

5.3. 调用IDaaS获取用户授权的应用接口

门户中往往需要展示一个用户所拥有的应用的列表, 这可以通过调用一个接口来获取。

请求地址: /api/bff/v1.2/enduser/portal/sso/app_list
请求方法: GET
接口描述: 通过用户的访问令牌access_token获取用户所有授权可单点登录的应用。
响应主要字段格式示例如下

{  
  "success":true,
  "code":"200",
  "message":null,
  "requestId":"1620373926480$c1112d2b-114d-237b-59ad-fe2f5d18eee8",
  "data":{
    "authorizationApplications":[
      {
        "name":"OAuth2",
        "applicationId":"goverplugin_oauth2",
        "applicationUuid":"2761c1936dbddbef56f3cec7bc885da6grO48gmRExY",
        "idpApplicationId":"plugin_oauth2",
        "logoUuid":"a4d0e4f81f4ad3f882a0128fc0d62a19mQzh887ke2p",
        "startUrl":"http://127.0.0.1:81/api/bff/v1.2/enduser/portal/sso/go_2761c1936dbddbef56f3cec7bc885da6grO48gmRExY",
        "createTime":"2021-04-20 14:09",
        "description":"OAuth 是一个开放的资源授权协议,应用可以通过 OAuth 获取到令牌 access_token,并携带令牌来服务端请求用户资源。应用可以使用 OAuth 应用模板来实现统一身份管理。",
        "enabled":true,
        "supportDeviceTypes":["WEB"],
        "existAccountLinking":false,
        "enableTwoFactor":false,
        "display":true,
        "defaultLinking":true,
        "autoLogin":false,
        "classifyUuid":null,
        "orderId":0
      }
    ]
  }
}

请求参数
Headers

参数名称

参数值

是否必须

示例

备注

Authorization

Bearer {access_token}

eyJhbGciOiJI…

用户令牌access_token,请求头中格式为
bearer {access_token}

返回数据

名称

类型

是否必须

默认值

备注

其他信息

success

boolean

必须

是否成功

code

string

必须

错误码,200则视为成功

message

null

必须

错误信息,code为非200时,可参考此值看错误信息

data

object

必须

├─
authorizationApplications

object []

必须

授权可单点登录的应用列表

item 类型:object

├─
name

string

必须

应用名

├─
applicationId

string

必须

应用ID

├─
applicationUuid

string

必须

应用uuid

├─
idpApplicationId

string

必须

应用模板ID

├─
logoUuid

string

必须

应用图标uuid

├─
startUrl

string

必须

应用单点登录地址

├─
createTime

string

必须

创建时间

├─
description

string

必须

应用模板描述

├─
enabled

boolean

必须

是否启用

├─
supportDeviceTypes

string []

必须

支持的设备类型

item 类型:string

├─

非必须

├─
existAccountLinking

boolean

必须

是否存在子账户

├─
enableTwoFactor

boolean

必须

应用是否开启二次认证

├─
display

boolean

必须

是否显示

├─
orderId

number

必须

应用排序号

其中startUrl为单点登录应用系统的URL。
支持可选参数如下:

参数名称

是否必须

示例

备注

access_token

eyJhbGciOiJI…

用户令牌access_token
加密跳转必填

redirect_url

http://www.xxx.com

应用系统支持的回调地址,需要urlencode

timestamp

unix时间戳13位

当前时间,注意有效期默认5分钟
加密跳转必填

sign

7148ce32446a6ad6141c2d7b911b81a441765639c125f736a6ffa9abf2c6063a

签名值
加密跳转必填

注意:需要单点登录到应用系统,针对不同的登录方式,实现方式有所不一样:

从4.18.2开始,所有重新创建的应用,需要对接自定义门户的都需要加密跳转
已经对接的应用,无需修改,兼容老版本方式,请尽快升级成加密方式跳转

登录方式

是否有IDaaS主会话

跳转方式

方式一:使用IDaaS统一登录页登录


1. 同一浏览器,直接跳转startUrl
2. 使用加密方式跳转

方式二:调用IDaaS的接口进行登录


1. 使用加密方式跳转

方式三:使用IDaaS无密码登录


1. 使用加密方式跳转

5.3.1. 加密算法

保证token传输安全性,防止重放攻击,url传递token需要生成签名,进行加密跳转,加密算法如下:

第一步:将所有URL参数通过key升序排序,并去掉空值,拼接成url参数形式(key+value&key=value)

如参数列表: access_token=123&timestamp=1667465505282&redirectUrl= 调换后为:access_token=123&timestamp=1667465505282

第二步:将拼接后的字符串进行sha256加密,apiSecret当盐值

accessToken=123&timestamp=1667465505282 签名后值为 : 7148ce32446a6ad6141c2d7b911b81a441765639c125f736a6ffa9abf2c6063a

第三步:将签名参数放入参数中,并进行跳转

最终跳转路径如下: https://{IDaaS_server}/api/bff/v1.2/enduser/portal/sso/go_xxxx?access_token=123&timestamp=1667465505282&sign=7148ce32446a6ad6141c2d7b911b81a441765639c125f736a6ffa9abf2c6063a

示例代码(JAVA):

/*
 * Copyright (c) 2016 BeiJing JZYT Technology Co. Ltd
 * www.idsmanager.com
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * BeiJing JZYT Technology Co. Ltd ("Confidential Information").
 * You shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement you
 * entered into with BeiJing JZYT Technology Co. Ltd.
 */
package com.perry.demo;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 2022/11/4
 *
 * @author zouzheng
 */
public class OAuthPortalDemo {
    public static void main(String[] args) {
        System.out.println(getSsoUrl());
    }

    /**
     * SSO发起地址签名:
     * 步骤:
     * 1. 将所有URL参数通过key升序排序,并去掉空值,拼接成url参数形式(key+value&key=value)
     * 2. 将拼接后的字符串进行sha256加密,apiSecret当盐值
     * 3. 将签名参数放入参数中,并进行跳转
     */
    private static String getSsoUrl() {
        Map<String, String> params = new HashMap<>(3);
        //前提: 准备参数
        //回调地址,非必传
        params.put("redirectUrl", "");
        //oauth token
        params.put("access_token", "123");
        //当前时间戳,注意SSO会校验,5分钟内有效
        params.put("timestamp", String.valueOf(System.currentTimeMillis()));

        //step one : 将所有URL参数通过key升序排序,并去掉空值,拼接成url参数形式(key+value&key=value)
        final String paramsString = params.entrySet().stream()
                .filter(e -> StringUtils.isNotBlank(e.getValue()))
                .sorted(Map.Entry.comparingByKey(String::compareTo))
                .map(e -> e.getKey() + "=" + e.getValue())
                .collect(Collectors.joining("&"));
        //step two : 将拼接后的字符串进行sha256加密,clientSecret当盐值
        //access_token=123&timestamp=1667465505282 签名后值为 d827c016f336676e23affa583e985bcbcc4772ec4d370afd7b2dc98acaa85c9d

        String apiSecret = "123456";
        //示例2 ,这里用commons-codec开源库
        final String sign = DigestUtils.sha256Hex(paramsString + apiSecret);
        System.out.println(sign);
        //示例2,用java原生方法
        final String sign2 = sha256Hex(paramsString, apiSecret);


        //step three : 将签名参数放入参数中,进行跳转
        //门户SSO发起地址,同原有发起地址,注意后面的UUID需要替换
        String ssoUrl = "https://{IDP_HOST}/api/bff/v1.2/enduser/portal/sso/go_{uuid}?";
        return ssoUrl + paramsString + "&sign=" + sign;
    }

    public static String sha256Hex(String password, String salt) {

        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(password.getBytes());
            md.update(salt.getBytes());
            byte[] bytes = md.digest();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < bytes.length; i++) {
                sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
            }
            generatedPassword = sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return generatedPassword;
    }
}

5.4. 门户衔接应用代办

门户中通常会挂应用内代办或者消息内容,门户开发者希望可以通过IDaaS将此类流程串联起来,应用将应用代办消息推送给门户,这条消息除了内容之外一般还包含一个回跳地址,门户上文接口获取的 startUrl加上回跳地址打通门户到应用消息的认证路径,进入应用后应用自主处理跳转地址,接口信息如下:
请求地址: {startUrl}?redirect_url={应用回跳地址}
请求方法: 浏览器跳转
注意,不同登录方式,访问方式有所不同,具体见上文

5.5. 对接IDaaS退出

当门户退出时,可调用IDaaS提供的全局统一退出地址,并传递一个门户portal的登录地址redirect_url,待IDaaS注销会话和注销用户的访问令牌后,则会重定向至门户的登录地址,接口地址信息如下:
请求地址: /public/sp/slo/{appId}
请求方法: GET,POST
请求参数:

参数名称

参数类型

是否必须

示例

备注

appId

text

idaasoauth2

应用ID,此处为门户应用

redirect_url

text

https%3A%2F%2Fwww.idsmanager.com%2F

退出成功后跳转的URL地址,可选(若不传或为空则跳转回IDP登录页),此处传递门户登录地址,注意使用urlencode

access_token

text

xxxxxxx

IDP签发的用户令牌access_token,可选,若有值则将access_token置为无效状态

请求示例:http://{IDaaS_server}/public/sp/slo/idaasoauth2?access_token={access_token}&redirect_url={portal_login_url}
注意事项:推荐使用POST请求方式,将参数redirect_url与access_token放在form表单中提交。

更多信息参考文档单点退出(SLO)】-【SP SLO】-【3.1 全局退出

6. 常见问题

6.1. 如何通过用户access_token获取用户更多信息

在获取到AT(Access Token)后,门户可以接着向IDaaS发送进一步的请求, 以获取到用户信息
Request URI: https://{IDaaS_server}/api/bff/v1.2/oauth2/userinfo
调用接口时传入access_token有两种方式:
URL值后:URL?access_token={access_token}
Header里面:Authorization Bearer {access_token}(注意 bearer与access_token之间的空格)
推荐使用header方式
接口说明: 获取用户详细信息
请求方式: GET
返回参数响应示例

{
    "success": true,
    "code": "200",
    "message": null,
    "requestId": "149DA248-8F49-4820-B87A-5EA36D932354",
    "data": {
        "sub": "823071756087671783",
        "ou_id": "2079225187122667069",
        "nickname": "test",
        "phone_number": 11136618971,
        "ou_name": "研发部",
        "email": "test@test.com",
        "username": "test"
    }
}

参数说明

参数

类型

示例值

描述

success

boolean

true

是否成功

code

String

200

状态码

message

String

null

返回消息

requestId

String

B3776BB1-930F-4581-B4C3-18F2D7D136CA

请求ID

data

Object

响应数据

└sub

String

823071756087671783

外部ID,唯一,可作主键

└ouid

String

2079225187122667069

父组织外部ID,唯一,可作主键

└nickname

String

test

昵称

└phone_number

String

11136618971

手机号

└ou_name

String

研发部

父组织名称

└email

String

test@test.com

邮箱

└username

String

test

用户名

错误码说明

HttpCode

错误码

错误信息

描述

401

Unauthorized

Unauthorized

未授权的访问

403

Forbidden

Forbidden

无权限访问

404

ResourceNotFound

ResourceNotFound

访问的资源不存在

415

UnsupportedMediaType

UnsupportedMediaType

不支持的媒体类型

500

InternalError

The request processing has failed due to some unknown error, exception or failure.

发生未知错误

7. 附录

7.1. IDaaS Portal接入Demo¶

链接地址:https://github.com/aliyun-idaas/idp4-developer-portal-demo
克隆后请先查看README.md文档参考步骤修改对应参数进行测试。