You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 09:56:18