openApiService = $openApiService; } /** * 验证API Key * * @param string $appId * @param string|null $appSecret * @return OpenApiApp|null */ public function validateApiKey(string $appId, ?string $appSecret): ?OpenApiApp { // 如果没有提供密钥,尝试通过应用ID查找 if (!$appSecret) { $app = $this->openApiService->getAppByAppId($appId); return $app && $app->can_call_api ? $app : null; } return $this->openApiService->validateApp($appId, $appSecret); } /** * 验证Bearer Token * * @param string $token * @return OpenApiApp|null */ public function validateBearerToken(string $token): ?OpenApiApp { // 这里可以实现JWT或OAuth2 token验证 // 暂时返回null,需要根据具体需求实现 return null; } /** * 验证签名 * * @param string $appId * @param string $signature * @param string $timestamp * @param Request $request * @return OpenApiApp|null */ public function validateSignature(string $appId, string $signature, string $timestamp, Request $request): ?OpenApiApp { $app = $this->openApiService->getAppByAppId($appId); if (!$app || !$app->can_call_api) { return null; } // 检查时间戳是否在容差范围内 $tolerance = config('openapi.security.signature_tolerance', 300); $currentTime = time(); $requestTime = (int) $timestamp; if (abs($currentTime - $requestTime) > $tolerance) { return null; } // 生成签名 $expectedSignature = $this->generateSignature($app, $request, $timestamp); // 验证签名 if (!hash_equals($expectedSignature, $signature)) { return null; } return $app; } /** * 生成签名 * * @param OpenApiApp $app * @param Request $request * @param string $timestamp * @return string */ protected function generateSignature(OpenApiApp $app, Request $request, string $timestamp): string { // 获取应用密钥 $appSecret = $app->app_secret; if (config('openapi.security.encrypt_secrets', true)) { $appSecret = decrypt($appSecret); } // 构建签名字符串 $method = $request->method(); $uri = $request->getRequestUri(); $body = $request->getContent(); $signString = implode("\n", [ $method, $uri, $timestamp, md5($body), ]); // 生成签名 $algorithm = config('openapi.auth.signature.algorithm', 'sha256'); return hash_hmac($algorithm, $signString, $appSecret); } /** * 生成JWT Token * * @param OpenApiApp $app * @param array $payload * @return string */ public function generateJwtToken(OpenApiApp $app, array $payload = []): string { if (!config('openapi.auth.jwt.enabled', true)) { throw new \RuntimeException('JWT认证未启用'); } $secret = config('openapi.auth.jwt.secret'); $algorithm = config('openapi.auth.jwt.algorithm', 'HS256'); $expire = config('openapi.auth.jwt.expire', 3600); $header = [ 'typ' => 'JWT', 'alg' => $algorithm, ]; $payload = array_merge([ 'iss' => config('app.url'), 'aud' => $app->app_id, 'iat' => time(), 'exp' => time() + $expire, 'app_id' => $app->app_id, 'scopes' => $app->scopes, ], $payload); $headerEncoded = $this->base64UrlEncode(json_encode($header)); $payloadEncoded = $this->base64UrlEncode(json_encode($payload)); $signature = hash_hmac('sha256', $headerEncoded . '.' . $payloadEncoded, $secret, true); $signatureEncoded = $this->base64UrlEncode($signature); return $headerEncoded . '.' . $payloadEncoded . '.' . $signatureEncoded; } /** * 验证JWT Token * * @param string $token * @return array|null */ public function validateJwtToken(string $token): ?array { if (!config('openapi.auth.jwt.enabled', true)) { return null; } $parts = explode('.', $token); if (count($parts) !== 3) { return null; } list($headerEncoded, $payloadEncoded, $signatureEncoded) = $parts; // 验证签名 $secret = config('openapi.auth.jwt.secret'); $expectedSignature = hash_hmac('sha256', $headerEncoded . '.' . $payloadEncoded, $secret, true); $expectedSignatureEncoded = $this->base64UrlEncode($expectedSignature); if (!hash_equals($expectedSignatureEncoded, $signatureEncoded)) { return null; } // 解析payload $payload = json_decode($this->base64UrlDecode($payloadEncoded), true); if (!$payload) { return null; } // 检查过期时间 if (isset($payload['exp']) && $payload['exp'] < time()) { return null; } return $payload; } /** * Base64 URL编码 * * @param string $data * @return string */ protected function base64UrlEncode(string $data): string { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } /** * Base64 URL解码 * * @param string $data * @return string */ protected function base64UrlDecode(string $data): string { return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); } /** * 生成OAuth2授权码 * * @param OpenApiApp $app * @param int $userId * @param array $scopes * @return string */ public function generateAuthCode(OpenApiApp $app, int $userId, array $scopes): string { if (!config('openapi.auth.oauth2.enabled', true)) { throw new \RuntimeException('OAuth2认证未启用'); } $code = bin2hex(random_bytes(16)); $expire = config('openapi.auth.oauth2.auth_code_expire', 600); $cacheKey = "openapi:auth_code:{$code}"; $data = [ 'app_id' => $app->app_id, 'user_id' => $userId, 'scopes' => $scopes, 'created_at' => time(), ]; Cache::put($cacheKey, $data, $expire); return $code; } /** * 验证OAuth2授权码 * * @param string $code * @param string $appId * @return array|null */ public function validateAuthCode(string $code, string $appId): ?array { $cacheKey = "openapi:auth_code:{$code}"; $data = Cache::get($cacheKey); if (!$data || $data['app_id'] !== $appId) { return null; } // 删除授权码(一次性使用) Cache::forget($cacheKey); return $data; } /** * 生成OAuth2访问令牌 * * @param OpenApiApp $app * @param int $userId * @param array $scopes * @return array */ public function generateAccessToken(OpenApiApp $app, int $userId, array $scopes): array { $accessToken = bin2hex(random_bytes(32)); $refreshToken = bin2hex(random_bytes(32)); $accessTokenExpire = config('openapi.auth.oauth2.access_token_expire', 3600); $refreshTokenExpire = config('openapi.auth.oauth2.refresh_token_expire', 2592000); // 存储访问令牌 $accessTokenKey = "openapi:access_token:{$accessToken}"; $accessTokenData = [ 'app_id' => $app->app_id, 'user_id' => $userId, 'scopes' => $scopes, 'created_at' => time(), ]; Cache::put($accessTokenKey, $accessTokenData, $accessTokenExpire); // 存储刷新令牌 $refreshTokenKey = "openapi:refresh_token:{$refreshToken}"; $refreshTokenData = [ 'app_id' => $app->app_id, 'user_id' => $userId, 'scopes' => $scopes, 'access_token' => $accessToken, 'created_at' => time(), ]; Cache::put($refreshTokenKey, $refreshTokenData, $refreshTokenExpire); return [ 'access_token' => $accessToken, 'refresh_token' => $refreshToken, 'token_type' => 'Bearer', 'expires_in' => $accessTokenExpire, 'scope' => implode(' ', $scopes), ]; } /** * 验证OAuth2访问令牌 * * @param string $accessToken * @return array|null */ public function validateAccessToken(string $accessToken): ?array { $cacheKey = "openapi:access_token:{$accessToken}"; return Cache::get($cacheKey); } /** * 刷新OAuth2访问令牌 * * @param string $refreshToken * @return array|null */ public function refreshAccessToken(string $refreshToken): ?array { $refreshTokenKey = "openapi:refresh_token:{$refreshToken}"; $refreshTokenData = Cache::get($refreshTokenKey); if (!$refreshTokenData) { return null; } // 获取应用信息 $app = $this->openApiService->getAppByAppId($refreshTokenData['app_id']); if (!$app) { return null; } // 删除旧的访问令牌 $oldAccessTokenKey = "openapi:access_token:{$refreshTokenData['access_token']}"; Cache::forget($oldAccessTokenKey); // 生成新的访问令牌 return $this->generateAccessToken($app, $refreshTokenData['user_id'], $refreshTokenData['scopes']); } }