Json web token (JWT), 是為了在網絡應用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標準((RFC 7519).該token被設計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便于從資源服務器獲取資源,也可以增加一些額外的其它業(yè)務邏輯所必須的聲明信息,該token也可直接被用于認證,也可被加密。
我們知道,http協(xié)議本身是一種無狀態(tài)的協(xié)議,而這就意味著如果用戶向我們的應用提供了用戶名和密碼來進行用戶認證,那么下一次請求時,用戶還要再一次進行用戶認證才行,因為根據http協(xié)議,我們并不能知道是哪個用戶發(fā)出的請求,所以為了讓我們的應用能識別是哪個用戶發(fā)出的請求,我們只能在服務器存儲一份用戶登錄的信息,這份登錄信息會在響應時傳遞給瀏覽器,告訴其保存為cookie,以便下次請求時發(fā)送給我們的應用,這樣我們的應用就能識別請求來自哪個用戶了,這就是傳統(tǒng)的基于session認證。
但是這種基于session的認證使應用本身很難得到擴展,隨著不同客戶端用戶的增加,獨立的服務器已無法承載更多的用戶,而這時候基于session認證應用的問題就會暴露出來.
Session: 每個用戶經過我們的應用認證之后,我們的應用都要在服務端做一次記錄,以方便用戶下次請求的鑒別,通常而言session都是保存在內存中,而隨著認證用戶的增多,服務端的開銷會明顯增大。
擴展性: 用戶認證之后,服務端做認證記錄,如果認證的記錄被保存在內存中的話,這意味著用戶下次請求還必須要請求在這臺服務器上,這樣才能拿到授權的資源,這樣在分布式的應用上,相應的限制了負載均衡器的能力。這也意味著限制了應用的擴展能力。
CSRF: 因為是基于cookie來進行用戶識別的, cookie如果被截獲,用戶就會很容易受到跨站請求偽造的攻擊。
基于token的鑒權機制類似于http協(xié)議也是無狀態(tài)的,它不需要在服務端去保留用戶的認證信息或者會話信息。這就意味著基于token認證機制的應用不需要去考慮用戶在哪一臺服務器登錄了,這就為應用的擴展提供了便利。
流程上是這樣的:
CORS(跨來源資源共享)
策略,一般我們在服務端這么做就可以了Access-Control-Allow-Origin: *
。 composer require firebase/php-jwt
app
文件夾下新建一個services
文件夾services
文件夾下新建JwtService.php
內容如下
<?php
namespace app\services;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Symfony\Component\VarDumper\VarDumper;
class JwtService{
public function getToken($uid){
$key="xuanransoftware";//自定義key值
$payload=array(
"iss"=>'http://api.xuanransoftware.com',//簽發(fā)者
"aud"=>"http://api.xuanransoftware.com",//接收者
"iat"=>time(),//初始時間
"nbf"=>time()+60*60*24*7,//過期時間為7天
"uid"=>$uid,//前端頁面?zhèn)鱱id
);
$jwt=JWT::encode($payload,$key,'HS256');
return $jwt;
}
public function checkToken($token){
$key="xuanransoftware";//自定義key值
$decoded=JWT::decode($token,new Key($key,'HS256'));
return $decoded;
}
}
php think make:controller admins/Login
Login控制器內容如下
<?php
declare (strict_types = 1);
namespace app\controller\admins;
use think\Request;
use app\BaseController;
// 引入admin模型
use app\model\Admin;
//引入JWT進行token驗證
use app\services\JwtService;
class Login extends BaseController
{
/**
* 顯示資源列表
*
* @return \think\Response
*/
public function login(Request $request)
{
//判斷是否為post請求
if($request->isPost()){
//獲取post參數
$params = $request->param() ;
$userName =trim($params['username']);
$password =trim(sha1($params['password']));
//使用Admin模型查找用戶名和密碼
$db = new Admin();
$index = $db->find('username',$userName);
//判斷用戶是否存在
if (isset($index)){
//判斷密碼是否正確
if($password==$index['password']){
//更新登錄時間
$index->lastlogin = time();
$index->save();
//根據用戶id生成token
$jwt = new JwtService();
$token = $jwt->getToken($index['id']);
return $this->msg(1,'登錄成功',$token);
}
else{
return $this->msg(0,'密碼錯誤');
}
}else{
return $this->msg(0,'用戶名不存在,請注冊后重新登錄');
}
}else{
return $this->msg(0,'請求類型碼錯誤');
}
}
//返回狀態(tài)碼
private function msg($code,$msg,$token='')
{
return json(['code'=>$code,'msg'=>$msg,'token'=>$token]);
}
}
php think make:middleware CheckToken
中間件內容如下
<?php
declare (strict_types = 1);
namespace app\middleware;
use app\services\JwtService;
class CheckToken
{
/**
* 處理請求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
$token=$request->param('token');
//沒有token
if (!$token){
return response("token是空的");
}
//沒有找到jwt類
$jwt=new JwtService();
try {
$decode=$jwt->checkToken($token);
}catch (\Exception $e){
echo $e;
return response("位置錯誤");
}
//解析token
$decode=json_decode(json_encode($decode),true);
var_dump($decode);
if (!$decode){
return response("token 不合法");
}
if (time() > $decode['nbf']){
return response("token 已過期");
}
return $next($request);
}
}
Copyright 2014-2025 http://ipnx.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號