Laravel实现手机号或邮箱加密码登录时手机号登录失败如何解决
解决方案
核心问题:你当前使用了Laravel Passport的密码授权模式,Passport默认仅通过email字段校验用户身份,所以手机号登录时,虽然你自己写的Auth::attempt校验通过了,但后续调用respondToAccessTokenRequest方法时,Passport内部会再次校验凭证,无法匹配手机号对应的用户,最终返回401。
步骤1:修复迁移文件约束错误(必改,避免脏数据)
你当前的CHECK约束存在笔误,仅校验了email不为空,无法实现「phone和email不同时为空」的设计规则,修改为:
// 替换原有DB::statement行 DB::statement('ALTER TABLE users ADD CONSTRAINT chk_phone_or_email CHECK (email IS NOT NULL OR phone IS NOT NULL);');
如果已经执行过迁移,可新增一个迁移修改约束,本地开发环境也可以回滚迁移后修改再重新执行。
步骤2:自定义Passport密码授权的用户查找逻辑
在app/Providers/AuthServiceProvider.php的boot方法中添加Passport自定义用户查询规则:
use Laravel\Passport\Passport; public function boot() { $this->registerPolicies(); // 新增以下内容 Passport::viaRequest('password', function ($request) { $username = $request->get('username'); return \App\Models\User::where('email', $username) ->orWhere('phone', $username) ->first(); }); }
步骤3:优化登录接口代码(可选)
添加上述自定义规则后,Passport会自动处理手机号/邮箱的身份校验,你可以直接删除自行实现的循环attempt逻辑,简化代码为:
public function login(ServerRequestInterface $request) { return $this->withErrorHandling(function () use ($request) { return $this->convertResponse( $this->server->respondToAccessTokenRequest($request, new Psr7Response) ); }); }
如果需要保留自定义的错误提示逻辑,也可以保留原有循环,但要注意统一参数获取方式,避免混用request()助手和注入的ServerRequestInterface实例导致参数不一致:
public function login(ServerRequestInterface $request) { $params = $request->getParsedBody(); $username = $params['username'] ?? ''; $password = $params['password'] ?? ''; $loginFields = ['phone', 'email']; foreach ($loginFields as $field) { $credentials = [ $field => $username, 'password' => $password ]; if (Auth::guard('web')->attempt($credentials)) { return $this->withErrorHandling(function () use ($request) { return $this->convertResponse( $this->server->respondToAccessTokenRequest($request, new Psr7Response) ); }); } } return response()->json([ 'message' => 'Invalid username or password' ], 401); }
内容的提问来源于stack exchange,提问作者Owen Kelvin
相关产品推荐
相关产品推荐

