一、JWT的優點
1、服務端不需要保存傳統會話信息,沒有跨域傳輸問題,減小服務器開銷。
2、jwt構成簡單,占用很少的字節,便於傳輸。
3、json格式通用,不同語言之間都可以使用。
二、使用JWT進行用戶登錄鑒權的流程
① 用戶使用用戶名密碼來請求服務器
② 服務器進行驗證用戶的信息
③ 服務器通過驗證發送給用戶一個token
④ 客戶端存儲token,並在每次請求時附送上這個token值
⑤ 服務端驗證token值,並返回數據
三、php-jwt庫下載地址
1、通過composer下載:
composer require firebase/php-jwt
2、github下載地址:https://github.com/firebase/php-jwt
3、百度雲下載地址:https://pan.baidu.com/s/1lpyz8oKf_CM-kOi7MGVoPg
提取碼:wyq0
三、簡單示例
1、登錄頁面代碼:login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="showpage"> <div class="form-group"> <label for="username">用戶名</label> <input type="text" class="form-control" id="username" placeholder="請輸入用戶名"> </div> <div class="form-group"> <label for="password">密碼</label> <input type="password" class="form-control" id="password" placeholder="請輸入密碼"> </div> <button type="submit" id="sub-btn" class="btn btn-default">登錄</button> <br/> <p class="bg-warning" style="padding: 10px;">演示用戶名和密碼都是<code>demo</code>。</p> </div> </body> </html> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script> $('#sub-btn').on('click',function (e) { $.ajax({ type: 'post', url: './user.php?action=login', data: { username:$('#username').val(), password:$('#password').val(), }, dataType:'json', success: function (data, status, xhr) { alert(data.msg); if(data.code==200){ //將jwt存儲到本地 var jwt = xhr.getResponseHeader('Authorization'); localStorage.setItem("jwt", jwt); //跳轉登錄成功的頁面 window.location.href="./user.html"; } } }); }); </script>
2、登錄成功后頁面代碼:user.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>你已成功登陸</h3> </body> </html> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script> $.ajax({ type: 'post', url: './user.php', headers: { 'Authorization': localStorage.getItem("jwt") }, data: {}, async: true, dataType: 'json', success: function (data, status, xhr) { if(data.code!=200){ //jwt驗證失敗 } //如果響應頭接收到了Authorization,則將本地jwt更新 if (xhr.getResponseHeader('Authorization')) { localStorage.setItem("jwt", jwt); } }, error: function () { alert('error'); } }); </script>
3、后端PHP代碼:user.php
<?php /* 使用composer安裝php-jwt,接收到登錄用戶名和密碼后,PHP驗證用戶名和密碼是否正確 (實際開發中應該結合數據庫,從數據庫里拿用戶名和密碼比對,本實例為了演示只做簡單驗證), 如果用戶名和密碼准確無誤,那么就簽發token,在token中,我們可以定義token的簽發者 、過期時間等等,並返回給前端。注意在簽發token時,我們需要定義一個密鑰,這個密鑰是一個私鑰, 實際應用中是保密的不可告訴別人。 * */ require_once './php-jwt-master/src/JWT.php'; use \Firebase\JWT\JWT; define('KEY', '1gHuiop975cdashyex9Ud23ldsvm2Xq'); //密鑰 $action = isset($_GET['action']) ? $_GET['action'] : ''; if ($action == 'login') { if ($_SERVER['REQUEST_METHOD'] == 'POST') { $username = htmlentities($_POST['username']); $password = htmlentities($_POST['password']); $data = ['userid' => 1, 'username' => $username]; if ($username == 'demo' && $password == 'demo') { //用戶名和密碼正確,則簽發tokon $nowtime = time(); $token = [ 'iss' => 'http://www.helloweba.net', //簽發者 'aud' => $_SERVER['REMOTE_ADDR'], //jwt所面向的用戶 'iat' => $nowtime, //簽發時間 'exp' => $nowtime + 600, //過期時間-10min 'data' => $data ]; $jwt = JWT::encode($token, KEY); header("Authorization:$jwt"); $res = array('code' => 200, 'msg' => '登錄成功', 'data' => $data); } else { $res = array('code' => 300, 'msg' => '登錄失敗'); } } die(json_encode($res)); } else { $jwt = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : ''; if (empty($jwt)) { $res = array('code' => 301, 'msg' => 'You do not have permission to access.'); die(json_encode($res)); } try { JWT::$leeway = 60;//當前時間減去60,把時間留點余地 $token = JWT::decode($jwt, KEY, ['HS256']); //HS256方式,這里要和簽發的時候對應 } catch (Exception $exception) { $res = array('code' => 302, 'msg' => $exception->getMessage()); die(json_encode($res)); } // 疑似竊取用戶Token攻擊行為:請求的客戶端ip已經改變, 拒絕請求 if ($token->aud !== $_SERVER['REMOTE_ADDR']) { $res['msg'] = "請求的客戶端ip已經改變, 拒絕請求"; } // token過了有效期, 但是在回旋時間內, 靜默更新用戶token if ($token->exp < time()) { $token = (array)$token; $nowtime = time(); $token['iat'] = $nowtime; //簽發時間 $token['exp'] = $nowtime + 600; //過期時間-10min; $jwt = JWT::encode($token, KEY); header("Authorization:$jwt"); } $res = array('code' => 200, 'msg' => 'success'); die(json_encode($res)); }