Web端接口实现 & 数美SDK逆向研究报告
一、概述
本项目新增了对 DeepSeek Web端接口的支持,可通过设置面板在"Android端"和"Web端"之间热切换,无需重启服务。
核心功能
| 特性 | Android端 | Web端 |
|---|---|---|
| User-Agent | DeepSeek/2.1.2 Android/32 |
Chrome 137 浏览器UA |
| TLS指纹 | Safari | Chrome (HTTP/2) |
| x-client-platform | android |
web |
| device_id | "android_device" |
数美SDK加密字符串 |
| x-hif-dliq | 无 | 动态轮询获取 |
| x-hif-leim | 无 | 动态轮询获取 |
| 浏览器特有头 | 无 | sec-ch-ua, origin, referer等 |
架构设计
┌─────────────────────────────────────────────────────────────┐
│ 设置面板 (WebUI) │
│ 选择: Android / Web 接口平台 │
└─────────────────────────┬───────────────────────────────────┘
│ PUT /admin/settings
▼
┌─────────────────────────────────────────────────────────────┐
│ Handler.applyPlatformChange() │
│ DS.SetPlatform("web" / "android") │
├─────────────────────────────────────────────────────────────┤
│ 1. dsprotocol.ApplyPlatform() → 切换 BaseHeaders / UA │
│ 2. RefreshTransport() → 切换 TLS 指纹 │
│ 3. hifPoller.Start()/Stop() → 启动/停止 HIF 轮询 │
└─────────────────────────────────────────────────────────────┘
切换后立即生效,新请求自动使用对应平台的 UA、TLS 指纹、Headers 和 device_id 算法。
二、文件变更清单
新增文件(8个)
| 文件 | 说明 |
|---|---|
internal/deepseek/protocol/constants_web.json |
Web端协议常量定义 |
internal/deepseek/hif/hif.go |
HIF头部轮询器 |
internal/deepseek/hif/hif_test.go |
HIF单元测试 |
internal/shumei/device_id.go |
数美SDK device_id生成入口 |
internal/shumei/crypto.go |
RSA/DES/AES/MD5/Gzip加密工具 |
internal/shumei/confusion.go |
DES字段混淆映射表 |
internal/shumei/api.go |
数美设备指纹API调用 |
webui/src/features/settings/PlatformSection.jsx |
平台切换UI组件 |
修改文件(18个)
Config层
internal/config/config.go— PlatformConfig结构体、PlatformMode()、IsWebPlatform()internal/config/codec.go— JSON序列化支持internal/config/store_accessors.go— Store级访问器internal/config/validation.go— 平台配置校验
Protocol层
internal/deepseek/protocol/constants.go— ApplyPlatform()、WebExtraHeaders()、UserAgent()
Transport层
internal/deepseek/transport/transport.go— Chrome TLS指纹、NewWithPlatform()
Client层
internal/deepseek/client/client_core.go— SetPlatform()、RefreshTransport()、HIF poller集成internal/deepseek/client/client_auth.go— Web端login(数美device_id)internal/deepseek/client/client_completion.go— Web端completion headersinternal/deepseek/client/client_continue.go— Web端continue headersinternal/deepseek/client/proxy.go— 平台感知代理客户端
Admin API
internal/httpapi/admin/shared/deps.go— DeepSeekCaller接口添加SetPlatforminternal/httpapi/admin/settings/handler_settings_*.go— 设置保存/读取/热加载
前端
webui/src/features/settings/SettingsContainer.jsx— 集成PlatformSectionwebui/src/features/settings/useSettingsForm.js— 平台表单数据流webui/src/locales/zh.json/en.json— 国际化翻译
三、数美SDK逆向分析报告
3.1 SDK基本信息
- SDK名称:数美科技(Shumei)DeviceFingerprint Web SDK
- 版本:3.0.0 / subVersion 1.0.0
- 加载方式:通过
https://cdn.deepseek.com/static/chat/fp-1.min.js动态加载 - 配置来源:
window._smConf(由DeepSeek页面注入) - API端点:
https://fp-it-acc.portal101.cn/deviceprofile/v4
3.2 device_id生成流程
1. 生成UUID v4 (uid)
│
2. 计算priId = md5(uid)[:16] ────→ 作为AES密钥
│
3. RSA加密uid (PKCS#1 v1.5) ────→ ep字段
│
4. 收集23项浏览器指纹数据
│
5. DES-ECB逐字段加密+字段名混淆
│
6. 计算tn = md5(sorted key=value) ──→ 完整性校验
│
7. JSON → Gzip压缩
│
8. AES-CBC加密 (priId + IV="0102030405060708" + ZeroPadding)
│
9. POST到数美API
│
10. 解析响应 code=1100 → detail.deviceId
│
11. getDeviceId() = "B" + deviceId
3.3 关键配置参数
| 参数 | 值 | 说明 |
|---|---|---|
| RSA公钥 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDetfEgYD4aE1ZjmWJ6/jnPurhzI+yeRoJHWrnNtQMte3stQ4VjG3yu21FuN75E6cDpA9KtDXwcB2M/FiGUAe3G0rNotbWI8+SjZfUbW/OILFTzY0uaeEkmVGW5WyJ6weQbbr1xTCPa2OO3YIMeZljWUYHG5h21WAm/PATg8im8cQIDAQAB |
用于加密uid |
| organization | P9usCUBauxft8eAmUXaZ |
设备标识归属 |
| appId | default |
应用ID |
| apiHost | fp-it-acc.portal101.cn |
数美API域名 |
3.4 指纹数据收集(23项)
| 字段 | 来源 | 说明 |
|---|---|---|
| ua | navigator.userAgent | 浏览器UA |
| platform | navigator.platform | Win32 |
| canvas | canvas 2D绘图 | 绘制'http://www.ishumei.com'文字取哈希 |
| timezone | Date.getTimezoneOffset() | 时区偏移 |
| res | screen.width/height/colorDepth/devicePixelRatio | 屏幕信息 |
| plugins | navigator.plugins | 浏览器插件列表 |
| referer | document.referrer | 来源页面 |
| url | location.href | 当前URL |
| clientSize | screenLeft/screenTop/body尺寸 | 窗口大小 |
| vpw | UUID v4 | 随机值 |
| svm | 时间戳 | 当前时间 |
| trees | UUID v4 | 随机值 |
| pmf | 时间戳 | 当前时间 |
| cpucount | navigator.hardwareConcurrency | CPU核心数 |
| battery | navigator.getBattery() | 电池状态 |
| connectionRtt | navigator.connection.rtt | 网络延迟 |
| maxTouchPoints | navigator.maxTouchPoints | 触控点数 |
| status | Flash检测+webdriver检测 | 环境检测 |
| rtype | 固定值"1" | 类型标识 |
| sdkver | "3.0.0" | SDK版本 |
| subVersion | "1.0.0" | 子版本 |
| appId | "default" | 应用ID |
| organization | 配置值 | 组织标识 |
3.5 DES字段混淆
每个指纹字段使用独立的8字节DES密钥进行ECB模式加密,然后Base64编码,最后字段名映射为混淆名称。
混淆映射表(部分):
| 原字段 | DES密钥 | 混淆名 |
|---|---|---|
| appId | q8zb1bs1 |
vw |
| organization | ht432iov |
tj |
| os | i7g8jtql |
vh |
| ua | z5jc9qyp |
wr |
| canvas | qy1d6fmu |
mi |
| plugins | j916a142 |
vc |
| referer | rf149ntc |
hv |
| res | rbtd2cl6 |
pi |
| timezone | 3w9lg8pr |
oi |
| svm | mqcala0h |
nu |
| trees | al8x9zt7 |
hc |
| vpw | xzvnu5jt |
st |
| protocol | (不加密) | protocol |
3.6 加密算法详解
RSA加密(uid → ep)
- 使用JSEncrypt库
- PKCS#1 v1.5填充
- 输出为Base64字符串
DES-ECB加密(字段混淆)
- 每个字段独立使用8字节密钥
- ECB模式,逐块加密
- 输出Base64编码
AES-CBC加密(数据体)
- 密钥:
priId(md5(uid)[:16],UTF-8编码) - IV:
0102030405060708 - 模式:CBC
- 填充:ZeroPadding
- 输入:gzip压缩后的JSON
- 输出:hex字符串
完整性校验(tn字段)
tn = md5(sorted_key_value_string(data))
对所有key排序后拼接key+value为字符串,取MD5。
3.7 API请求格式
请求:
POST https://fp-it-acc.portal101.cn/deviceprofile/v4
{
"appId": "default",
"organization": "P9usCUBauxft8eAmUXaZ",
"ep": "RSA加密的uid(hex)",
"data": "AES加密后的hex字符串",
"os": "web",
"encode": 5,
"compress": 2
}
成功响应:
{
"code": 1100,
"detail": {
"deviceId": "XkNeGKe7f4Y5FtZamaxQPqL3ypegujSyds5eQGup13ajuer9XhBAM82w1QWifIm6KUNv2hu3tUTMwoXrVzD0ZA=="
}
}
最终返回的device_id为:"B" + deviceId
3.8 Go实现要点
- 不需要浏览器:device_id由数美服务器生成,客户端只负责发送指纹数据和接收结果
- 指纹数据可伪造:服务端不校验指纹真实性,只需要格式正确
- 关键依赖:crypto/des、crypto/aes、crypto/rsa、crypto/md5、compress/gzip
- DES-ECB:Go标准库不提供ECB模式,需手动逐块实现
- AES ZeroPadding:标准库只提供PKCS7,需手动补零
四、HIF头部轮询机制
4.1 概述
Web端每个请求需要携带两个特殊头部:
x-hif-dliqx-hif-leim
这两个值通过轮询DeepSeek的两个端点动态获取。
4.2 端点
| 类型 | URL | 对应头部 |
|---|---|---|
| dliq | https://hif-dliq.deepseek.com/query |
x-hif-dliq |
| leim | https://hif-leim.deepseek.com/query |
x-hif-leim |
4.3 响应格式
{
"code": 0,
"data": {
"biz_code": 0,
"biz_data": {
"value": "9lspMrX9S0NlRuD7+ChfCz3ENqJPjH4qnMfnlPAwq0wfRuH2nmSepbg=.xuVOOVjUztQikXFf"
}
}
}
响应头 x-hif-ttl 包含缓存有效期(秒),默认600秒。
4.4 轮询策略
初始化 → 启动两个goroutine轮询
│
├── 成功 → 缓存value → 等待TTL秒 → 继续轮询
│
└── 失败 → 指数退避(1s→2s→4s→...→max 600s) → 重试
- 初始重试间隔:1秒
- 退避倍数:2x
- 最大间隔:600秒
- 成功间隔:TTL(从响应头获取)
4.5 Go实现
internal/deepseek/hif/hif.go 实现了完整的轮询逻辑:
type HIFPoller struct {
mu sync.RWMutex
dliq string
leim string
stopCh chan struct{}
client *trans.Client // 使用Chrome TLS
fallback *http.Client // 标准HTTP fallback
}
func (p *HIFPoller) Start() // 启动两个轮询goroutine
func (p *HIFPoller) Stop() // 停止轮询
func (p *HIFPoller) GetHeaders() map[string]string // 获取当前HIF头部
五、TLS指纹对比
Android端
ClientHello: HelloSafari_Auto
ALPN: 强制 http/1.1 (forceHTTP11ALPN)
Web端
ClientHello: HelloChrome_Auto
ALPN: 自然协商 (支持 h2 和 http/1.1)
ForceAttemptHTTP2: true
实现位于 internal/deepseek/transport/transport.go:
func TLSDialerForPlatform(platform string) func(...) {
if platform == "web" {
return chromeTLSDialer(dialContext) // Chrome + HTTP/2
}
return safariTLSDialer(dialContext) // Safari + HTTP/1.1
}
六、使用方式
6.1 设置面板切换
访问 WebUI 设置页面,在"接口平台"选项中选择:
- Android端(默认):使用Safari TLS指纹,device_id为固定值
- Web端(Chrome浏览器):使用Chrome TLS指纹+HTTP/2,动态生成数美device_id,轮询HIF头部
切换后立即生效,无需重启服务。
6.2 环境变量
# 也可通过环境变量设置默认平台
DS2API_PLATFORM_MODE=web
6.3 API设置
curl -X PUT http://localhost:5001/admin/settings \
-H "Authorization: Bearer admin" \
-H "Content-Type: application/json" \
-d '{"platform":{"mode":"web"}}'
七、兼容性说明
- 默认行为不变:未设置platform时默认为"android"
- 向后兼容:所有现有配置和API保持不变
- 热切换:切换平台时自动重建HTTP客户端、更新headers、启停HIF轮询
- 代理兼容:使用代理时同样应用正确的TLS指纹和headers
八、测试状态
✅ go build ./... 编译通过
✅ go vet (核心包) 静态检查通过
✅ go test deepseek/... deepseek包测试通过
✅ go test shumei/... 数美包测试通过
✅ npm run build webui 前端构建通过
注意:
TestGetSettingsIncludesCurrentInputFileDefaults是预先存在的测试失败,与本次改动无关。