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

Laravel Passport无状态API认证:弃用Password Grant后的授权码模式适配问题

无状态API下Laravel Passport替代Password Grant的解决方案

针对你的核心问题,这里提供三种可行方案,适配无状态API的需求:


方案1:继续使用Password Grant(虽被弃用但仍可用)

官方标记Password Grant为弃用只是出于安全最佳实践的建议(避免直接传输用户密码),但该功能并未从Passport中移除。如果你的API服务仅对接信任的客户端(比如自家的移动端应用、内部服务),这种方式是最直接高效的,完全适配无状态场景:

  1. 创建密码授权客户端:
php artisan passport:client --password

执行后会生成client_id和client_secret,把它们存入环境变量或配置文件。

  1. 编写登录API端点:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

Route::post('/api/login', function (Request $request) {
    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
    ]);

    $response = Http::asForm()->post(config('app.url').'/oauth/token', [
        'grant_type' => 'password',
        'client_id' => config('passport.password_client.id'),
        'client_secret' => config('passport.password_client.secret'),
        'username' => $request->email,
        'password' => $request->password,
        'scope' => '',
    ]);

    return response()->json($response->json(), $response->status());
});

这个端点直接调用Passport的token接口,返回包含access_token和refresh_token的响应,无需重定向,完全基于api守卫工作。

注意:务必确保API全程使用HTTPS,避免密码明文传输风险。


方案2:使用Authorization Code Grant with PKCE

如果你的客户端是非信任的外部应用(比如第三方SPA、移动APP),推荐使用带PKCE的授权码模式,它可以替代客户端密钥,同时规避重定向对无状态服务的限制:

  1. 创建公开授权客户端:
php artisan passport:client --public

此客户端无需client_secret,适合前端类客户端。

  1. 客户端侧流程调整:
  • 客户端生成随机的code_verifier和对应的code_challenge
  • 客户端直接向Passport的授权端点请求授权码(无需服务端重定向,由客户端自行处理授权页面的跳转与回调)
  • 拿到授权码后,客户端直接调用/oauth/token交换access_token和refresh_token
  1. 配置API守卫:
    在config/auth.php中确保api守卫使用Passport驱动:
'guards' => [
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

这种模式完全符合OAuth 2.0官方推荐,同时适配无状态API的架构。


方案3:自定义Grant类型(完全适配无状态需求)

如果不想依赖弃用的Password Grant,也不想处理授权码的跳转逻辑,可以自定义一个支持刷新令牌的Grant类型,完全基于api守卫工作:

  1. 创建自定义Grant类:
<?php

namespace App\Passport;

use League\OAuth2\Server\Grant\AbstractGrant;
use League\OAuth2\Server\RequestEvent;
use Psr\Http\Message\ServerRequestInterface;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use League\OAuth2\Server\Exception\OAuthServerException;

class PasswordRefreshGrant extends AbstractGrant
{
    public function getName()
    {
        return 'password_refresh';
    }

    public function respondToAccessTokenRequest(
        ServerRequestInterface $request,
        ResponseTypeInterface $responseType,
        \DateInterval $accessTokenTTL
    ) {
        // 验证客户端合法性
        $client = $this->validateClient($request);

        // 验证用户账号密码
        $user = $this->validateUser($request);

        // 生成访问令牌
        $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getAuthIdentifier(), []);

        // 生成刷新令牌
        $refreshToken = $this->issueRefreshToken($accessToken);

        $responseType->setAccessToken($accessToken);
        $responseType->setRefreshToken($refreshToken);

        return $responseType;
    }

    protected function validateUser(ServerRequestInterface $request)
    {
        $email = $this->getRequestParameter('email', $request);
        $password = $this->getRequestParameter('password', $request);

        if (is_null($email) || is_null($password)) {
            throw OAuthServerException::invalidRequest('email or password');
        }

        $user = $this->getUserEntityByUserCredentials(
            $email,
            $password,
            $this->getName(),
            $this->getClientEntityOrFail($request)
        );

        if ($user === null) {
            throw OAuthServerException::invalidCredentials();
        }

        return $user;
    }
}
  1. 注册自定义Grant:
    在AuthServiceProvider的boot方法中添加:
use App\Passport\PasswordRefreshGrant;
use Laravel\Passport\Passport;

public function boot()
{
    $this->registerPolicies();

    Passport::routes();

    // 启用自定义Grant类型
    app(\League\OAuth2\Server\AuthorizationServer::class)->enableGrantType(
        new PasswordRefreshGrant(),
        Passport::tokensExpireIn()
    );
}
  1. 使用自定义Grant获取令牌:
    客户端直接POST请求/oauth/token,参数如下:
grant_type: password_refresh
client_id: 你的客户端ID
email: 用户邮箱
password: 用户密码
scope: (可选)权限范围

此请求会直接返回access_token和refresh_token,全程无重定向,完全适配无状态API。


内容的提问来源于stack exchange,提问作者Eugene Zdravstvuy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 19:35:20