This commit is contained in:
2026-03-06 13:51:25 +08:00
parent 43022b8ff6
commit 3ad6b05bf8
5 changed files with 218 additions and 2 deletions
+135
View File
@@ -609,4 +609,139 @@ HTML;
return $this->error($e->getMessage());
}
}
/**
* 获取飞书 JS API 鉴权签名
* 用于前端初始化飞书 JS SDK
*
* company 参数可选,如果未提供则从登录 token 中获取用户所属公司
*/
public function feishuJsapiSignature(Request $request): Response
{
$url = $request->get('url');
$companyParam = $request->get('company');
if (!$url) {
return $this->error('缺少 url 参数');
}
try {
$companyId = null;
// 优先使用传入的 company 参数
if ($companyParam) {
$company = \app\model\Company::find($companyParam);
if (!$company) {
return $this->error('公司不存在');
}
$companyId = $company->id;
error_log("✅ 使用参数中的 company: {$companyId}");
}
// 从 token 中获取用户所属公司
else {
$companyId = $this->getCompanyId($request);
if (!$companyId) {
return $this->error('未登录且未指定公司', 401);
}
error_log("✅ 从 token 获取 company: {$companyId}");
}
// 获取该公司的默认飞书应用
$feishuApp = \app\model\CompanyFeishuApp::where('company_id', $companyId)
->where('system_id', 1)
->where('status', 1)
->first();
if (!$feishuApp) {
return $this->error('该公司未配置飞书应用');
}
// 生成 JS API 签名
$signatureData = $this->generateJsapiSignature($feishuApp->id, $url);
return $this->success([
'app_id' => $feishuApp->app_id,
'company_id' => $companyId,
'jsapi_ticket' => $signatureData['jsapi_ticket'],
'noncestr' => $signatureData['noncestr'],
'timestamp' => $signatureData['timestamp'],
'url' => $signatureData['url'],
'signature' => $signatureData['signature'],
]);
} catch (\Exception $e) {
error_log("❌ feishuJsapiSignature 失败: " . $e->getMessage());
return $this->error($e->getMessage());
}
}
/**
* 生成 JS API 鉴权签名
*
* @param int $feishuAppId 飞书应用配置ID
* @param string $url 当前页面URL
* @return array 签名数据
*/
private function generateJsapiSignature(int $feishuAppId, string $url): array
{
// 获取 jsapi_ticket
$jsapiTicket = \app\util\FeishuUtil::getJsapiTicket($feishuAppId);
// 清理 URL:移除 # 和 ? 后面的参数
$url = $this->cleanUrl($url);
// 生成随机字符串
$noncestr = $this->generateNonceStr();
// 获取毫秒级时间戳
$timestamp = (int)round(microtime(true) * 1000);
// 拼接签名字符串
$verifyStr = "jsapi_ticket={$jsapiTicket}&noncestr={$noncestr}&timestamp={$timestamp}&url={$url}";
// 生成 SHA1 签名
$signature = sha1($verifyStr);
error_log("✅ JS API 签名生成成功: url={$url}");
return [
'jsapi_ticket' => $jsapiTicket,
'noncestr' => $noncestr,
'timestamp' => $timestamp,
'url' => $url,
'signature' => $signature,
];
}
/**
* 清理 URL,移除 # 和 ? 后面的参数
*/
private function cleanUrl(string $url): string
{
// 移除 # 后面的内容
$pos = strpos($url, '#');
if ($pos !== false) {
$url = substr($url, 0, $pos);
}
// 移除 ? 后面的内容
$pos = strpos($url, '?');
if ($pos !== false) {
$url = substr($url, 0, $pos);
}
return $url;
}
/**
* 生成随机字符串(由数字、字母组成)
*/
private function generateNonceStr(int $length = 16): string
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$noncestr = '';
for ($i = 0; $i < $length; $i++) {
$noncestr .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $noncestr;
}
}