Laravel Passport密码授权如何获取已有Bearer Token而非新建
Laravel Passport 复用有效密码授权令牌实现方案
Laravel Passport 原生密码授权逻辑每次请求都会生成全新令牌,且默认仅在oauth_access_tokens表存储令牌的哈希值(和密码存储逻辑一致,不可逆解密),因此要实现「返回已有有效令牌、不重复生成」的需求,不要直接修改原生oauth/token接口逻辑(避免后续升级Passport版本出现兼容问题),按以下步骤实现即可:
- 第一步:新增独立路由
建议使用POST方式传递账号密码(避免GET参数被浏览器历史、服务器日志留存导致密码泄露),如果要匹配给出的GET示例也可以直接按示例配置,在routes/api.php中添加路由:
use App\Http\Controllers\Auth\PassportTokenController; // 生产环境推荐替换为Route::post Route::get('oauth/token', [PassportTokenController::class, 'fetchValidToken']);
- 第二步:调整数据库结构存储明文令牌
因为原生Passport不存明文令牌,需要新增字段存储首次生成的明文内容,创建新的迁移文件:
// 迁移up方法内容 Schema::table('oauth_access_tokens', function (Blueprint $table) { $table->text('plain_access_token')->nullable()->comment('明文访问令牌,用于令牌复用'); });
执行php artisan migrate完成字段添加。
- 第三步:编写控制器逻辑
新建app/Http/Controllers/Auth/PassportTokenController.php,核心逻辑为「校验账号密码 -> 查询对应用户名下未撤销、未过期的有效令牌 -> 存在则直接返回,不存在则生成新令牌并存好明文后返回」:
<?php namespace App\Http\Controllers\Auth; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Laravel\Passport\Token; use App\Http\Controllers\Controller; class PassportTokenController extends Controller { public function fetchValidToken(Request $request) { // 参数校验,按实际的账号字段调整,示例中用name作为账号字段 $validated = $request->validate([ 'name' => 'required|string', 'password' => 'required|string', ]); // 查询用户并校验密码 $user = User::where('name', $validated['name'])->first(); if (!$user || !Hash::check($validated['password'], $user->password)) { return response()->json(['message' => '账号或密码错误'], 401); } // 查询用户名下有效令牌,可按需增加client_id、scope条件限定令牌范围 $existingToken = Token::where('user_id', $user->id) ->where('revoked', 0) ->where('expires_at', '>', now()) ->latest('expires_at') ->first(); // 存在有效令牌直接返回 if ($existingToken && $existingToken->plain_access_token) { return response()->json([ 'token' => $existingToken->plain_access_token ]); } // 无有效令牌则生成新令牌 $newToken = $user->createToken('password_grant'); $tokenModel = $newToken->token; // 令牌过期时间和原有Passport配置保持一致即可 $tokenModel->expires_at = now()->addDays(15); // 存储明文令牌到新增字段 $tokenModel->plain_access_token = $newToken->accessToken; $tokenModel->save(); return response()->json([ 'token' => $newToken->accessToken ]); } }
注意事项
- 强烈不建议生产环境使用GET方式传递账号、密码参数,泄露风险远高于POST请求。
plain_access_token属于高度敏感字段,不要对非授权人员开放查询权限,有安全需求的话可以在存储时做应用层加密,读取时再解密。- 如果系统给不同端(APP/网页/小程序)分配了不同的Passport客户端,查询有效令牌时请加上
client_id筛选条件,避免跨端返回错误令牌。 - 令牌被用户主动撤销、过期后,下次请求会自动生成新令牌,不影响原有Passport的令牌生命周期逻辑。
内容的提问来源于stack exchange,提问作者Roman Dukhanin
相关产品推荐
相关产品推荐

