feat: 增加可信应用免登录接入
This commit is contained in:
124
ai_knowledge_base_v2/docs/应用免登录接入说明.md
Normal file
124
ai_knowledge_base_v2/docs/应用免登录接入说明.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# 千问千答应用免登录接入说明
|
||||
|
||||
## 1. 适用场景
|
||||
|
||||
学员已经在其他可信应用完成登录,从该应用进入千问千答时,不再重复输入手机号和验证码。
|
||||
|
||||
接入应用只能在自己的服务端申请一次性授权码。应用密钥不得写入网页、App 安装包、小程序或公开仓库。
|
||||
|
||||
## 2. 准备工作
|
||||
|
||||
管理员在千问千答后台的“应用接入”页面完成:
|
||||
|
||||
1. 填写千问千答用户端公网地址;
|
||||
2. 新增接入应用;
|
||||
3. 保存系统只展示一次的应用密钥;
|
||||
4. 如需学员从千问千答返回来源应用,配置完整的回跳地址白名单。
|
||||
|
||||
首次免登录时,接入应用必须提供学员在本应用内的稳定用户 ID 和已经由本应用验证过的手机号。手机号仅用于首次匹配千问千答学员,后续以账号绑定为准。
|
||||
|
||||
## 3. 申请一次性授权码
|
||||
|
||||
接口:
|
||||
|
||||
```text
|
||||
POST /api/integration/sso/ticket
|
||||
```
|
||||
|
||||
请求体示例:
|
||||
|
||||
```json
|
||||
{"externalUserId":"student-10086","verifiedPhone":"13800138000","displayName":"张同学","returnUrl":"https://student.example.com/home"}
|
||||
```
|
||||
|
||||
请求头:
|
||||
|
||||
```text
|
||||
X-App-Id: 后台生成的应用ID
|
||||
X-Timestamp: 当前 Unix 秒级时间戳
|
||||
X-Nonce: 每次请求生成的唯一随机字符串,长度至少16位
|
||||
X-Signature: HMAC-SHA256 十六进制签名
|
||||
```
|
||||
|
||||
签名原文:
|
||||
|
||||
```text
|
||||
时间戳 + "\n" + 随机字符串 + "\n" + SHA256(原始请求体字节)
|
||||
```
|
||||
|
||||
使用应用密钥对签名原文执行 HMAC-SHA256,输出小写十六进制字符串。必须对实际发送的原始 JSON 字节计算摘要,不要对解析后重新排序的对象签名。
|
||||
|
||||
Python 示例:
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import secrets
|
||||
import time
|
||||
|
||||
body = json.dumps(
|
||||
{
|
||||
"externalUserId": "student-10086",
|
||||
"verifiedPhone": "13800138000",
|
||||
"displayName": "张同学",
|
||||
"returnUrl": "https://student.example.com/home",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
separators=(",", ":"),
|
||||
).encode("utf-8")
|
||||
timestamp = str(int(time.time()))
|
||||
nonce = secrets.token_urlsafe(24)
|
||||
canonical = f"{timestamp}\n{nonce}\n{hashlib.sha256(body).hexdigest()}".encode()
|
||||
signature = hmac.new(APP_SECRET.encode(), canonical, hashlib.sha256).hexdigest()
|
||||
```
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"code": "一次性授权码",
|
||||
"expiresInSeconds": 60,
|
||||
"entryUrl": "https://qa.example.com/?sso_code=一次性授权码"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
接入应用收到响应后,让浏览器、App WebView 或小程序 WebView 打开 `entryUrl`。授权码只能使用一次,60 秒后自动失效。
|
||||
|
||||
## 4. 用户匹配规则
|
||||
|
||||
- 已经绑定:按“应用 ID + 外部用户 ID”直接找到千问千答学员;
|
||||
- 首次进入:使用已验证手机号匹配现有学员并建立绑定;
|
||||
- 手机号不在学员名单:拒绝进入,不自动创建无权益账号;
|
||||
- 学员已禁用、尚未生效或权益已过期:拒绝进入;
|
||||
- 同一应用中的一个千问千答学员只能绑定一个外部用户 ID。
|
||||
|
||||
绑定错误时,管理员可以在“应用接入 → 账号绑定”中解除绑定,学员下次进入时重新核对手机号。
|
||||
|
||||
## 5. 安全规则
|
||||
|
||||
- 所有生产接口必须使用 HTTPS;
|
||||
- 应用密钥只保存在接入应用服务端;
|
||||
- 请求时间戳允许误差不超过 5 分钟;
|
||||
- `X-Nonce` 在 5 分钟内不得重复;
|
||||
- `returnUrl` 必须与后台白名单完全匹配;
|
||||
- Redis 不可用时停止签发和兑换授权码,不降级为长期 Token;
|
||||
- 更新应用密钥后,旧密钥立即失效;
|
||||
- 停用接入应用后,该应用不能继续申请或兑换登录态;
|
||||
- 登录成功和失败都会写入登录审计。
|
||||
|
||||
## 6. 验收清单
|
||||
|
||||
- 已登录来源应用的学员点击后直接进入千问千答;
|
||||
- 首次进入能按已验证手机号正确绑定;
|
||||
- 后续进入不再依赖手机号;
|
||||
- 同一个授权码第二次兑换失败;
|
||||
- 错误签名、过期时间戳、重复随机数均被拒绝;
|
||||
- 非白名单回跳地址被拒绝;
|
||||
- 禁用或过期学员不能进入;
|
||||
- 停用应用和更新密钥立即生效;
|
||||
- 手机号验证码登录仍可正常使用。
|
||||
Reference in New Issue
Block a user