使用Laravel Passport密码授权及Guzzle时服务器卡顿问题排查
问题根源:请求阻塞(不是Guzzle或Passport的锅)
你遇到的卡顿问题,本质上是请求死锁导致的,和Guzzle、Passport本身没有直接关系。
原因很简单:当你在Laravel的login方法里用Guzzle发起请求到http://localhost:8000/oauth/token时,如果你用的是Laravel内置的开发服务器(php artisan serve),它是单线程/单进程的——当前请求还在处理中,新的Guzzle请求会被服务器挂起,等着当前进程释放资源,而当前进程又在等着Guzzle的请求返回,形成了互相等待的死锁,所以会一直卡顿。
而Postman是外部独立请求,和当前处理login方法的进程无关,服务器可以正常处理,所以没有问题。
解决方案:避免内部HTTP调用,直接用Passport的底层逻辑
既然是同一个应用,完全没必要通过Guzzle发起HTTP请求来获取令牌,直接调用Passport的内部逻辑就能生成令牌,既高效又不会有阻塞问题。
这里提供两种可行的方案:
方案1:直接为验证通过的用户生成令牌
如果你的场景是用户登录后获取令牌,可以先验证用户凭证,再直接创建令牌:
use Illuminate\Support\Facades\Auth; use Carbon\Carbon; public function login(Request $request) { // 验证用户名和密码是否正确 if (!Auth::attempt($request->only('username', 'password'))) { return response()->json(['error' => '用户名或密码错误'], 401); } $user = Auth::user(); // 创建密码授权类型的访问令牌 $tokenResult = $user->createToken('Mobile App Access Token'); $token = $tokenResult->accessToken; // 返回令牌信息 return response()->json([ 'access_token' => $token, 'token_type' => 'Bearer', 'expires_at' => Carbon::parse( $tokenResult->token->expires_at )->toDateTimeString() ]); }
方案2:内部转发请求到Passport的令牌接口
如果你想严格遵循OAuth2的密码授权流程(比如要复用Passport的参数验证、令牌过期逻辑),可以内部转发请求到/oauth/token,而不是用Guzzle发起外部请求:
use Laravel\Passport\Client; use Illuminate\Http\Request as LaravelRequest; use League\OAuth2\Server\Exception\OAuthServerException; public function login(Request $request) { // 获取密码授权类型的客户端(你之前配置的password client) $passwordClient = Client::where('password_client', true)->first(); if (!$passwordClient) { return response()->json(['error' => '未找到密码授权客户端'], 500); } try { // 构建令牌请求所需的参数 $request->merge([ 'grant_type' => 'password', 'client_id' => $passwordClient->id, 'client_secret' => $passwordClient->secret, 'scope' => '*', ]); // 内部创建POST请求到/oauth/token $proxyRequest = LaravelRequest::create( '/oauth/token', 'POST', $request->all() ); // 让应用处理这个内部请求,返回令牌响应 $response = app()->handle($proxyRequest); return json_decode($response->getContent(), true); } catch (OAuthServerException $e) { return response()->json(['error' => $e->getMessage()], $e->getHttpStatusCode()); } }
额外提示:生产环境注意事项
如果你的生产环境用的是多进程服务器(比如Nginx+PHP-FPM),理论上不会出现单进程阻塞的问题,但依然不推荐用Guzzle调用自己的API——内部调用逻辑的性能要比HTTP请求高得多,还能避免网络层面的潜在问题。
内容的提问来源于stack exchange,提问作者Bruno Gutierrez
相关产品推荐
相关产品推荐

