如何配置Laravel Passport实现多种认证方式返回JWT?
Laravel Passport 多认证逻辑优化方案
针对你在Laravel 10+Octane+Spatie多租户环境下,用Passport搭建认证服务时遇到的模型依赖注入问题,以及后续扩展多种登录方式的需求,以下是几种合理的解决方案:
一、自定义 AccessTokenController(优先推荐)
这种方案能完全接管Passport的令牌发放流程,自然实现依赖注入,同时方便后续扩展SSO、令牌登录等多种认证方式。
实现步骤:
- 复制Passport默认的
AccessTokenController代码到自定义控制器(例如App\Http\Controllers\Auth\CustomAccessTokenController),原控制器路径为Laravel\Passport\Http\Controllers\AccessTokenController。 - 在自定义控制器构造函数中注入所需的认证服务(如
UserAuthenticator),替代模型中app()->make的写法:use App\Services\UserAuthenticator; use Illuminate\Http\Request; use Laravel\Passport\Client; class CustomAccessTokenController extends Controller { protected $authenticator; public function __construct(UserAuthenticator $authenticator) { $this->authenticator = $authenticator; $this->middleware('throttle'); } public function issueToken(Request $request) { // 保留Passport原有参数验证逻辑 $request->validate([ 'grant_type' => 'required|string', 'client_id' => 'required|string', 'client_secret' => 'required|string', 'username' => 'required|string', 'password' => 'required|string', 'scope' => 'nullable|string', ]); // 通过注入的服务验证用户凭证 $user = $this->authenticator->validateCredentials($request->username, $request->password); if (!$user) { return response()->json(['error' => 'invalid_credentials'], 401); } // 获取租户对应的Password Grant Client $client = Client::where('tenant_id', tenant()->id) ->where('password_client', true) ->firstOrFail(); // 生成令牌并添加自定义Claims $token = $user->createToken('Password Grant Token', [], now()->addMinutes(60)); $token->accessToken->forceFill([ 'payload' => array_merge($token->accessToken->payload, [ 'tenant_id' => tenant()->id, // 按需添加其他自定义Claims ]) ])->save(); return response()->json([ 'access_token' => $token->accessToken, 'token_type' => 'Bearer', 'expires_in' => $token->token->expires_at->diffInSeconds(now()), 'refresh_token' => $token->refreshToken, ]); } } - 替换Passport默认路由,在
routes/web.php中使用自定义控制器:Route::post('/oauth/token', [\App\Http\Controllers\Auth\CustomAccessTokenController::class, 'issueToken']);
优缺点:
- 优点:完全掌控认证流程,依赖注入规范,便于后续扩展多种登录方式,适配微服务认证中心的长期迭代需求。
- 缺点:需要维护Passport核心逻辑的副本,Passport版本升级时需同步代码差异。
二、自定义 UserProvider(适合复杂凭证验证场景)
如果不同租户或登录方式的用户来源、验证逻辑差异较大,可以通过自定义UserProvider将认证逻辑从模型中剥离,符合单一职责原则。
实现步骤:
- 创建自定义UserProvider,继承EloquentUserProvider并注入认证服务:
namespace App\Auth; use App\Services\UserAuthenticator; use Illuminate\Auth\EloquentUserProvider; use Illuminate\Contracts\Auth\Authenticatable; class TenantUserProvider extends EloquentUserProvider { protected $authenticator; public function __construct($hasher, $model, UserAuthenticator $authenticator) { parent::__construct($hasher, $model); $this->authenticator = $authenticator; } // 重写根据凭证获取用户的方法 public function retrieveByCredentials(array $credentials) { $model = $this->createModel(); return $model->where('username', $credentials['username']) ->where('tenant_id', tenant()->id) ->first(); } // 重写验证凭证的方法 public function validateCredentials(Authenticatable $user, array $credentials) { return $this->authenticator->validate($user, $credentials['password']); } } - 在
AuthServiceProvider中注册自定义Provider:public function boot() { $this->registerPolicies(); Auth::provider('tenant-user', function ($app, array $config) { return new \App\Auth\TenantUserProvider( $app['hash'], $config['model'], $app->make(UserAuthenticator::class) ); }); } - 修改
config/auth.php配置,使用自定义Provider:'guards' => [ 'api' => [ 'driver' => 'passport', 'provider' => 'tenant-users', ], ], 'providers' => [ 'tenant-users' => [ 'driver' => 'tenant-user', 'model' => App\Models\User::class, ], ],
优缺点:
- 优点:符合Laravel认证体系设计,逻辑分离清晰,适合多用户类型、复杂凭证验证场景。
- 缺点:需要熟悉Laravel UserProvider机制,对新手有一定门槛。
三、不推荐的方案:静态方法/app()->make
直接在User模型中使用静态方法或app()->make获取服务,虽然能临时解决问题,但违背依赖注入原则,代码耦合度高,难以测试和维护。尤其在Octane环境下,静态方法可能引发状态残留问题,不建议长期使用。
内容的提问来源于stack exchange,提问作者WellBloud
相关产品推荐
相关产品推荐

