laravel tymon/jwt-auth在用户是多表情况下的授权验证

昌乐
2023-12-01

导语

接上一篇文章末尾抛出的问题,在多表用户的情况下,一个守卫对应的一个资源提供者势必不能够满足要求了。这个时候最直观的操作就是增加守卫及其对应的资源提供者,用来对这个新数据表进行验证

接下来是很重要的一段话,请仔细阅读:

当两个守卫(本文以user和admin为例)所对应的资源提供者不同时(分别对应Model\User,\Model\admin),假设分别以两表中id为1的记录生成token,分别为token-user,token-admin。

在版本为0.5.*的tymon/jwt-auth下,守卫的功能是解密token并找到与载荷中sub字段对应的主键记录信息,注意,这个版本的守卫并不会判断该token是否创建于本守卫对应的资源提供者。,也就是以User表id为1的记录登录验证之后,生成的token,不但在user守卫处能够通过,而且在admin守卫处也能通过,因为这个版本的守卫只要看到正确解密+sub主键在表中对应存在就判断你是非常合法的token,就会放行。所以就形成了老版本下所谓的权限越权,要进行手动权限隔离的操作。

但是,好消息来了,在最新版的1.0.0及以上版本中,守卫又增加了一项新功能:除了正确解密+sub主键对应查找,还要进行token创建源头的验证,也就是要判断token创建时对应的资源提供者和当前验证守卫对应的资源提供者是否一致,不一致就验证不通过,所以就自动实现了权限隔离,非常方便。

在新版本中,享受到了权限隔离自动化的红利之后,就会意识到一个问题。如果token-user创建成功,跟随请求进入服务器,如果使用admin守卫进行验证,因为自动隔离,验证不通过,就会报出token-user是非法token的结果,但事实上,token-user并不是非法的,而是验证的位置不正确。它是user端登录后创建的token,在逻辑范围上应该去到user守卫处验证才能得出最终结果。所以,在多表情况下,怎么引导token前往正确的验证守卫才是至关重要的。

因为jwt本身并不携带token创建的源头信息,所以我们要人为的改写jwt数据,用来记录token创建的源头。这个时候我们就要想起来jwt数据中一个组成部分:payload,载荷。这部分信息是可以自定义扩展的。我们可以在该部分信息中心加入tokenType字段,记录token的隶属。然后在token验证前,根据tokenType来判断应该使用那个守卫。从而完成token正确引导的功能。

代码实现

两个表守卫及对应资源提供者的配置:config/auth.php
'guards' => [
        'web' => [
            'driver'   => 'session',
            'provider' => 'users',
        ],

        'user' => [
            'driver'   => 'jwt',
            'provider' => 'user',
        ],

        'admin' => [
            'driver'   => 'jwt',
            'provider' => 'admin',
        ],
    ],
'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model'  => App\User::class,
    ],

    'user' => [
        'driver' => 'eloquent',
        'model'  => \App\Models\User::class,
    ],

    'admin' => [
        'driver' => 'eloquent',
        'model'  => App\Models\Admin::class,
    ],

    // 'users' => [
    //     'driver' => 'database',
    //     'table' => 'users',
    // ],
],
根据各自守卫对应的资源提供者创建token,并在载荷中携带token创建源标识字段
  if ($request->input('role') == 'user') {
            $user      = User::first();
            $tokenInfo = $user;
            $token     = auth()->claims(['tokenType' => 'user'])->login($user);
        } else {
            $admin     = Admin::first();
            $tokenInfo = $admin;
            $token     = auth('admin')->claims(['tokenType' => 'admin'])->login($admin);
        }
        return response()->json(['user' => $tokenInfo, 'token' => $token]);
根据jwt载荷中tokenType的内容将token引导到正确的守卫进行验证
$payload = auth()->payload()->toArray();
$user    = auth($payload['tokenType'])->user();
return response()->json(['tokenUser' => $user]);

这里必须明确一点,auth()辅助函数会根据参数,也就是守卫的名称来初始化响应的守卫实例,这些守卫实例中有很多能够共用的相同的方法来处理不同守卫中中功能重合的事务,比如说对于载荷等共有部分的处理。所以说在获取载荷信息时,只要是共有的功能,可以随便指定守卫来进行处理。

auth()的功能其实就是初始化一个守卫实例,然后把token引入这个守卫实例进行验证。

结语

不知不觉,又要结束了,希望知识能够越积累越多,最近正在学graphql,准备结合laravel使用,等我出文章。

 类似资料: