Files
zyt/FIX_USERSIG_ALGORITHM.md
T
2026-03-04 15:32:30 +08:00

7.6 KiB
Raw Blame History

修复UserSig生成算法错误

🔍 问题发现

通过对比腾讯云官方生成的UserSig和我们系统生成的UserSig,发现格式完全不同:

腾讯云官方生成(正确)

eJwtjEEPwTAYQP9Lz7J8rXWdJS4OhMwFw1yW0q4*zWimGSH*u5kd33vJe5NNug4aXZOEsADIoGNU*uqxxE476bHFYtjHu7LSOVQkoREAZUJQ*Bf9dFjr1nPOGUBvPVY-JwQLGfBw1F-QtO9Jtrsd1OlcHe28iS*wTOXCZrGSj9K*ZF6uoi2fTWFvTD4mny8uFDNl
  • 格式:gzip压缩 + Base64 URL安全编码
  • 特点:包含 *- 字符
  • 长度:约150-200字符

我们系统生成(错误)

eyJUTFMuc2lnIjoiVmRNRDdcL2IyMTF1MHpDSjFuNkc1aFNxNitnMm5rdm11Mkh1WnRyZVwvYTI0PSIsIlRMUy52ZXIiOiIyLjAiLCJUTFMuaWRlbnRpZmllciI6InBhdGllbnRfNCIsIlRMUy5zZGthcHBpZCI6MTYwMDEyNzcxMCwiVExTLmV4cGlyZSI6MTU1NTIwMDAsIlRMUy50aW1lIjoxNzcyNDE4MTcyfQ==
  • 格式:仅Base64编码,缺少gzip压缩
  • 特点:以 == 结尾,标准Base64
  • 长度:约250-300字符

解码后是纯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 类。

错误的实现

// ❌ 错误:缺少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 类,它已经实现了正确的算法。

修改前

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;
    }
}

修改后

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 的正确实现

官方类的关键代码:

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. 清除旧数据

-- 清除旧的患者TRTC账号(UserSig格式错误)
TRUNCATE TABLE zyt_tcm_patient_trtc;

2. 重新生成UserSig

# 方式A:通过新增诊单
# 进入诊单管理 > 新增诊单 > 保存

# 方式B:运行测试脚本
cd server
php test_usersig.php

3. 验证格式

查看数据库中的UserSig

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:清空表(推荐)

    TRUNCATE TABLE zyt_tcm_patient_trtc;
    
  2. 方案B:删除旧记录

    DELETE FROM zyt_tcm_patient_trtc WHERE create_time < UNIX_TIMESTAMP();
    
  3. 方案C:让系统自动更新

    • 编辑诊单时会自动重新生成
    • 但旧的UserSig仍然无效

不影响新数据

修复后新生成的UserSig都是正确格式,可以正常使用。

📚 相关文档

更新日志

2026-03-02

  • 发现UserSig生成算法错误
  • 修改为使用官方TLSSigAPIv2类
  • 添加gzip压缩步骤
  • 使用URL安全的Base64编码
  • 清理重复代码

关键要点:必须使用官方的TLSSigAPIv2类,自定义实现会缺少gzip压缩导致UserSig无效!🔐