diff --git a/app/common/controller/AuthController.php b/app/common/controller/AuthController.php index 6923468..bd14627 100644 --- a/app/common/controller/AuthController.php +++ b/app/common/controller/AuthController.php @@ -113,4 +113,16 @@ class AuthController extends BaseController // (new AuthService())->blockToken($token, $systemCode); // return $this->success([], '已强制停用该token'); // } + + + public function getJsApi(Request $request) + { + try { + $url = $request->get('url'); + $result = (new AuthService())->getJsApiConfig($request->corp_abbr, $request->system_code,$url); + return $this->success($result); + } catch (\Throwable $e) { + return $this->error($e->getMessage()); + } + } } \ No newline at end of file diff --git a/app/common/service/AuthService.php b/app/common/service/AuthService.php index a675025..09b884d 100644 --- a/app/common/service/AuthService.php +++ b/app/common/service/AuthService.php @@ -86,4 +86,20 @@ class AuthService { (new AuthLogic())->blockToken($token, $systemCode); } + + /** + * 获取jsapi配置 + * @param string $corpAbbr + * @param string $systemCcode + * @param string $url 当前页面完整 URL(不包含 # 后面的内容) + * @return array + */ + public function getJsApiConfig(string $corpAbbr, string $systemCcode, string $url) + { + $logic = new AuthLogic(); + $certificate = $logic->feishuAppInfo($corpAbbr, $systemCcode); + $feishu = new Feishu($certificate['app_id'], $certificate['app_secret'], $certificate['abbr']); + $jsApi = $feishu->getJsApiConfig($url); + return $jsApi; + } } \ No newline at end of file diff --git a/app/common/utils/Feishu.php b/app/common/utils/Feishu.php index e10672c..c5d188e 100644 --- a/app/common/utils/Feishu.php +++ b/app/common/utils/Feishu.php @@ -225,4 +225,125 @@ class Feishu // ]; // return $this->requestResult($url); // } + + /** + * 获取 JSAPI 配置(签名) + * @param string $url 当前页面完整 URL(不包含 # 后面的内容) + * @return array + */ + public function getJsApiConfig($url) + { + // 1. 获取 tenant_access_token + //$accessToken = $this->getTenantAccessToken(); + $accessToken = $this->tenantAccessToken(); + + // 2. 获取 jsapi_ticket + $ticket = $this->getJsapiTicket($accessToken); + + // 3. 生成签名参数 + $timestamp = time(); + $nonceStr = $this->generateNonceStr(); + + // 4. 生成签名 + $signature = $this->generateSignature($ticket, $nonceStr, $timestamp, $url); + + return [ + 'appId' => $this->appId, + 'timestamp' => $timestamp, + 'nonceStr' => $nonceStr, + 'signature' => $signature + ]; + } + + /** + * 获取 jsapi_ticket + * @param string $accessToken + * @return string + */ + private function getJsapiTicket($accessToken) + { + // 检查缓存 + $cachedTicket = Cache::get($this->abbr.'-feishu:jsapi_ticket'); +// $cachedTicket = $this->getCachedData('feishu:jsapi_ticket'); + if ($cachedTicket) { + return $cachedTicket; + } + + $url = $this->baseUri . '/open-apis/jssdk/ticket/get'; + + $headers = [ + 'Authorization: Bearer ' . $accessToken, + 'Content-Type: application/json' + ]; + + $result = $this->httpGet($url, $headers); + + if ($result['code'] !== 0) { + throw new BusinessException('获取 jsapi_ticket 失败: ' . json_encode($result)); + } + + $ticket = $result['ticket']; + $expire = $result['expire_in'] - 300; // 提前5分钟过期 + + // 缓存 ticket + Cache::set($this->abbr.'-feishu:jsapi_ticket', $ticket, $expire); +// $this->setCachedData('feishu:jsapi_ticket', $ticket, $expire); + + return $ticket; + } + + /** + * 生成签名 + * @param string $ticket + * @param string $nonceStr + * @param int $timestamp + * @param string $url + * @return string + */ + private function generateSignature($ticket, $nonceStr, $timestamp, $url) + { + // 拼接待签名字符串(注意顺序) + $signStr = "jsapi_ticket={$ticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}"; + + return sha1($signStr); + } + + /** + * 生成随机字符串 + * @param int $length + * @return string + */ + private function generateNonceStr($length = 16) + { + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + $str = ''; + for ($i = 0; $i < $length; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + return $str; + } + + /** + * HTTP GET 请求 + */ + private function httpGet($url, $headers = []) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不验证证书 + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 不验证主机名 + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode !== 200) { + throw new BusinessException("HTTP GET 请求失败: {$httpCode}"); + } + + return json_decode($response, true); + } } \ No newline at end of file diff --git a/config/route.php b/config/route.php index 7843ffe..320fe49 100644 --- a/config/route.php +++ b/config/route.php @@ -51,7 +51,7 @@ Route::group('/common', function () { Route::any('/register', [app\common\controller\AuthController::class, 'register']); Route::any('/feishu/scope', [app\common\controller\AuthController::class, 'feishuScope']); Route::any('/refresh/token', [app\common\controller\AuthController::class, 'refresh']); - + Route::any('/feishu/jsapi', [app\common\controller\AuthController::class, 'getJsApi']); // // 调试 Route::any('/feishu/auth', [app\common\controller\AuthController::class, 'feishuAuth']);