使用 JWT 时,添加自定义数据并在登陆时校验
需求如下
同一个用户有两种角色,分别定义了是否可以禁止登陆即冻结。
应用登陆时,需要选择角色,然后才点击登陆按钮。
当后台设置该用户的某个角色冻结时,需要禁止该角色使用我们的应用程序。
解决思路
1、登陆时,在token的自定义数据中指明角色类型。
2、每当任何一个请求到来时,都检查token中保存的角色类型,然后都到角色表查数据库一次,如果此人此角色被禁止,就返回错误。
3、本方案的缺点是:每次请求都需查一次数据库,但这是应产品的要求,只要后台设置冻结,就立刻禁止用户使用。其实也有其他方案,本文是比较简单、快捷、有效的一种。
4、另外,本文使用的是广受好评的 tymon/jwt-auth
表结构
表:有用户表,有角色表。
角色表字段:用户 id(user_id),角色 type(role_type),是否冻结(is_valid)。
解决步骤1:添加User模型类的两个子类
比如建立 UserType1 这个类
额外说明,这里代码里的 role_type 这个键是自己起名字的,不需要和表中字段一致。但值需要表示正确的角色类型 role_type。
namespace App\Models;
use Tymon\JWTAuth\Contracts\JWTSubject;
class UserType1 extends User implements JWTSubject
{
public function getJWTCustomClaims()
{
return ['role_type'=>1];
}
}
再建立 UserType2 这个类
namespace App\Models;
use Tymon\JWTAuth\Contracts\JWTSubject;
class UserType2 extends User implements JWTSubject
{
public function getJWTCustomClaims()
{
return ['role_type'=>2];
}
}
请注意,不是角色表。
解决步骤2:添加一个查冻结功能的中间件 ,并在 Kernel.php 中指定顺序
中间件名字自定,比如叫 PubUser
Kernel.php
protected $routeMiddleware = [
。。。
'pubuser' => \App\Http\Middleware\PubUser::class,
'jwt.auth' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class,
];
protected $middlewarePriority = [
\Tymon\JWTAuth\Http\Middleware\Authenticate::class,
\App\Http\Middleware\PubUser::class,
];
class PubUser
{
public function handle($request, Closure $next)
{
$user_sub = 角色表::query()->where('user_id', $request->user()->id)
->where('role_type', JWTAuth::getClaim('role_type'))
->firstOrFail();
if ($user_sub->is_valid != 1) {
throw new JWTException('您的账号被冻结');
}
return $next($request);
}
}
解决步骤3:登陆时,选择正确的子类
如下代码,当选择了正确的子类时,该子类的自定义数据将会被放到 JWT 的 token 中,然后就可以每次都读取自定义数据了。
public function success_login($user)
{
// $user 必须是 UserType2 或 UserType1 的实例,决不能是 User 的实例
$new_token = auth()->login($user);
return [
'new_token' => $new_token,
'expire_time' => time() + config('jwt.ttl') * 60,
];
}
解决步骤4:加载带有检查冻结功能中间件的路由
注意,下面的路由都是必须登陆后才能使用的。
登陆路由另外写。
api.php
Route::middleware(['jwt.auth', 'pubuser' ])->group(function () {
Route::any('/route1', 'IndexController@route1' );
Route::any('/route2', 'IndexController@route2' );
});
总的来说,就是轻松搞定。
妈妈再也不用担心我不知道怎么在 JWT 中添加自定义数据了!