Lcobucci/jwt 多應(yīng)用認(rèn)證插件(支持單點(diǎn))

簡(jiǎn)介
Lcobucci/jwt Integration For webman
webman的jwt(JSON Web Token)身份驗(yàn)證包。支持Header、Cookie、Param等多種傳參方式
說(shuō)明
目前支持如下三大類型加密方式:RSA,HASH,DSA。再各分256、384、512位。 默認(rèn)是HS256,即hash 256位加密。
需要修改加密方式,請(qǐng)修改參數(shù):signer,參數(shù)選項(xiàng):
-
HS256
備注:hash 256位 -
HS384
備注:hash 384位 -
HS512
備注:hash 512位 -
RS256
備注:rsa 256位 -
RS384
備注:rsa 384位 -
RS512
備注:rsa 512位 -
ES256
備注:dsa 256位 -
ES384
備注:dsa 384位 -
ES512
備注:dsa 512位
重要:RSA和DSA 都是非對(duì)稱加密方式,除了修改參數(shù)signer外,需要配置:PUBLIC_KEY、PRIVATE_KEY兩個(gè)參數(shù), 這兩個(gè)參數(shù)是密鑰文件路徑
安裝
composer require yzh52521/webman-jwt-auth
多應(yīng)用 需要插件大于版本 >=1.1.1
單點(diǎn)登錄多點(diǎn)登錄 需要插件 >=2.0.0
說(shuō)明:
支持多應(yīng)用單點(diǎn)登錄、多應(yīng)用多點(diǎn)登錄、多應(yīng)用支持注銷 token(token會(huì)失效)、支持多應(yīng)用刷新 token
多應(yīng)用單點(diǎn)登錄:在該應(yīng)用配置下只會(huì)有一個(gè) token 生效,一旦刷新 token ,前面生成的 token 都會(huì)失效,一般以用戶 id 來(lái)做區(qū)分
多應(yīng)用多點(diǎn)登錄:在該配置應(yīng)用下token 不做限制,一旦刷新 token ,則當(dāng)前配置應(yīng)用的 token 會(huì)失效
注意:使用多應(yīng)用單點(diǎn)登錄或者多應(yīng)用多點(diǎn)登錄時(shí),必須要開(kāi)啟黑名單,如果不開(kāi)啟黑名單,無(wú)法使 token 失效,生成的 token 會(huì)在有效時(shí)間內(nèi)都可以使用(未更換證書或者 secret )。
多應(yīng)用單點(diǎn)登錄原理:JWT 有七個(gè)默認(rèn)字段供選擇。單點(diǎn)登錄主要用到 jti 默認(rèn)字段,jti 字段的值默認(rèn)為緩存到redis中的key(該key的生成為應(yīng)用名稱+存儲(chǔ)的用戶id),這個(gè)key的值會(huì)存一個(gè)簽發(fā)時(shí)間,token檢測(cè)會(huì)根據(jù)這個(gè)時(shí)間來(lái)跟token原有的簽發(fā)時(shí)間對(duì)比,如果token原有時(shí)間小于等于redis存的時(shí)間,則認(rèn)為無(wú)效
多應(yīng)用多點(diǎn)登錄原理:多點(diǎn)登錄跟單點(diǎn)登錄差不多,唯一不同的是jti的值不是應(yīng)用名稱+用戶id,而是一個(gè)唯一字符串,每次調(diào)用 refresh 來(lái)刷新 token 或者調(diào)用 logout 注銷 token 會(huì)默認(rèn)把請(qǐng)求頭中的 token 加入到黑名單,而不會(huì)影響到別的 token
token 不做限制原理:token 不做限制,在 token 有效的時(shí)間內(nèi)都能使用,你只要把配置文件中的 blacklist_enabled 設(shè)置為 false 即可,即為關(guān)閉黑名單功能
使用
完整配置
<?php
return [
'manager' => [
//是否開(kāi)啟黑名單,單點(diǎn)登錄和多點(diǎn)登錄的注銷、刷新使原token失效,必須要開(kāi)啟黑名單
'blacklist_enabled' => true,
//黑名單緩存的前綴
'blacklist_prefix' => 'yzh52521',
//黑名單的寬限時(shí)間 單位為:秒,注意:如果使用單點(diǎn)登錄,該寬限時(shí)間無(wú)效
'blacklist_grace_period' => 0,
],
'stores' => [
// 單應(yīng)用
'default' => [
'login_type' => 'mpo',
'signer_key' => 'oP0qmqzHS4Vvml5a',
'public_key' => 'file://path/public.key',
'private_key' => 'file://path/private.key',
'expires_at' => 3600,
'refresh_ttL' => 7200,
'leeway' => 0,
'signer' => 'HS256',
'type' => 'Header',
'auto_refresh' => false,
'iss' => 'webman.client.api',
'event_handler' => Event::class,
'user_model' => User::class
],
// 多應(yīng)用
'admin' => [
'login_type' => 'mpo',
'signer_key' => 'oP0qmqzHS4Vvml5a',
'public_key' => 'file://path/public.key',
'private_key' => 'file://path/private.key',
'expires_at' => 3600,
'refresh_ttL' => 7200,
'leeway' => 0,
'signer' => 'HS256',
'type' => 'Header',
'auto_refresh' => false,
'iss' => 'webman.client.admin',
'event_handler' => Event::class,
'user_model' => User::class
],
]
];
token
- login_type 登錄方式,sso為單點(diǎn)登錄,mpo為多點(diǎn)登錄
- signer_key 密鑰
- expires_at Token有效期, 單位秒
- refresh_ttL 刷新有效期, 單位秒
- leeway 時(shí)鐘偏差冗余時(shí)間,單位秒。建議這個(gè)余地應(yīng)該不大于幾分鐘。
- signer 加密算法
- type 獲取 Token 途徑
- auto_refresh 開(kāi)啟過(guò)期自動(dòng)續(xù)簽
- event_handler token用戶事件 (實(shí)現(xiàn) yzh52521\JwtAuth\event\EventHandler )
- user_model 用戶模型 (實(shí)現(xiàn) yzh52521\JwtAuth\user\AuthorizationUserInterface )
Token 生成
public function login()
{
//...登錄判斷邏輯
$config = JwtAuth::getConfig();
// 自動(dòng)獲取當(dāng)前應(yīng)用下的jwt配置
return json([
'token' => JwtAuth::token($uid, ['params1' => 1, 'params2' => 2])->toString(),
'token_type' => $config->getType(),
'expires_in' => $config->getExpires(),
'refresh_in' => $config->getRefreshTTL(),
]);
// 自定義用戶模型
return json([
'token' => JwtAuth::token($uid, ['user_model' => UserMember::class])->toString(),
'token_type' => $config->getType(),
'expires_in' => $config->getExpires(),
'refresh_in' => $config->getRefreshTTL(),
]);
}
Token 驗(yàn)證
public function verify()
{
try {
$payload=JwtAuth::verify($token); //驗(yàn)證token, 并獲取token中的payload部分
//$uid = $payload['jti']; //可以繼而獲取payload里自定義的字段,比如uid
//$uid =JwtAuth::getVerifyToken()->claims()->get('jti');
} catch (JwtException $e) {
throw new TokenInvalidException('登錄校驗(yàn)已失效, 請(qǐng)重新登錄', 401);
}
// 驗(yàn)證成功
// 如配置用戶模型文件 可獲取當(dāng)前用戶信息
var_dump(JwtAuth::getUser());
}
Token 刷新
注意:必須驗(yàn)證通過(guò)才可以刷新 獲取新Token
$jwt =JwtAuth::refresh();
Token 注銷
注銷后Token就失效了(用戶退出)
JwtAuth::logout($token);
Token 動(dòng)態(tài)獲取過(guò)期時(shí)間
JwtAuth::getTokenExpirationTime($token=null);
Token 移除黑名單token(指定某個(gè))
JwtAuth::removeBlackList($token);
Token 移除所有黑名單Token
JwtAuth::clearBlackList();
路由驗(yàn)證示例
Route::any('/user/index', [app\controller\User::class, 'index'])
->middleware([JwtAuthMiddleware::class]);
// 自定義應(yīng)用驗(yàn)證 (Webman-framework >= 1.3.16)
Route::any('/user/index', [app\controller\User::class, 'index'])
->setParams(['store'=>'api'])
->middleware([JwtAuthMiddleware::class]);
Token 自動(dòng)獲取
支持以下方式自動(dòng)獲取
Header
Cookie
Url
賦值方式
類型 | 途徑 | 標(biāo)識(shí) |
---|---|---|
Header | Authorization | Bearer Token |
Cookie | Cookie | token |
Url | Request | token |
過(guò)期自動(dòng)續(xù)簽
auto_refresh => true
系統(tǒng)檢測(cè)到 Token 已過(guò)期, 會(huì)自動(dòng)續(xù)期并返回以下 header 信息。
- Automatic-Renewal-Token
- Automatic-Renewal-Token-RefreshAt
前端需要接收最新 Token,下次異步請(qǐng)求時(shí),攜帶此 Token。
用戶模型示例如下
namespace app\common\model;
use support\Model;
use yzh52521\JwtAuth\user\AuthorizationUserInterface;
class User extends Model implements AuthorizationUserInterface
{
public function getUserById($id)
{
return $this->find($id);
}
}
用戶事件示例如下
namespace app\common\event;
use Lcobucci\JWT\Token;
class UserEvent implements \yzh52521\JwtAuth\event\EventHandler
{
public function login(Token $token)
{
// todo
echo 'login';
}
public function logout(Token $token)
{
// todo
}
public function verify(Token $token)
{
// todo
echo 'verify';
}
}
中間件示例如下
class JwtAuthMiddleware implements MiddlewareInterface
{
public function process(Request $request, callable $next): Response
{
if ($request->method() === 'OPTIONS') {
response('', 204);
}
if ( $route = $request->route ) {
$store = $route->param('store');
}
$store = $store ?? ( \request()->app==='' ? 'default' : \request()->app);
try {
$requestToken = new RequestToken();
$handel = JwtAuth::getConfig($store)->getType();
$token = $requestToken->get($handel);
JwtAuth::verify($token);
$request->user = JwtAuth::getUser();
return $next($request);
} catch (JwtException $e) {
throw new JwtException($e->getMessage(), $e->getCode());
}
}
}
備注 :多應(yīng)用 默認(rèn)是在應(yīng)用目錄里使用 跨應(yīng)用 生成token 驗(yàn)證token 解析token 等使用如下
實(shí)例化 參數(shù)應(yīng)用名
$JwtAuth =new yzh52521\JwtAuth\JwtAuth('default');
//生成token
$config = $JwtAuth->getConfig();
return json([
'token' => $JwtAuth->token($uid, ['params1' => 1, 'params2' => 2])->toString(),
'token_type' => $config->getType(),
'expires_in' => $config->getExpires(),
'refresh_in' => $config->getRefreshTTL(),
]);
//驗(yàn)證token
$JwtAuth =new yzh52521\JwtAuth\JwtAuth('default');
try {
$data= $JwtAuth->verify($token);
dump($data);
} catch (JwtException $e) {
throw new TokenInvalidException('登錄校驗(yàn)已失效, 請(qǐng)重新登錄', 401);
}
//解析token
$JwtAuth =new yzh52521\JwtAuth\JwtAuth('default');
$JwtAuth->parseToken($token);
$JwtAuth->getVerifyToken();