Laravel5.6+ JWT 多表多用户登录

使用Laravel5.6+JWT+dingo时。

之前用到laravel-5.4 的多表多用户配置,其中有一个严重的潜在bug。。当我拿到用户user表的token时,如果在后台admin表里面存在相同ID数据时,token是可以直接验证通过的。-例如我的用户表ID为100的用户.数据。如果后台管理表也存在这个ID为100数据。就可以直接用前台的用户token 去验证后台所有接口。细思极恐啊

然后我的解决思路时给各个用户表加一个字段,让他们各自有各自的身份。但是这样解决毕竟不符合框架的优雅性白白的增加代码逻辑,经过不懈寻找,百度。发现把laravel升到5.6可以解决这个问题。下面是实操记录:

1. 先使用 composer 安装 jwt-auth 执行下面命令

       composer require "tymon/jwt-auth 1.*@rc"

2. 发布生成配置文件

       php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

3. 使用以下命令生成密钥

      php artisan jwt:secret

4. 配置多 guard 来区分认证 在 config/auth.php

  ['guards' => [
       'web' => [
           'driver' => 'jwt',
           'provider' => 'users',
       ],
       //前端api验证
       'api' => [
           'driver' => 'jwt',
           'provider' => 'users',
       ],
       // 后台验证
       'admin' => [
           'driver' => 'jwt',
           'provider' => 'admins',
       ],
       //销售人员验证
       'salesman' => [
           'driver' => 'jwt',
           'provider' => 'salesman',
       ]
   ],'providers' => [
       'users' => [
           'driver' => 'eloquent',
           'model' => User::class
       ],
       'admins' => [
           'driver' => 'eloquent',
            'model' => Admin::class,

       ],
        'salesman' => [

            'driver' => 'eloquent',

           'model' => ActivitySalesman::class,
       ]
  ]
 

5.更改相应的models文件如下。跟原来区别—主要 getJWTCustomClaims方法添加模型键值

<?php
namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * 获取会储存到 jwt 声明中的标识
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * 返回包含要添加到 jwt 声明中的自定义键值对数组
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return ['role' => 'user'];
    }
}
<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;

class Admin extends Authenticatable implements JWTSubject
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * 获取会储存到 jwt 声明中的标识
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * 返回包含要添加到 jwt 声明中的自定义键值对数组
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return ['role' => 'admin'];
    }
}

6. 在 router/api.php 文件中创建路由信息   主要是使用中间件做验证 ->middleware([‘jwt.role:****’, ‘jwt.auth’]);

//普通用户登录
Route::group(['prefix' => 'auth'], function () {
    Route::post('login', 'AuthController@me')->middleware(['jwt.role:user', 'jwt.auth']);
});
//后台用户登录
Route::group(['prefix' => 'admin', 'namespace' => 'Admin'], function () {
    Route::post('login', 'LoginController@me')->middleware(['jwt.role:admin', 'jwt.auth'])->name('me');
});

7. 创建中间件检测当前 token 对应的是哪个表 php artisan make:middleware JWTRoleAuth

<?php

namespace App\Http\Middleware;

use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

class JWTRoleAuth extends BaseMiddleware
{
    /**
     * JWT 检测当前登录的平台
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  null $role
     * @return mixed
     */
    public function handle($request, Closure $next, $role = null)
    {
        try {
            // 解析token角色
            $tokenRole = $this->auth->parseToken()->getClaim('role');
        } catch (JWTException $e) {
            /**
             * token解析失败,说明请求中没有可用的token。
             * 为了可以全局使用(不需要token的请求也可通过),这里让请求继续。
             * 因为这个中间件的责职只是校验token里的角色。
             */
            return $next($request);
        }
        // 判断token角色。
        if ($tokenRole != $role) {
            throw new UnauthorizedHttpException('jwt-auth', 'User role error');
        }
        return $next($request);
    }
}

大概原理应该就是在生成token的时候,给那条数据增加一个role字段。在验证token 的时候去检查role字段是否存在。跟我想法不谋而合

欢迎来收藏–https://www.talksphp.com/2020/09/05/larave-jwt-dinggo/

Read More

发表回复