Laravel8中GuzzleHttp调用第三方API的Token失效错误处理咨询
处理Laravel 8中Guzzle调用第三方API的401 Token失效问题
1. 捕获并识别Token失效异常
首先要在Guzzle请求代码中捕获GuzzleHttp\Exception\ClientException,判断响应状态码是否为401,同时解析响应体确认是Token过期/撤销的场景:
use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; try { $client = new Client(); $response = $client->post('https://bankul/acctenq/v2/prefetchAccount', [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->getValidToken(), // 其他业务请求头 ], 'json' => [/* 接口请求参数 */] ]); // 处理正常响应数据 } catch (ClientException $e) { $statusCode = $e->getResponse()->getStatusCode(); $responseBody = json_decode($e->getResponse()->getBody()->getContents(), true); // 精准匹配Token失效的401场景 if ($statusCode === 401 && isset($responseBody['error']) && in_array($responseBody['error'], ['Token has been revoked.', 'Token expired'])) { // 刷新Token并重试请求 $newToken = $this->refreshBankToken(); $response = $client->post('https://bankul/acctenq/v2/prefetchAccount', [ 'headers' => [ 'Authorization' => 'Bearer ' . $newToken, // 其他业务请求头 ], 'json' => [/* 接口请求参数 */] ]); // 处理重试后的响应 } else { // 其他客户端异常直接抛出或自定义处理 throw $e; } } catch (\Exception $e) { // 处理非客户端类异常 throw $e; }
2. 封装Token刷新与缓存逻辑
把Token的获取、刷新逻辑封装成独立服务,并用Laravel缓存存储有效Token,避免重复请求银行Token接口:
// app/Services/BankApiService.php namespace App\Services; use Illuminate\Support\Facades\Cache; use GuzzleHttp\Client; class BankApiService { protected $client; protected $tokenCacheKey = 'bank_api_valid_token'; public function __construct() { $this->client = new Client(); } // 获取当前有效Token public function getValidToken() { if (Cache::has($this->tokenCacheKey)) { return Cache::get($this->tokenCacheKey); } return $this->fetchNewToken(); } // 强制刷新Token public function refreshBankToken() { return $this->fetchNewToken(); } // 向银行请求新Token protected function fetchNewToken() { $response = $this->client->post('https://bankul/auth/token', [ 'form_params' => [ 'grant_type' => 'client_credentials', // 按银行API要求调整 'client_id' => config('services.bank.client_id'), 'client_secret' => config('services.bank.client_secret'), // 其他必填参数 ] ]); $tokenData = json_decode($response->getBody()->getContents(), true); $token = $tokenData['access_token']; $expiresIn = $tokenData['expires_in'] ?? 3600; // 缓存Token时提前1分钟过期,避免边缘时间的无效请求 Cache::put($this->tokenCacheKey, $token, now()->addSeconds($expiresIn - 60)); return $token; } }
3. 处理并发场景下的Token刷新
如果多个请求同时触发Token过期,可能会重复调用银行Token接口,用Laravel缓存锁可以避免这个问题:
修改fetchNewToken方法:
protected function fetchNewToken() { // 创建缓存锁,有效期10秒,防止并发重复请求 $lock = Cache::lock('bank_token_refresh_lock', 10); if ($lock->get()) { try { // 再次检查缓存,避免其他请求已获取到Token if (Cache::has($this->tokenCacheKey)) { return Cache::get($this->tokenCacheKey); } // 请求新Token逻辑 $response = $this->client->post('https://bankul/auth/token', [ 'form_params' => [ 'grant_type' => 'client_credentials', 'client_id' => config('services.bank.client_id'), 'client_secret' => config('services.bank.client_secret'), ] ]); $tokenData = json_decode($response->getBody()->getContents(), true); $token = $tokenData['access_token']; $expiresIn = $tokenData['expires_in'] ?? 3600; Cache::put($this->tokenCacheKey, $token, now()->addSeconds($expiresIn - 60)); return $token; } finally { $lock->release(); } } else { // 等待锁释放后重试获取Token sleep(1); return $this->getValidToken(); } }
内容的提问来源于stack exchange,提问作者Ashok
相关产品推荐
相关产品推荐

