Laravel 8升级后API调用失败:类缺失与认证异常问题
Laravel 8升级后API认证问题排查与修复
问题背景
将旧Laravel应用升级到Laravel 8后,API连接出现两个核心问题:
- 调用API时抛出错误:
Class 'Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory' not found - 改用PSR-7适配后,所有API调用均返回
Unauthenticated——即使通过curl调用/oauth/token能正常获取访问令牌,用该令牌调用其他接口仍无法通过认证。
涉及的自定义中间件MyCheckClientCredentials在Laravel 6/7中正常工作,修改后不再崩溃但始终返回未认证,需明确Laravel 8处理访问令牌的默认方案,或修复现有中间件。
原中间件代码
<?php namespace App\Http\Middleware; use Closure; use League\OAuth2\Server\ResourceServer; use Illuminate\Auth\AuthenticationException; use Laravel\Passport\Exceptions\MissingScopeException; use League\OAuth2\Server\Exception\OAuthServerException; use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory; class MyCheckClientCredentials { /** * The Resource Server instance. * * @var \League\OAuth2\Server\ResourceServer */ private $server; /** * Create a new middleware instance. * * @param \League\OAuth2\Server\ResourceServer $server * @return void */ public function __construct(ResourceServer $server) { $this->server = $server; } /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param mixed ...$scopes * @return mixed * @throws \Illuminate\Auth\AuthenticationException */ public function handle($request, Closure $next, ...$scopes) { $psr = (new DiactorosFactory)->createRequest($request); try { $psr = $this->server->validateAuthenticatedRequest($psr); $request['oauth_client_id'] = $psr->getAttribute('oauth_client_id'); } catch (OAuthServerException $e) { throw new AuthenticationException; } $this->validateScopes($psr, $scopes); return $next($request); } /** * Validate the scopes on the incoming request. * * @param \Psr\Http\Message\ResponseInterface $psr * @param array $scopes * @return void * @throws \Laravel\Passport\Exceptions\MissingScopeException */ protected function validateScopes($psr, $scopes) { if (in_array('*', $tokenScopes = $psr->getAttribute('oauth_scopes'))) { return; } foreach ($scopes as $scope) { if (! in_array($scope, $tokenScopes)) { throw new MissingScopeException($scope); } } } }
修改后的中间件代码
<?php namespace App\Http\Middleware; use Closure; use League\OAuth2\Server\ResourceServer; use Illuminate\Auth\AuthenticationException; use Laravel\Passport\Exceptions\MissingScopeException; use League\OAuth2\Server\Exception\OAuthServerException; //use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory; use Nyholm\Psr7\Factory\Psr17Factory; use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; use Symfony\Component\HttpFoundation\Request; class MyCheckClientCredentials { /** * The Resource Server instance. * * @var \League\OAuth2\Server\ResourceServer */ private $server; /** * Create a new middleware instance. * * @param \League\OAuth2\Server\ResourceServer $server * @return void */ public function __construct(ResourceServer $server) { $this->server = $server; } /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param mixed ...$scopes * @return mixed * @throws \Illuminate\Auth\AuthenticationException */ public function handle($request, Closure $next, ...$scopes) { //$psr = (new DiactorosFactory)->createRequest($request); $symfonyRequest = new Request([], [], [], [], [], ['HTTP_HOST' => 'toi.development.lib.utah.edu'], 'Content'); // The HTTP_HOST server key must be set to avoid an unexpected error $psr17Factory = new Psr17Factory(); $psrHttpFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory); $psr = $psrHttpFactory->createRequest($symfonyRequest); try { $psr = $this->server->validateAuthenticatedRequest($psr); $request['oauth_client_id'] = $psr->getAttribute('oauth_client_id'); } catch (OAuthServerException $e) { throw new AuthenticationException; } $this->validateScopes($psr, $scopes); return $next($request); } /** * Validate the scopes on the incoming request. * * @param \Psr\Http\Message\ResponseInterface $psr * @param array $scopes * @return void * @throws \Laravel\Passport\Exceptions\MissingScopeException */ protected function validateScopes($psr, $scopes) { if (in_array('*', $tokenScopes = $psr->getAttribute('oauth_scopes'))) { return; } foreach ($scopes as $scope) { if (! in_array($scope, $tokenScopes)) { throw new MissingScopeException($scope); } } } }
Composer配置文件
{ "name": "laravel/laravel", "description": "The Laravel Framework.", "keywords": ["framework", "laravel"], "license": "MIT", "type": "project", "require": { "php": ">=7.1", "doctrine/dbal": "^2.5", "fideloper/proxy": "~4.0", "laravel/framework": "^8.0", "laravel/helpers": "^1.4", "laravel/passport": "^10.0", "laravel/tinker": "~2.0", "lcobucci/jwt": "^4.0", "subfission/cas": "dev-master", "symfony/psr-http-message-bridge": "^2.0", "nyholm/psr7": "^1.5", "zendframework/zend-diactoros": "2.2.1", "guzzlehttp/guzzle": "^7.0.1", "laravel/ui": "^3.0" }, "require-dev": { "facade/ignition": "^2.3.6", "filp/whoops": "~2.0", "fzaninotto/faker": "~1.4", "mockery/mockery": "0.9.*", "nunomaduro/collision": "^5.0", "phpunit/phpunit": "^9.0" }, "autoload": { "psr-4": { "App\": "app/", "Database\\Factories": "database/factories/", "Database\\Seeders": "database/seeders/" }, "files": [ "app/Functions/helper_functions.php" ] }, "autoload-dev": { "psr-4": { "Tests": "tests/" } }, "extra": { "laravel": { "dont-discover": [ ] } }, "scripts": { "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "post-create-project-cmd": [ "@php artisan key:generate" ], "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan package:discover" ] }, "config": { "preferred-install": "dist", "sort-packages": true, "optimize-autoloader": true } }
解决方案
1. Laravel 8默认的API认证中间件
Laravel 8配合Passport提供了开箱即用的API认证中间件,无需自定义即可处理访问令牌验证:
auth:api:验证用户级别的访问令牌(对应Passport个人访问令牌、密码授权令牌)client:验证客户端凭证授权的令牌(匹配你自定义中间件的场景)
直接在路由中使用这些默认中间件替代自定义的MyCheckClientCredentials:
// 客户端凭证认证路由组 Route::middleware('client')->group(function () { // 你的API路由 }); // 用户级认证路由组 Route::middleware('auth:api')->group(function () { // 你的API路由 });
2. 修复自定义中间件的问题
修改后的中间件核心错误:手动创建了空的Symfony\Component\HttpFoundation\Request实例,完全丢失原请求中的认证令牌信息,导致验证失败。正确做法是将传入的Laravel Request转换为PSR-7请求,保留所有请求数据。
修复后的中间件代码:
<?php namespace App\Http\Middleware; use Closure; use League\OAuth2\Server\ResourceServer; use Illuminate\Auth\AuthenticationException; use Laravel\Passport\Exceptions\MissingScopeException; use League\OAuth2\Server\Exception\OAuthServerException; use Nyholm\Psr7\Factory\Psr17Factory; use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; class MyCheckClientCredentials { private $server; public function __construct(ResourceServer $server) { $this->server = $server; } public function handle($request, Closure $next, ...$scopes) { // 将Laravel请求转换为PSR-7请求,保留所有请求信息 $psr17Factory = new Psr17Factory(); $psrHttpFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory); $psrRequest = $psrHttpFactory->createRequest($request); try { $psrRequest = $this->server->validateAuthenticatedRequest($psrRequest); $request->merge([ 'oauth_client_id' => $psrRequest->getAttribute('oauth_client_id'), 'oauth_scopes' => $psrRequest->getAttribute('oauth_scopes'), ]); } catch (OAuthServerException $e) { throw new AuthenticationException; } $this->validateScopes($psrRequest, $scopes); return $next($request); } protected function validateScopes($psrRequest, $scopes) { $tokenScopes = $psrRequest->getAttribute('oauth_scopes'); if (in_array('*', $tokenScopes)) { return; } foreach ($scopes as $scope) { if (!in_array($scope, $tokenScopes)) { throw new MissingScopeException($scope); } } } }
3. 额外检查事项
- 重新生成Passport加密密钥:运行
php artisan passport:install - 确认
.env文件中PASSPORT_CLIENT_ID和PASSPORT_CLIENT_SECRET配置正确 - 确保请求头正确携带
Authorization: Bearer {token} - 清除应用缓存:运行
php artisan config:clear和php artisan cache:clear
内容的提问来源于stack exchange,提问作者Califer
相关产品推荐
相关产品推荐

