簡體   English   中英

在 Laravel 微服務中解碼 JWT 並創建經過身份驗證的用戶,與本地用戶數據合並

[英]Decode JWT and create authenticated User in Laravel microservice, merging with local user data

背景

我有一個微服務設置,流程是:

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 數據合並

微服務認證

我創建了一個 JwtGuard,它可以解碼 JWT 並使用 GenericUser 創建一個用戶:

身份驗證文件

'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),除了:

  • 我無法正確使用授權策略,因為 GenericUser 沒有 can() 方法
  • 沒有簡單的方法可以與本地用戶對象合並

到目前為止我所擁有的

我嘗試了以下將本地用戶數據與 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() 函數。


請幫忙!

我不禁覺得有一種更好的(正確的?)方法可以使用“User”而不是“GenericUser”來實現這一點,這將允許 Laravel 中的所有身份驗證/授權功能正常工作,並輕松合並數據。

我通過將 $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;
            }
        });
    }

用戶名

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

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

實現這一目標的簡單方法是:

use Firebase\JWT\JWT;
use Laravel\Passport\Token;

$jwt = 'eyJ0...';
$publicKey = file_get_contents(storage_path('oauth-public.key'));
$res = JWT::decode($jwtToken, $publicKey, ['RS256']);
$user = Token::findOrFail($res->jti)->user;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM