Laravel jwt 多表(多用户端)验证隔离的实现


Posted in PHP onDecember 18, 2019

Tips: tymon/jwt-auth 作者已通过增加 prv 字段修复这一问题#1167,但是如果你是用 dingo api + jwt 的话,该问题依然存在。#

JWT 多表验证隔离

为什么要做隔离

当同一个 laravel 项目有多端(移动端、管理端......)都需要使用 jwt 做用户验证时,如果用户表有多个(一般都会有),就需要做 token 隔离,不然会发生移动端的 token 也能请求管理端的问题,造成用户越权。

会引发这个问题的原因是 laravel 的 jwt token 默认只会存储数据表的主键的值,并没有区分是那个表的。所以只要 token 里携带的 ID 在你的用户表中都存在,就会导致越权验证。

我们来看看 laravel 的 jwt token 的原貌:

{
 "iss": "http://your-request-url",
 "iat": 1558668215,
 "exp": 1645068215,
 "nbf": 1558668215,
 "jti": "XakIDuG7K0jeWGDi",
 "sub": 1
}

携带数据的是 sub 字段,其他字段是 jwt 的验证字段。

我们只看到 sub 的值为 1,并没有说明是那个表或是哪个验证器的。这个 token 通过你的验证中间件时,你使用不同的 guard 就能拿到对应表 id 为 1 的用户(了解 guard 请查看 laravel 的文档)。

解决办法

想要解决用户越权的问题,我们只要在 token 上带上我们的自定义字段,用来区分是哪个表或哪个验证器生成的,然后再编写自己的中间件验证我们的自定义字段是否符合我们的预期。

添加自定义信息到 token

我们知道要使用 jwt 验证,用户模型必须要实现 JWTSubject 的接口(代码取自jwt 文档):

<?php

namespace App;

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

class User extends Authenticatable implements JWTSubject
{
 use Notifiable;

 // Rest omitted for brevity

 /**
  * Get the identifier that will be stored in the subject claim of the JWT.
  *
  * @return mixed
  */
 public function getJWTIdentifier()
 {
  return $this->getKey();
 }

 /**
  * Return a key value array, containing any custom claims to be added to the JWT.
  *
  * @return array
  */
 public function getJWTCustomClaims()
 {
  return [];
 }
}

我们可以看看实现的这两个方法的作用:

  • getJWTIdentifier 的:获取会储存到 jwt 声明中的标识,其实就是要我们返回标识用户表的主键字段名称,这里是返回的是主键 'id',
  • getJWTCustomClaims:返回包含要添加到 jwt 声明中的自定义键值对数组,这里返回空数组,没有添加任何自定义信息。

接下来我们就可以在实现了 getJWTCustomClaims 方法的用户模型中添加我们的自定义信息了。

管理员模型:

/**
 * 额外在 JWT 载荷中增加的自定义内容
 *
 * @return array
 */
public function getJWTCustomClaims()
{
 return ['role' => 'admin'];
}

移动端用户模型:

/**
 * 额外在 JWT 载荷中增加的自定义内容
 *
 * @return array
 */
public function getJWTCustomClaims()
{
 return ['role' => 'user'];
}

这里添加了一个角色名作为用户标识。

这样管理员生成的 token 会像这样:

{
 "iss": "http://your-request-url",
 "iat": 1558668215,
 "exp": 1645068215,
 "nbf": 1558668215,
 "jti": "XakIDuG7K0jeWGDi",
 "sub": 1,
 "role": "admin"
}

移动端用户生成的 token 会像这样:

{
 "iss": "http://your-request-url",
 "iat": 1558668215,
 "exp": 1645068215,
 "nbf": 1558668215,
 "jti": "XakIDuG7K0jeWGDi",
 "sub": 1,
 "role": "user"
}

我们可以看到这里多了一个我们自己加的 role 字段,并且对应我们的用户模型。

接下来我们自己写一个中间件,解析 token 后判断是否是我们想要的角色,对应就通过,不对应就报 401 就好了。

编写 jwt 角色校验中间件

这里提供一个可全局使用的中间件 (推荐用在用户验证中间件前):

<?php
/**
 * Created by PhpStorm.
 * User: wlalala
 * Date: 2019-04-17
 * Time: 13:55
 */

namespace App\Http\Middleware;

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

class JWTRoleAuth extends BaseMiddleware
{
 /**
  * Handle an incoming request.
  *
  * @param $request
  * @param Closure $next
  * @param null $role
  * @return mixed
  */
 public function handle($request, Closure $next, $role = null)
 {
  try {
   // 解析token角色
   $token_role = $this->auth->parseToken()->getClaim('role');
  } catch (JWTException $e) {
   /**
    * token解析失败,说明请求中没有可用的token。
    * 为了可以全局使用(不需要token的请求也可通过),这里让请求继续。
    * 因为这个中间件的责职只是校验token里的角色。
    */
   return $next($request);
  }

  // 判断token角色。
  if ($token_role != $role) {
   throw new UnauthorizedHttpException('jwt-auth', 'User role error');
  }

  return $next($request);
 }
}

