首页 > 解决方案 > 在 Laravel 微服务中解码 JWT 并创建经过身份验证的用户,并与本地用户数据合并

问题描述

背景

我有一个微服务设置流程是:

client > api gateway > auth server > api gateway > microservice
  1. 客户有一个来自 Laravel 护照的“外部”JWT
  2. 客户端使用“外部”JWT 向 api 网关发送请求
  3. api 网关使用“外部”JWT 向身份验证服务器(Laravel 护照)发送请求
  4. 身份验证服务器验证用户仍然处于活动状态,并向 api 网关返回一个新的“内部”JWT,其中包含用户配置文件、组等
  5. api 网关使用这个新的“内部”JWT 将请求转发到微服务
  6. (到目前为止一切都很好)
  7. 微服务使用身份验证服务器公钥验证“内部”JWT
  8. 微服务解码“内部”JWT 并从包含在其中的配置文件创建用户对象
  9. 如果微服务有本地用户表(例如针对微服务特定的用户数据),则将本地数据与 JWT 数据合并

微服务认证

我创建了一个可以解码 JWT 并使用 GenericUser 创建用户的 JwtGuard:

授权文件

'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

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

AuthServiceProvider.php

public function boot()
    {
        $this->registerPolicies();

        Auth::extend('jwt', function ($app) {
            return new JwtGuard($app['request']);
        });
    }

JwtGuard.php

<?php
namespace App\Services\Auth;

use Illuminate\Auth\GenericUser;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;

use \Firebase\JWT\JWT;
use Illuminate\Http\Request;

class JwtGuard implements Guard {

    use GuardHelpers;

    /**
     * @var Request
     */
    private $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    /**
     * Get the currently authenticated user.
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user()
    {
        if (!is_null($this->user)) {
            return $this->user;
        }

        if(!$jwt = $this->getJwt()) {
            return null;
        }

        return $this->decode($jwt);
    }

    /**
     * Validate a user's credentials.
     *
     * @param  array $credentials
     * @return bool
     */
    public function validate(array $credentials = [])
    {
        if(!$jwt = $this->getJwt()) {
            return false;
        }

        return !is_null($this->decode($jwt))?true:false;
    }

    /**
     * Decode JWT and return user
     *
     * @return mixed|null
     */
    private function decode($jwt)
    {
        $publicKey = file_get_contents(storage_path('oauth-public.key'));

        try {
            $res = JWT::decode($jwt, $publicKey, array('RS256'));
            return $this->user = new GenericUser(json_decode(json_encode($res->user), true));
        } catch (\Exception $e) {
            return null;
        }
    }

    private function hasAuthHeader()
    {
        return $this->request->header('Authorization')?true:false;
    }

    private function getJwt()
    {
        if(!$this->hasAuthHeader()){
            return null;
        }

        preg_match('/Bearer\s((.*)\.(.*)\.(.*))/', $this->request->header('Authorization'), $jwt);

        return $jwt[1]?$jwt[1]:null;
    }

}

问题

这工作正常(ish),除了:


到目前为止我所拥有的

我尝试了以下方法将本地用户数据与 JWT 配置文件合并:

private function decode($jwt)
    {
        $publicKey = file_get_contents(storage_path('oauth-public.key'));

        try {
            $res = JWT::decode($jwt, $publicKey, array('RS256'));
            $this->user = new GenericUser(json_decode(json_encode($res->user), true));
            $this->user->localUser = \App\User::where('user_id', $this->user->id)->first();
            return $this->user;
        } catch (\Exception $e) {
            return null;
        }
    }

但这仍然使 GenericUser 没有 can() 函数。


请帮忙!

我不禁觉得有一种更好的(正确的?)方法可以使用“用户”而不是“通用用户”来实现这一点,这将允许 Laravel 中的所有身份验证/授权功能正常工作,并轻松合并数据。

标签: phplaraveljwtmicroservices

解决方案


我通过将 $jwt_user 添加到 User 构造以跳过“可填充”来解决它:

授权文件

'defaults' => [
    'guard' => 'api',
],
'guards' => [
    'api' => [
        'driver' => 'jwt',
    ],
],

AuthServiceProvider.php

use App\User;
use \Firebase\JWT\JWT;

public function boot()
    {
        $this->registerPolicies();

        Auth::viaRequest('jwt', function ($request) {
            $publicKey = file_get_contents(storage_path('oauth-public.key'));

            if(!$hasAuthHeader = $request->header('Authorization')?true:false){
                return null;
            }

            preg_match('/Bearer\s((.*)\.(.*)\.(.*))/', $request->header('Authorization'), $jwt);

            try {
                $res                        = JWT::decode($jwt[1], $publicKey, array('RS256'));
                $jwt_user                   = json_decode(json_encode($res->user), true);
                $local_user                 = User::find($jwt_user['id']);
                $jwt_user['local_profile']  = $local_user?$local_user:[];
                $user                       = new User([], $jwt_user);
                return $user;
            } catch (\Exception $e) {
                return null;
            }
        });
    }

用户.php

public function __construct(array $attributes = array(), $jwt_user = array())
    {
        parent::__construct($attributes);

        foreach($jwt_user as $k=>$v){
            $this->$k = $v;
        }
    }

推荐阅读