AI應用接入
更新時間 2025-07-17 20:17:32
最近更新時間: 2025-07-17 20:17:32
分享文章
接口功能介紹
AI應用中心登錄接入是為了實現用戶便捷安全自動登錄AI空間的合作類AI應用。開發者將AI應用接入后上架AI空間,即可實現用戶自動登錄AI應用功能。
架構流程
接口約束
無
請求參數
請求頭header參數
無
請求body參數
| 參數 | 是否必填 | 參數類型 | 說明 |
|---|---|---|---|
| ticket | 是 | String | 有效期是60秒,用于第三方服務回調AI應用后臺,查詢用戶信息 |
| source | 是 | String | 來源類型,表明該請求從那一方請求, 向服務方申請提供 |
響應參數
| 參數 | 參數類型 | 說明 |
|---|---|---|
| auid | String | 用戶唯一標識 |
| name | String | 用戶名稱 |
| tenantId | Long | 當前租戶標識 [0.2.6版加入] |
| mobile | String | 手機號,詢問用戶授權后提供, 為空時為用戶不授權 [0.2.6版加入] |
| bssResourceId | String | BSS系統資源id, 默認為空 |
枚舉參數
無
請求示例
curl //eaichat.daliqc.cn/ai/portal/v1/app/queryUserInfoByTicket?ticket=xxxxxx&source=xxx響應示例一
{
"resultCode": 0,
"resultMsg": "success",
"data": {"auid":"xxxxxxxxxx","name":"張三","tenantId":123,"mobile":"18100001111"}
}響應示例二
{
"resultCode": 10002,
"resultMsg": "ticket 無效",
"data": null
}測試環境URL
//eai-test.bgzs.site:1443/ai/portal/v1/app/queryUserInfoByTicket(外網訪問,請提供公網出口IP,配置白名單)生產環境URL
# 示例
wget //eaichat.daliqc.cn/ai/portal/v1/app/queryUserInfoByTicket?ticket=111&source=techexxx接口驗簽
對ai/portal/v1/app/queryUserInfoByTicket 請求接口URL進行參數驗簽
請求頭需帶
| 參數名 | 參數值類型 | 值描述 |
|---|---|---|
| YL-Signature | <string> | |
| YL-Timestamp | <uint64> | 毫秒時間戳 |
| YL-Random | <string> | 8位隨機數,例如Cq8s9vqi |
| YL-3rd-Appcode | <string> | ak 管理員提供 |
第三方應用的簽名生成由下面幾項決定:
URL參數params(目前只支持驗簽URL參數,body參數不驗)
時間戳YL-Timestamp
隨機數YL-Random(由客戶端隨機生成,用于區分極短時間內發起的相同參數請求)
第三方應用標識碼YL-3rd-Appcode
本地預埋的sk
簽名YL-Signature生成方法:
1.將請求參數鍵值對中的key按照字母升序排序,而后將排好序的參數以“&”為分隔符進行拼接,產生一個字符串;
2.在步驟1字符串的基礎上,再按順序將sk、YL-Timestamp、YL-Random、YL-3rd-Appcode以“&”為分隔符拼接在末尾;
3.基于單向哈希算法SHA256,應用密鑰sk對步驟2的字符串生成簽名。
例如:SHA256(param1=123¶m2=456&sk×tamp&random&appcode)
| 參數 | 參數類型 | 參數說明 |
|---|---|---|
| ak | String | 用于請求參數簽名的ak 向服務方申請提供 |
| sk | String | 用于請求參數簽名的私鑰,私發 |
Java簽名算法例子
package com.ctg.ai.platform.demo.util;
import org.apache.commons.lang3.RandomStringUtils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class OpenApiUtil {
private static final String KEY_SEPARATOR = "&";
/** ak 由管理員提供 */
private static final String HTTP_HEADER_YL_APPCODE = "YL-3rd-Appcode";
/** 時間戳 單位毫秒 */
private static final String HTTP_HEADER_YL_TIMESTAMP = "YL-Timestamp";
/** 8位隨機數 */
private static final String HTTP_HEADER_YL_RANDOM = "YL-Random";
/** 簽名 */
private static final String HTTP_HEADER_YL_SIGNATURE = "YL-Signature";
/**
* 組裝驗簽頭部
* @param appKey
* @param appSecret
* @param paramMap
* @return
*/
public static Map<String, String> buildHeaders(String appKey, String appSecret, Map<String, String[]> paramMap) {
if (appKey == null || appSecret == null) {
return null;
}
// 組裝前置條件
Long timestamp = System.currentTimeMillis();
String random = RandomStringUtils.randomAlphanumeric(8);
// 計算簽名
String signature = buildSignature(appKey, appSecret, random, timestamp, paramMap);
// 組裝HTTP頭部
return buildHeaders(appKey, random, timestamp, signature);
}
/**
* 簽名
* @param appKey
* @param appSecret
* @param random
* @param timestamp
* @param paramMap
* @return
*/
private static String buildSignature(String appKey, String appSecret, String random, Long timestamp, Map<String, String[]> paramMap) {
if (appKey == null || appSecret == null || random == null || timestamp == null) {
return null;
}
// 組成簽名前原串
StringBuilder sb = new StringBuilder();
if (paramMap != null && paramMap.size() > 0) {
List<String> keys = new ArrayList<>(paramMap.keySet());
// 參數排序 (ASCII 升序)
Collections.sort(keys);
for (String key : keys) {
String val = paramMap.get(key)[0];
sb.append(key).append("=").append(val).append(KEY_SEPARATOR);
}
}
sb.append(appSecret).append(KEY_SEPARATOR)
.append(timestamp).append(KEY_SEPARATOR)
.append(random).append(KEY_SEPARATOR)
.append(appKey);
//System.out.println("text=\n"+sb);
return sha256(sb.toString().getBytes(StandardCharsets.UTF_8));
}
/**
* 組裝驗簽頭部
* @param appKey
* @param random
* @param timestamp
* @param signature
* @return
*/
private static Map<String, String> buildHeaders(String appKey, String random, Long timestamp, String signature) {
Map<String, String> headers = new HashMap<>(4);
if (appKey != null) {
headers.put(HTTP_HEADER_YL_APPCODE, appKey);
}
if (random != null) {
headers.put(HTTP_HEADER_YL_RANDOM, random);
}
if (timestamp != null) {
headers.put(HTTP_HEADER_YL_TIMESTAMP, ""+timestamp);
}
if (signature != null) {
headers.put(HTTP_HEADER_YL_SIGNATURE, signature);
}
return headers;
}
// 哈希部分邏輯
private static final char[] LOWER_HEX_DIGITS =
new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static final String MD5_ALGORITHM_NAME = "MD5";
private static final String SHA256_ALGORITHM_NAME = "SHA-256";
public static String md5(byte[] data) {
return encodeAsHexString(MD5_ALGORITHM_NAME, data);
}
public static String sha256(byte[] data) {
return encodeAsHexString(SHA256_ALGORITHM_NAME, data);
}
/**
* 使用 {@code algorithmName} 加密算法編碼 {@code data}
* @param algorithmName 算法名
* @param data 被編碼的字節數組
* @return {@code data} 編碼后的小寫 16 進制形式字符串
* @throw IllegalStateException 當找不到 {@code algorithmName} 對應算法
*/
private static String encodeAsHexString(String algorithmName, byte[] data) {
try {
byte[] hash = MessageDigest.getInstance(algorithmName).digest(data);
return new String(encodeHex(hash));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("MessageDigest 找不到算法:" + algorithmName, e);
}
}
private static char[] encodeHex(byte[] data) {
int l = data.length;
char[] arr = new char[l << 1];
int i = 0;
for(int j = 0; i < l; ++i) {
arr[j++] = LOWER_HEX_DIGITS[(240 & data[i]) >>> 4];
arr[j++] = LOWER_HEX_DIGITS[15 & data[i]];
}
return arr;
}
public static void main(String[] args) {
String ak = "ak";
String sk = "sk";
Map<String, String[]> paramMap = new HashMap<>(2);
paramMap.put("param2", new String[]{"456","789"});
paramMap.put("param1", new String[]{"123"});
Map<String, String> header = buildHeaders(ak, sk, paramMap);
System.out.println("header="+header);
}
}