php - 在 Laravel 微服务中解码 JWT 并创建经过身份验证的用户,并与本地用户数据合并
问题描述
背景
我有一个微服务设置流程是:
client > api gateway > auth server > api gateway > microservice
- 客户有一个来自 Laravel 护照的“外部”JWT
- 客户端使用“外部”JWT 向 api 网关发送请求
- api 网关使用“外部”JWT 向身份验证服务器(Laravel 护照)发送请求
- 身份验证服务器验证用户仍然处于活动状态,并向 api 网关返回一个新的“内部”JWT,其中包含用户配置文件、组等
- api 网关使用这个新的“内部”JWT 将请求转发到微服务
- (到目前为止一切都很好)
- 微服务使用身份验证服务器公钥验证“内部”JWT
- 微服务解码“内部”JWT 并从包含在其中的配置文件创建用户对象
- 如果微服务有本地用户表(例如针对微服务特定的用户数据),则将本地数据与 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),除了:
- 我无法正确使用授权策略,因为 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() 函数。
请帮忙!
我不禁觉得有一种更好的(正确的?)方法可以使用“用户”而不是“通用用户”来实现这一点,这将允许 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;
}
});
}
用户.php
public function __construct(array $attributes = array(), $jwt_user = array())
{
parent::__construct($attributes);
foreach($jwt_user as $k=>$v){
$this->$k = $v;
}
}
推荐阅读
- ios - 核心数据图像不持久?
- audio - 处理中的 OutOfBoundsException(声音库)
- node.js - 在 npm Install 我收到“未处理的拒绝错误:完整性检查失败”,但是当我删除 package-lock.json 时,错误消失了
- ruby-on-rails - Rails 单点登录
- spring - 是什么导致“通过方法'setTargetDatastore'参数0表达的不满足的依赖关系”?
- python - 查询匹配的 json 数组键
- c - 检查 malloc/calloc 的分配
- kubernetes - Kubernetes 集群 CA 证书和 ca 证书私钥的路径
- nginx - Nginx + fcgiwrap 以高请求率抛出 502 Bad Gateway
- java - UCanAccess 加载数据库时出现 HSQLDB“精度或比例超出范围”错误