注册 jwt 角色校验中间件

在 app/Http/Kernel.php 中注册中间件:

/**
  * The application's route middleware.
  *
  * These middleware may be assigned to groups or used individually.
  *
  * @var array
  */
 protected $routeMiddleware = [
  // ...省略 ...

  // 多表jwt验证校验
  'jwt.role' => \App\Http\Middleware\JWTRoleAuth::class,
 ];

使用 jwt 角色校验中间件

接下来在需要用户验证的路由组中添加我们的中间件:

Route::group([
 'middleware' => ['jwt.role:admin', 'jwt.auth'],
], function ($router) {
 // 管理员验证路由
 // ...
});

Route::group([
 'middleware' => ['jwt.role:user', 'jwt.auth'],
], function ($router) {
 // 移动端用户验证路由
 // ...
});

至此完成 jwt 多表用户验证隔离。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
PHP 中的一些经验积累
Oct 09 PHP
基于文本的留言簿
Oct 09 PHP
PHP UTF8编码内的繁简转换类
Jul 20 PHP
PHP ? EasyUI DataGrid 资料取的方式介绍
Nov 07 PHP
教你如何在CI框架中使用 .htaccess 隐藏url中index.php
Jun 09 PHP
php过滤HTML标签、属性等正则表达式汇总
Sep 22 PHP
php+Mysqli利用事务处理转账问题实例
Feb 11 PHP
linux下为php添加iconv模块的方法
Feb 28 PHP
PHP常见错误提示含义解释(实用!值得收藏)
Apr 25 PHP
用Laravel Sms实现laravel短信验证码的发送的实现
Nov 29 PHP
PHP PDOStatement::fetchObject讲解
Feb 01 PHP
PHP获取ttf格式文件字体名的方法示例
Mar 06 PHP
PHP中用Trait封装单例模式的实现
Dec 18 #PHP
PHP实现微信公众号验证Token的示例代码
Dec 16 #PHP
浅析PHP中的 inet_pton 网络函数
Dec 16 #PHP
Laravel等框架模型关联的可用性浅析
Dec 15 #PHP
php获取是星期几的的一些常用姿势
Dec 15 #PHP
Yii2框架中一些折磨人的坑
Dec 15 #PHP
关于Yii2框架跑脚本时内存泄漏问题的分析与解决
Dec 01 #PHP
You might like
PHP中ltrim与rtrim去除左右空格及特殊字符实例
2016/01/07 PHP
音乐播放用的的几个函数
2006/09/07 Javascript
js css样式操作代码(批量操作)
2009/10/09 Javascript
javascript 日期常用的方法
2009/11/11 Javascript
javascript date格式化示例
2013/09/25 Javascript
多选列表框动态添加,移动,删除,全选等操作的简单实例
2014/01/13 Javascript
js浏览器本地存储store.js介绍及应用
2014/05/13 Javascript
javascript中var的重要性分析
2015/02/11 Javascript
js+css实现有立体感的按钮式文字竖排菜单效果
2015/09/01 Javascript
jQuery qrcode生成二维码的方法
2016/04/03 Javascript
在javascript中,null>=0 为真,null==0却为假,null的值详解
2017/02/22 Javascript
js的各种数据类型判断的介绍
2019/01/19 Javascript
JavaScript实现旋转木马轮播图
2020/03/16 Javascript
[02:44]DOTA2英雄基础教程 魅惑魔女
2014/01/07 DOTA
Eclipse中Python开发环境搭建简单教程
2016/03/23 Python
Python实现屏幕截图的代码及函数详解
2016/10/01 Python
PYTHON 中使用 GLOBAL引发的一系列问题
2016/10/12 Python
一个Python最简单的接口自动化框架
2018/01/02 Python
Python利用正则表达式实现计算器算法思路解析
2018/04/25 Python
python处理数据,存进hive表的方法
2018/07/04 Python
Opencv实现抠图背景图替换功能
2019/05/21 Python
python里dict变成list实例方法
2019/06/26 Python
python实现微信自动回复及批量添加好友功能
2019/07/03 Python
2020年10款优秀的Python第三方库,看看有你中意的吗?
2021/01/12 Python
使用CSS3制作倾斜导航条和毛玻璃效果
2017/09/12 HTML / CSS
html5定制表单_动力节点Java学院整理
2017/07/11 HTML / CSS
巴西最大的家具及装饰用品店:Mobly
2017/10/11 全球购物
美国巧克力喷泉品牌:Sephra
2019/05/05 全球购物
远东集团网络工程师面试题
2014/10/20 面试题
2014自荐信的写作技巧
2014/01/28 职场文书
高中美术教师事迹材料
2014/08/22 职场文书
村道德模范事迹材料
2014/08/28 职场文书
2014教师个人自我评价范文
2014/09/13 职场文书
2016年第16个全民国防教育日宣传活动总结
2016/04/05 职场文书
小学五年级(说明文3篇)
2019/08/13 职场文书
Pygame Time时间控制的具体使用详解
2021/11/17 Python