[jwt 做多用户表验证] jwt.auth 中间件一直报错找不到用户

环境

机器: CentOS release 6.10 (Final)
语言: PHP 7.2.24
框架: laravel 5.4
插件: tymon/jwt-auth 1.0

情景

一个 web 项目使用 tymon/jwt-auth 1.0 做 API 用户验证,用户表为自定义的 customer 表(user 表被用作后台用户表来用),用户登录可以正常使用,但是在登录后调用需检验登录状态的接口时会报错: UnauthorizedHttpException“User not found”(使用 jwt.auth 中间件)。

代码

 // config/auth.php
 'defaults' => [
     'guard' => 'web',
     'passwords' => 'users',
 ],
 'guards' => [
     'web' => [
         'driver' => 'session',
         'provider' => 'users',
     ],
     'api' => [
        // 'driver' => 'token',
        'driver' => 'jwt',
        'provider' => 'customers',
    ],
],
// 控制器中间件声明
public function __construct() {
    $this->middleware('jwt.auth', ['except' => ['login', 'refresh']]);
}

排查过程

  1. 找到中间件报错位置
    // Tymon\JWTAuth\Http\Middleware\BaseMiddleware
    /**
    * Attempt to authenticate a user via the token in the request.
    *
    * @param  \Illuminate\Http\Request  $request
    *
    * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
    *
    * @return void
    */
    public function authenticate(Request $request) {
      $this->checkForToken($request);
      try {
          if (! $this->auth->parseToken()->authenticate()) {
              throw new UnauthorizedHttpException('jwt-auth', 'User not found');
          }
      } catch (JWTException $e) {
          throw new UnauthorizedHttpException(
              'jwt-auth',
              $e->getMessage(),
              $e,
              $e->getCode()
          );
      }
    }
    问题是 $this->auth->parseToken()->authenticate() 返回了 false
  2. 继续往下找
    // Tymon\JWTAuth\JWTAuth
    /**
    * Authenticate a user via a token.
    *
    * @return \Tymon\JWTAuth\Contracts\JWTSubject|false
    */
    public function authenticate() {
     $id = $this->getPayload()->get('sub');
     if (! $this->auth->byId($id)) {
         return false;
     }
     return $this->user();
    }
    此处 $this->auth->byId($id) 返回了 false 。什么?我的用户ID不存在?什么鬼,继续往下找。
  3. 继续往下找
    // Tymon\JWTAuth\Providers\Auth\Illuminate
    ...
    use Illuminate\Contracts\Auth\Guard as GuardContract;
    ...
    public function __construct(GuardContract $auth) {
     $this->auth = $auth;
    }
    ...
    /**
    * Authenticate a user via the id.
    * @param  mixed  $id
    * @return bool
    */
    public function byId($id) {
     return $this->auth->onceUsingId($id);
    }
    明显是这个 Guard 的实例并不是我们想要的,打出来看看他是什么鬼:Illuminate\Auth\SessionGuard,原形毕露,我们的中间件最后使用的 guard 是SessionGuard

问题

它调用 SessionGuard 去找的话会查到默认的 user 表中,能有这个ID就有鬼了。
那么,是我哪里搞错了呢?

各位大佬帮帮我,不胜感激

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 21

有种解决方法,可以在 jwt.auth 中间件前写一个前置的中间件切换默认 guard,经测试有效

路由部分修改为:

Route::group([
    'middleware' => ['jwt.switch:admin', 'jwt.auth']
], function (Router $router) {
...
public function handle(Request $request, Closure $next, $guard)
{
    // 切换默认guard
    config(['auth.defaults.guard' => $guard]);

    ...

    return $next($request);
}
2年前 评论
ASOUG 2年前
ASOUG 2年前
showcj (作者) 2年前

要么默认的guard 改成api

 // config/auth.php
 'defaults' => [
     'guard' => 'api',
     'passwords' => 'users',
 ],

要么中间件增加对应的guard

// 控制器中间件声明
public function __construct() {
    $this->middleware('auth:api', ['except' => ['login', 'refresh']]);
}
3年前 评论
liuwq (楼主) 3年前
岁月流沙 3年前
liuwq (楼主) 3年前

有配置 User Providers吗 没有的话参考这个配置。

// config/auth.php
...
'providers' => [
...
        'customers' => [
            'driver' => 'eloquent',
            'model' => App\Customers::class,   // customers 对应的模型
        ]
...
    ],
3年前 评论
liuwq (楼主) 3年前
public function authenticate() {
 $id = $this->getPayload()->get('sub');
 if (! $this->auth->byId($id)) {
     return false;
 }
 return $this->user();
}

可以在 if 的地方下个断点看auth是哪个类被传进来了,这个类是不是没实现GuardContract接口

3年前 评论
liuwq (楼主) 3年前

我也遇到了这个问题,期待解答

3年前 评论
liuwq (楼主) 3年前
littledragoner (作者) 3年前
liuwq (楼主) 3年前

把 guards的api和web的位置换一下试试,api放在first。 参考:docs.spatie.be/laravel-permission/...

3年前 评论
liuwq (楼主) 3年前

试试中间件改成 auth.api
我的配置和你一样的,管理后台用的jwt接口认证,web端用的session。只是中间件使用不一样。

Route::group([
    'middleware' => 'auth:api'
],function(){
});
3年前 评论
liuwq (楼主) 3年前

Laravel下多模块的认证 之前我自己写的,可以借鉴一下

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!