以往,沒有做過前后端分離的項目之前,都是服務器渲染的模板,然后用cookie和session進行賬號的權限驗證或者是登錄狀態的管理。后來接觸了vue和小程序之后,在進行前后端分離的時候,就會遇到權限驗證和登錄會話保存。因為HTTP協議是開放的,可以任人調用。所以,如果接口不希望被隨意調用,就需要做訪問權限的控制,認證是好的用戶,才允許調用API。
JWT優點
1:服務端不需要保存傳統會話信息,沒有跨域傳輸問題,減小服務器開銷。
2:jwt構成簡單,占用很少的字節,便於傳輸。
3:json格式通用,不同語言之間都可以使用。
jwt由三部分組成:
頭部(header) 載荷(payload) 包含一些定義信息和自定義信息 簽證(signature)
所以這里就會用到bearer的令牌訪問,就是jwt;定義:為了驗證使用者的身份,需要客戶端向服務器端提供一個可靠的驗證信息,稱為Token,這個token通常由Json數據格式組成,通過hash散列算法生成一個字符串,所以稱為Json Web Token(Json表示令牌的原始值是一個Json格式的數據,web表示是在互聯網傳播的,token表示令牌,簡稱JWT)
首先我們從GitHub處用composer require firebase/php-jwt
下載firebase/php-jwt,怎么用composer我就不累述了,我過去的文章里面有安裝教程。
安裝好了之后,我們可以新建一個user控制來測試代碼的完整性,首先我們創建三個控制方法
Base.php
基礎控制器base.php主要是用來驗證每次接受請求的時候,驗證http請求頭里面是否攜帶了token,如何將token放到請求頭里面,這個前端會做的了。
<?php
/**
* Created by PhpStorm.
* User: nobita
* Date: 2/15
* Time: 14:55
*/
namespace app\user\controller;
use think\Request;
use Firebase\JWT\JWT;
use think\Controller;
class Base extends Controller
{
public function _initialize()
{
parent::_initialize();
$this->checkToken();
}
public function checkToken()
{
$header = Request::instance()->header();
if ($header['authorization'] == 'null'){
echo json_encode([
'status' => 1002,
'msg' => 'Token不存在,拒絕訪問'
]);
exit;
}else{
$checkJwtToken = $this->verifyJwt($header['authorization']);
if ($checkJwtToken['status'] == 1001) {
return true;
}
}
}
//校驗jwt權限API
protected function verifyJwt($jwt)
{
$key = md5('nobita');
// JWT::$leeway = 3;
try {
$jwtAuth = json_encode(JWT::decode($jwt, $key, array('HS256')));
$authInfo = json_decode($jwtAuth, true);
$msg = [];
if (!empty($authInfo['user_id'])) {
$msg = [
'status' => 1001,
'msg' => 'Token驗證通過'
];
} else {
$msg = [
'status' => 1002,
'msg' => 'Token驗證不通過,用戶不存在'
];
}
return $msg;
} catch (\Firebase\JWT\SignatureInvalidException $e) {
echo json_encode([
'status' => 1002,
'msg' => 'Token無效'
]);
exit;
} catch (\Firebase\JWT\ExpiredException $e) {
echo json_encode([
'status' => 1003,
'msg' => 'Token過期'
]);
exit;
} catch (Exception $e) {
return $e;
}
}
}
Login.php
登錄控制器,只要是用來驗證用戶輸入的賬號密碼是否匹配數據庫的信息,如果匹配的話,就申請token,並且返回token給前端儲存在本地,每次請求的時候把token假如到請求頭里面
<?php
/**
* Created by PhpStorm.
* User: nobita
* Date: 2/15
* Time: 14:55
*/
namespace app\user\controller;
use app\common\model\nobita\Test as TestModel;
use Firebase\JWT\JWT;
class Login
{
public function index()
{
$data = input('post.');
$username = htmlspecialchars($data['username']);
$password = htmlspecialchars($data['password']);
$user = TestModel::where('username', $username)->find();
if (!empty($user)) {
if ($username === $user['username'] && $password === $user['password']) {
$msg = [
'status' => 1001,
'msg' => '登錄成功',
'jwt' => self::createJwt($user['id'])
];
return $msg;
} else {
return [
'status' => 1002,
'msg' => '賬號密碼錯誤'
];
}
} else {
return [
'status' => 1002,
'msg' => '請輸入賬號密碼'
];
}
}
public function createJwt($userId)
{
$key = md5('nobita'); //jwt的簽發密鑰,驗證token的時候需要用到
$time = time(); //簽發時間
$expire = $time + 14400; //過期時間
$token = array(
"user_id" => $userId,
"iss" => "https://199508.com",//簽發組織
"aud" => "https://199508.com", //簽發作者
"iat" => $time,
"nbf" => $time,
"exp" => $expire
);
$jwt = JWT::encode($token, $key);
return $jwt;
}
}
User.php
用來驗證代碼的完整性
<?php
/**
* Created by PhpStorm.
* User: nobita
* Date: 2/15
* Time: 15:24
*/
namespace app\user\controller;
use think\Request;
use app\common\model\nobita\Test as TestModel;
class User extends Base //繼承基礎控制器
{
public function index()
{
return TestModel::all();
}
}