300 lines
7.6 KiB
Markdown
300 lines
7.6 KiB
Markdown
# 修复UserSig生成算法错误
|
||
|
||
## 🔍 问题发现
|
||
|
||
通过对比腾讯云官方生成的UserSig和我们系统生成的UserSig,发现格式完全不同:
|
||
|
||
### 腾讯云官方生成(正确)✅
|
||
```
|
||
eJwtjEEPwTAYQP9Lz7J8rXWdJS4OhMwFw1yW0q4*zWimGSH*u5kd33vJe5NNug4aXZOEsADIoGNU*uqxxE476bHFYtjHu7LSOVQkoREAZUJQ*Bf9dFjr1nPOGUBvPVY-JwQLGfBw1F-QtO9Jtrsd1OlcHe28iS*wTOXCZrGSj9K*ZF6uoi2fTWFvTD4mny8uFDNl
|
||
```
|
||
- 格式:gzip压缩 + Base64 URL安全编码
|
||
- 特点:包含 `*` 和 `-` 字符
|
||
- 长度:约150-200字符
|
||
|
||
### 我们系统生成(错误)❌
|
||
```
|
||
eyJUTFMuc2lnIjoiVmRNRDdcL2IyMTF1MHpDSjFuNkc1aFNxNitnMm5rdm11Mkh1WnRyZVwvYTI0PSIsIlRMUy52ZXIiOiIyLjAiLCJUTFMuaWRlbnRpZmllciI6InBhdGllbnRfNCIsIlRMUy5zZGthcHBpZCI6MTYwMDEyNzcxMCwiVExTLmV4cGlyZSI6MTU1NTIwMDAsIlRMUy50aW1lIjoxNzcyNDE4MTcyfQ==
|
||
```
|
||
- 格式:仅Base64编码,**缺少gzip压缩**
|
||
- 特点:以 `==` 结尾,标准Base64
|
||
- 长度:约250-300字符
|
||
|
||
解码后是纯JSON:
|
||
```json
|
||
{
|
||
"TLS.sig": "VdMD7/b211u0zCJ1n6G5hSq6+g2nkvmu2HuZtre/a24=",
|
||
"TLS.ver": "2.0",
|
||
"TLS.identifier": "patient_4",
|
||
"TLS.sdkappid": 1600127710,
|
||
"TLS.expire": 15552000,
|
||
"TLS.time": 1772418172
|
||
}
|
||
```
|
||
|
||
## ❌ 问题根源
|
||
|
||
在 `DiagnosisLogic.php` 中,`generateUserSig()` 方法使用了**自定义的错误实现**,而不是官方的 `TLSSigAPIv2` 类。
|
||
|
||
### 错误的实现
|
||
|
||
```php
|
||
// ❌ 错误:缺少gzip压缩步骤
|
||
private static function generateUserSig(...)
|
||
{
|
||
$sigDoc = [
|
||
'TLS.ver' => '2.0',
|
||
'TLS.identifier' => $userId,
|
||
// ...
|
||
];
|
||
|
||
$base64Doc = base64_encode(json_encode($sigDoc));
|
||
$signature = hash_hmac('sha256', $base64Doc, $secretKey, true);
|
||
|
||
$userSig = [
|
||
'TLS.sig' => base64_encode($signature),
|
||
// ...
|
||
];
|
||
|
||
return base64_encode(json_encode($userSig)); // ❌ 缺少gzip压缩
|
||
}
|
||
```
|
||
|
||
### 正确的流程
|
||
|
||
腾讯云官方算法:
|
||
```
|
||
1. 构建签名内容
|
||
↓
|
||
2. HMAC-SHA256签名
|
||
↓
|
||
3. 构建完整的JSON文档
|
||
↓
|
||
4. gzip压缩 ⭐ 关键步骤
|
||
↓
|
||
5. Base64 URL安全编码
|
||
↓
|
||
6. 返回UserSig
|
||
```
|
||
|
||
我们的错误实现:
|
||
```
|
||
1. 构建签名内容
|
||
↓
|
||
2. HMAC-SHA256签名
|
||
↓
|
||
3. 构建完整的JSON文档
|
||
↓
|
||
4. Base64编码 ❌ 缺少gzip压缩
|
||
↓
|
||
5. 返回UserSig
|
||
```
|
||
|
||
## ✅ 解决方案
|
||
|
||
使用官方的 `TLSSigAPIv2` 类,它已经实现了正确的算法。
|
||
|
||
### 修改前
|
||
|
||
```php
|
||
private static function generateUserSig(int $sdkAppId, string $secretKey, string $userId, int $expire = 86400)
|
||
{
|
||
try {
|
||
$current = time();
|
||
$sigDoc = [
|
||
'TLS.ver' => '2.0',
|
||
'TLS.identifier' => $userId,
|
||
'TLS.sdkappid' => $sdkAppId,
|
||
'TLS.expire' => $expire,
|
||
'TLS.time' => $current
|
||
];
|
||
|
||
$base64Doc = base64_encode(json_encode($sigDoc));
|
||
$signature = hash_hmac('sha256', $base64Doc, $secretKey, true);
|
||
$base64Signature = base64_encode($signature);
|
||
|
||
$userSig = [
|
||
'TLS.sig' => $base64Signature,
|
||
'TLS.ver' => '2.0',
|
||
'TLS.identifier' => $userId,
|
||
'TLS.sdkappid' => $sdkAppId,
|
||
'TLS.expire' => $expire,
|
||
'TLS.time' => $current
|
||
];
|
||
|
||
return base64_encode(json_encode($userSig)); // ❌ 错误
|
||
} catch (\Exception $e) {
|
||
return false;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 修改后
|
||
|
||
```php
|
||
private static function generateUserSig(int $sdkAppId, string $secretKey, string $userId, int $expire = 86400)
|
||
{
|
||
try {
|
||
// ✅ 使用官方的TLSSigAPIv2类
|
||
$api = new \app\common\service\TLSSigAPIv2($sdkAppId, $secretKey);
|
||
$userSig = $api->genUserSig($userId, $expire);
|
||
|
||
return $userSig;
|
||
} catch (\Exception $e) {
|
||
\think\facade\Log::error('生成UserSig失败: ' . $e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 📝 TLSSigAPIv2 的正确实现
|
||
|
||
官方类的关键代码:
|
||
|
||
```php
|
||
private function __genSig($identifier, $expire, $userbuf, $userbuf_enabled)
|
||
{
|
||
$current = time();
|
||
$sigDoc = [
|
||
'TLS.ver' => '2.0',
|
||
'TLS.identifier' => strval($identifier),
|
||
'TLS.sdkappid' => intval($this->sdkappid),
|
||
'TLS.expire' => intval($expire),
|
||
'TLS.time' => intval($current)
|
||
];
|
||
|
||
// 生成签名
|
||
$sig = $this->hmacsha256($identifier, $current, $expire, $base64_userbuf, $userbuf_enabled);
|
||
$sigDoc['TLS.sig'] = base64_encode($sig);
|
||
|
||
// 转JSON
|
||
$json_text = json_encode($sigDoc);
|
||
|
||
// ⭐ 关键:gzip压缩
|
||
$compressed = gzcompress($json_text);
|
||
|
||
// ⭐ 关键:URL安全的Base64编码
|
||
return $this->base64_url_encode($compressed);
|
||
}
|
||
|
||
// URL安全的Base64编码
|
||
private function base64_url_encode($input)
|
||
{
|
||
return str_replace(['+', '/', '='], ['*', '-', '_'], base64_encode($input));
|
||
}
|
||
```
|
||
|
||
## 🎯 影响范围
|
||
|
||
这个修复会影响所有生成UserSig的地方:
|
||
|
||
1. **医生通话签名** - `getCallSignature()`
|
||
2. **患者通话签名** - `getPatientSignature()`
|
||
3. **患者账号创建** - `createPatientTrtcAccount()`
|
||
|
||
所有这些方法都调用 `generateUserSig()`,现在都会使用正确的算法。
|
||
|
||
## 🧪 测试方法
|
||
|
||
### 1. 清除旧数据
|
||
|
||
```sql
|
||
-- 清除旧的患者TRTC账号(UserSig格式错误)
|
||
TRUNCATE TABLE zyt_tcm_patient_trtc;
|
||
```
|
||
|
||
### 2. 重新生成UserSig
|
||
|
||
```bash
|
||
# 方式A:通过新增诊单
|
||
# 进入诊单管理 > 新增诊单 > 保存
|
||
|
||
# 方式B:运行测试脚本
|
||
cd server
|
||
php test_usersig.php
|
||
```
|
||
|
||
### 3. 验证格式
|
||
|
||
查看数据库中的UserSig:
|
||
|
||
```sql
|
||
SELECT user_id, user_sig FROM zyt_tcm_patient_trtc ORDER BY id DESC LIMIT 1;
|
||
```
|
||
|
||
**正确的格式**应该类似:
|
||
```
|
||
eJwtjEEPwTAYQP9Lz7J8rXWdJS4OhMwFw1yW0q4*zWimGSH*...
|
||
```
|
||
|
||
**错误的格式**(旧数据):
|
||
```
|
||
eyJUTFMuc2lnIjoiVmRNRDdcL2IyMTF1MHpDSjFuNkc1aFNxNitnMm5r...
|
||
```
|
||
|
||
### 4. 测试通话
|
||
|
||
1. 进入诊单详情
|
||
2. 点击"视频通话"
|
||
3. 应该能成功初始化,不再报 -1201 错误
|
||
|
||
## 📊 对比总结
|
||
|
||
| 项目 | 错误实现 | 正确实现 |
|
||
|------|---------|---------|
|
||
| 算法来源 | 自定义 | 腾讯云官方 |
|
||
| gzip压缩 | ❌ 无 | ✅ 有 |
|
||
| Base64编码 | 标准 | URL安全 |
|
||
| UserSig长度 | 250-300字符 | 150-200字符 |
|
||
| 格式特征 | 以`==`结尾 | 包含`*`和`-` |
|
||
| 能否登录IM | ❌ 失败 | ✅ 成功 |
|
||
|
||
## 🔍 为什么之前没发现?
|
||
|
||
1. **测试不充分**:只测试了UserSig生成,没有测试IM登录
|
||
2. **错误提示不明确**:-1201错误只说"初始化未完成",没说UserSig无效
|
||
3. **格式看起来正常**:都是Base64编码,不仔细对比看不出问题
|
||
|
||
## ⚠️ 重要提醒
|
||
|
||
### 需要清除旧数据
|
||
|
||
数据库中已存在的患者TRTC账号使用的是错误格式的UserSig,需要:
|
||
|
||
1. **方案A:清空表**(推荐)
|
||
```sql
|
||
TRUNCATE TABLE zyt_tcm_patient_trtc;
|
||
```
|
||
|
||
2. **方案B:删除旧记录**
|
||
```sql
|
||
DELETE FROM zyt_tcm_patient_trtc WHERE create_time < UNIX_TIMESTAMP();
|
||
```
|
||
|
||
3. **方案C:让系统自动更新**
|
||
- 编辑诊单时会自动重新生成
|
||
- 但旧的UserSig仍然无效
|
||
|
||
### 不影响新数据
|
||
|
||
修复后新生成的UserSig都是正确格式,可以正常使用。
|
||
|
||
## 📚 相关文档
|
||
|
||
- [TLSSigAPIv2.php](./server/app/common/service/TLSSigAPIv2.php) - 官方算法实现
|
||
- [腾讯云UserSig生成文档](https://cloud.tencent.com/document/product/269/32688)
|
||
- [DIAGNOSE_INIT_FAIL.md](./DIAGNOSE_INIT_FAIL.md) - 初始化失败诊断
|
||
|
||
## 更新日志
|
||
|
||
### 2026-03-02
|
||
- ✅ 发现UserSig生成算法错误
|
||
- ✅ 修改为使用官方TLSSigAPIv2类
|
||
- ✅ 添加gzip压缩步骤
|
||
- ✅ 使用URL安全的Base64编码
|
||
- ✅ 清理重复代码
|
||
|
||
---
|
||
|
||
**关键要点**:必须使用官方的TLSSigAPIv2类,自定义实现会缺少gzip压缩导致UserSig无效!🔐
|