| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- <?php
- namespace App\Module\OpenAPI\Services;
- use App\Module\OpenAPI\Models\OpenApiApp;
- use Illuminate\Http\Request;
- use Illuminate\Support\Facades\Cache;
- /**
- * 认证服务
- */
- class AuthService
- {
- /**
- * @var OpenApiService
- */
- protected OpenApiService $openApiService;
- public function __construct(OpenApiService $openApiService)
- {
- $this->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']);
- }
- }
|