Laravel Passport 需先配置 APP_KEY、确保 users 表存在且数据库连通、匹配 Laravel 与 Passport 版本,再执行 migrate 和 passport:install;AuthServiceProvider 中需调用 Passport::routes(),并配置 auth.php 的 api guard 为 passport。

直接装 laravel/passport 不等于能用 OAuth2——它默认依赖 Laravel 自带的用户模型和数据库结构,且必须在已配置好 APP_KEY 和数据库迁移的前提下初始化,否则运行 php artisan passport:install 会报 Class 'Laravel\Passport\Passport' not found 或 SQLSTATE[42S02]: Base table or view not found。
安装前必须确认的三件事
Passport 不是开箱即用的“插件”,它是深度耦合 Laravel 认证体系的扩展包:
-
APP_KEY已生成(php artisan key:generate),否则加密令牌失败 - 数据库已可连接,且
users表存在(Passport 会向该表加tokenable_type和tokenable_id字段) - Laravel 版本匹配:v10+ 需用 Passport v12+;v9 对应 v11;不匹配会导致
Call to undefined method Laravel\Passport\Passport::routes()
标准安装与基础路由注册
执行命令顺序不能错,且 php artisan migrate 必须在 passport:install 前完成:
composer require laravel/passport php artisan migrate php artisan passport:install
然后在 AuthServiceProvider@boot() 中注册路由(注意不是 routes/web.php):
use Laravel\Passport\Passport;
public function boot()
{
$this->registerPolicies();
Passport::routes(); // 这行必须有,否则 /oauth/token 等接口 404
}
最后在 config/auth.php 的 guards.api 下把 driver 改为 passport,否则中间件 auth:api 不识别 Bearer Token。
客户端创建与令牌获取常见卡点
开发时最常卡在「能注册客户端但拿不到 access_token」,原因多为:
- 请求
/oauth/token时用了application/x-www-form-urlencoded,但没设Content-Type: application/x-www-form-urlencoded头(Postman 默认不带) - 传参用 JSON body(如
{"grant_type":"password",...}),但 Passport 只认表单编码参数 -
password授权模式需先调用php artisan passport:client --password创建密码型客户端,普通--client不支持 - 用户未实现
HasApiTokenstrait,或没在User模型中调用use HasApiTokens;
刷新令牌与作用域控制的实际写法
Passport 默认返回 refresh_token,但不会自动续期——你得自己存、自己传、自己换。关键点:
- 刷新请求必须用原
refresh_token+client_id+client_secret,缺一不可 - 作用域(scopes)不是靠中间件自动过滤,而是要在控制器里显式校验:
$request->user()->tokenCan('edit-posts') - 定义 scope 要在
AuthServiceProvider@boot()中调用Passport::tokensCan([...]),否则scope:xxx参数会被忽略 -
前端若用 Axios,记得设
withCredentials: true才能携带 Cookie(仅限 cookie-based session 场景,Bearer Token 不需要)
复杂点在于:Passport 的 Personal Access Token 和 Client Credentials 模式共享同一套密钥,但权限模型完全不同;一旦混用,容易出现 token 有权限却无法访问资源的情况——这时候别急着重装,先查 oauth_access_tokens 表里的 scopes 字段是否为空字符串或 JSON 格式错误。










