# AI比价系统单点登录
# 一、基本介绍
用户系统可以通过生成登录链接的方式单点登录到慧讯网AI比价数据系统。
# 二、使用方法
# 登录方式
用户系统通过特殊规则生成登录链接,用户点击该链接即会单点登录到慧讯网AI比价数据系统。
生成链接有效时间1小时,只能使用一次。
# 前置条件
- 用户系统需要提前申请
appKey
和appSecret
(应用程序密钥,用户在购买慧讯网AI比价数据系统API服务时获得的唯一识别密钥
)。 - 用户系统中需要提前同步用户的账号信息到慧讯网AI比价数据系统。
# 链接格式
生成的单点登录链接格式:
https://srm.iccchina.com/#/open/auto_login?token={secretDigest}&appKey={appKey}×tamp={timestamp}
# 链接参数说明
参数 | 类型 | 是否必须 | 说明 |
---|---|---|
secretDigest | String | ✔️ |
appKey | String | ✔️ |
timestamp | 当前时间戳(13位) | ✔️ |
redirect_uri | 登录后重定向的地址 | ✖️ |
secretDigest
为 通过 aes-256-ctr
模式 加密 用户的 UUID
生成的密钥。
其中UUID
为自身OA系统中账号的唯一性约束(每个用户对应一个,同步组织结构时会传递到慧讯网AI比价数据系统)。secretKey
作为密钥(32位)。timestamp
为当前时间戳,需要同步在链接中传递,timestamp
+ '000' 作为偏移(凑够16位即可)。
加密方式详见下文示例代码。
# 链接生成示例
https://srm.iccchina.com/#/open/auto_login?token=8123497494&appKey=12345678901234567890123456789012×tamp=1744358531893
# 三、secretDigest生成方式
# 生成secretDigest的说明
使用aes-256-ctr
模式加密生成secretDigest
参数。
其中:
加密的内容为用户的UUID
。
密钥为appSecret
。
偏移为timestamp
+ '000' 凑够16个字节(timestamp默认是13个字节的,如果是其他数量的字节数,一直往后面加0凑够16个字节作为 偏移)。
# 生成secretDigest示例代码(NodeJS)
let crypto = require('crypto')
function encrypt(text) {
if (!text) {
return ''
}
let algorithm = 'aes-256-ctr'
let password = '12345678901234567890123456789012' // 密钥:appSecret
let piv = '1234567890123456' // 偏移:timestamp + '000' 凑够16个字节(timestamp默认是13个字节的,如果是其他数量的字节数,一直往后面加0凑够16个字节作为 偏移)
try {
let cipher = crypto.createCipheriv(algorithm, password, piv)
let crypted = cipher.update(text, 'utf8', 'base64')
crypted += cipher.final('base64')
return crypted
} catch (e) {
console.log(e)
}
}
let encrypted = encrypt("8123497494")
console.log(encrypted)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 生成secretDigest示例代码(Java)
import java.lang.Math; // headers MUST be above the first class
import java.util.Base64;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
// one class needs to have a main() method
public class App
{
private static void log(String s)
{
System.out.print("\r\n"+s);
}
public static SecureRandom IVGenerator() {
return new SecureRandom();
}
// arguments are passed using the text field below this editor
public static void main(String[] args)
{
String valueToEncrypt = "message";
String key = "12345678901234567890123456789012"; // 密钥:appSecret
byte[] iv = new String("1234567890123456").getBytes(); // 偏移:timestamp + '000' 凑够16个字节(timestamp默认是13个字节的,如果是其他数量的字节数,一直往后面加0凑够16个字节作为 偏移)
int ivLength=16;
String encrypted = "";
String decrypted = "";
//ENCODE part
SecureRandom IVGenerator = IVGenerator();
byte[] encryptionValueRaw = key.getBytes();
try {
Cipher encryptionCipher = Cipher.getInstance("AES/CTR/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptionValueRaw, "AES"), new IvParameterSpec(iv));
//encrypt
byte[] cipherText = encryptionCipher.doFinal(valueToEncrypt.getBytes());
ByteBuffer byteBuffer = ByteBuffer.allocate(cipherText.length);
//storing IV in first part of whole message
//store encrypted bytes
byteBuffer.put(cipherText);
//concat it to result message
byte[] cipherMessage = byteBuffer.array();
//and encrypt to base64 to get readable value
encrypted = new String(Base64.getEncoder().encode(cipherText));
} catch (Exception e) {
throw new IllegalStateException(e);
}
//END OF ENCODE CODE
log("encrypted and saved as Base64 : "+encrypted);
///DECRYPT CODE :
try {
//decoding from base64
byte[] cipherMessageArr = Base64.getDecoder().decode(encrypted);
// DECRYPT start
Cipher decryptionCipher = Cipher.getInstance("AES/CTR/NoPadding");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(encryptionValueRaw, "AES");
decryptionCipher.init(Cipher.DECRYPT_MODE,secretKeySpec , ivSpec);
//decrypt
byte[] finalCipherText = decryptionCipher.doFinal(cipherMessageArr);
//converting to string
String finalDecryptedValue = new String(finalCipherText);
decrypted = finalDecryptedValue;
} catch (Exception e) {
throw new IllegalStateException(e);
}
log("decrypted from Base64->aes128 : "+decrypted);
//END OF DECRYPT CODE
}
}
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 四、登录后重定向页面设置
用户登录成功后,会重定向到慧讯网AI比价数据系统,默认为首页
如果需要登录后重定向到其他页面,可以通过redirect_uri
参数设置。
# 生成带有重定向地址的单点登录链接示例(Javascript)
let redirect_uri = encodeURIComponent('https://srm.iccchina.com/#/special/price_review/project_show/8814ad42284a816d4da89528b0d87d8d?user_price_id=c2d19f079428f909bf73f73b5f00f705&type=icc_price')
let timestamp = Date.now()
let appKey = '12345678901234567890123456789012'
let secretDigest = '123456789'
let url = `URL_ADDRESSrm.iccchina.com/#/open/auto_login?token=${secretDigest}&appKey=${appKey}×tamp=${timestamp}&redirect_uri=${redirect_uri}`
2
3
4
5
# 重定向地址示例
生成的单点登录链接格式:
https://srm.iccchina.com/#/open/auto_login?token=8123497494&appKey=12345678901234567890123456789012×tamp=1744358531893&redirect_uri=https%3A%2F%2Fsrm.iccchina.com%2F%23%2Fspecial%2Fprice_review%2Fproject_show%2F8814ad42284a816d4da89528b0d87d8d%3Fuser_price_id%3Dc2d19f079428f909bf73f73b5f00f705%26type%3Dicc_price