最近給本身列了一個list,Ummm...列來列去大概是下面這個樣子:前端
好吧,誰讓本身菜呢,沒什麼好抱怨的,一個一個來吧。正好最近看了一些token作身份認證的文章,發現其中大部分都是說token登陸怎麼怎麼好,反正沒有幾個認認真真的實現的。。。正好,秉着我是小白我怕誰的原則,繼續分享一下express + jwt的填坑經歷。爲何題目起名是最輕實踐呢?由於確實看完這個你能夠大概理解token登陸的好處以及如何簡單的實現一個先後端經過token進行認證的小系統。這個demo是在我第一篇文章那個腳手架上跑起來的,感興趣的還能夠回顧一下----->express-react-scaffold。具體實現就是下面這個樣子:react
在先後端分離的系統中,身份認證是十分重要的,目前經常使用的兩種身份認證方式以下:ios
JWT的本質實際上就是一個字符串,它有三部分組成頭部+載荷+簽名。git
// Header
{
"alg": "HS256",//所使用的簽名算法
"typ": "JWT"
}
// Payload
{
//該JWT的簽發者
"iss": "luffy",
// 這個JWT是何時簽發的
"iat":1441593502,
//何時過時,這是一個時間戳
"exp": 1441594722,
// 接收JWT的一方
"aud":"www.youdao.com",
// JWT所面向的用戶
"sub":"any@126.com",
// 上面是JWT標準定義的一些字段,除此以外還能夠私人定義一些字段
"form_user": "fsdfds"
}
// Signature 簽名
將上面兩個對象進行base64編碼以後用.進行鏈接,而後經過HS256算法進行加密就造成了簽名,通常須要加上咱們提供的一個密匙,例如secretKey:'name_luffy'
const base64url = require('base64url')
const base64header = base64url(JSON.stringify(header));
const base64payload = base64url(JSON.stringify(payload));
const secretKey = 'name_luffy';
const signature = HS256(`${base64header}.${base64payload}`,secretKey);
// JWT
// 最後就造成了咱們所須要的JWT:
const JWT = base64header + "." + base64payload + "." + signature;
// 它長下面這個樣子:
// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
複製代碼
我從官網JWT.io拿下來的圖來展現,就是下面這個過程,說的很詳細,此外還有一些細節的東西,好比什麼形式存儲,放在頭部哪裏,客戶端要存儲在哪裏等,官網都有比較詳細的介紹,你們能夠去看看。 github
接下來要詳細的說如何使用jwt來進行先後端的身份驗證了,具體思路以下:web
yarn add express-jwt jsonwebtoken
複製代碼
以後就是在登陸環節生成token而且把token返回給前端算法
// /routes/user.js
if (user !== null) {
// 用戶登陸成功事後生成token返給前端
let token = jwt.sign(tokenObj, secretKey, {
expiresIn : 60 * 60 * 24 // 受權時效24小時
});
res.json({
success: true,
message: 'success',
token: token
});
}
複製代碼
其次,設置攔截token的中間件,包括token的驗證以及錯誤信息的返回:數據庫
// jwt.js,token中間件
const expressJwt = require("express-jwt");
const { secretKey } = require('../constant/constant');
// express-jwt中間件幫咱們自動作了token的驗證以及錯誤處理,因此通常狀況下咱們按照格式書寫就沒問題,其中unless放的就是你想要不檢驗token的api。
const jwtAuth = expressJwt({secret: secretKey}).unless({path: ["/api/user/login", "/api/user/register"]});
module.exports = jwtAuth;
複製代碼
// constant.js
// 設置了密碼鹽值以及token的secretKey
const crypto = require('crypto');
module.exports = {
MD5_SUFFIX: 'luffyZhou我是一個固定長度的鹽值',
md5: (pwd) => {
let md5 = crypto.createHash('md5');
return md5.update(pwd).digest('hex');
},
secretKey: 'luffy_1993711_26_jwttoken'
};
複製代碼
最後在路由中間件前面放上jwt中間件express
// routes/index.js
// 全部請求過來都會進行身份驗證
router.use(jwtAuth);
// 路由中間件
router.use((req, res, next) => {
// 任何路由信息都會執行這裏面的語句
console.log('this is a api request!');
// 把它交給下一個中間件,注意中間件的註冊順序是按序執行
next();
});
複製代碼
後端邏輯部分所有完成,下面是前端的實現部分。json
第1、把登錄成功以後返回的token存在客戶端,可使用localStorage也可使用cookie,我看官方推薦使用localStorage,我這邊也就用localStorage吧。 第2、每次請求把token放到header頭部Authorization字段。
// axios攔截器
// 攔截請求,給全部的請求都帶上token
axios.interceptors.request.use(request => {
const luffy_jwt_token = window.localStorage.getItem('luffy_jwt_token');
if (luffy_jwt_token) {
// 此處有坑,下方記錄
request.headers['Authorization'] =`Bearer ${luffy_jwt_token}`;
}
return request;
});
// 攔截響應,遇到token不合法則報錯
axios.interceptors.response.use(
response => {
if (response.data.token) {
console.log('token:', response.data.token);
window.localStorage.setItem('luffy_jwt_token', response.data.token);
}
return response;
},
error => {
const errRes = error.response;
if (errRes.status === 401) {
window.localStorage.removeItem('luffy_jwt_token');
swal('Auth Error!', `${errRes.data.error.message}, please login!`, 'error')
.then(() => {
history.push('/login');
});
}
return Promise.reject(error.message); // 返回接口返回的錯誤信息
});
複製代碼
能夠看到,登陸成功後,token被存放在localStorage裏而且每一次請求都會將token放在頭部Authorization字段內。若是咱們把token從localStorage清除,再次訪問就會報錯。此處有坑,在此記錄request.headers['Authorization']必須經過此種形式設置Authorization,不然後端即便收到字段也會出現問題,返回401,request.headers.Authorization或request.headers.authorization能夠設置成功,瀏覽器查看也沒有任何問題,可是在後端會報401而且後端一概只能拿到小寫的,也就是res.headers.authorization,後端用大寫獲取會報undefined.
很是簡單的一個小栗子,也沒什麼技術含量的文章,就當寫着玩練習文筆了。代碼沒有另外放在哪?就在express-react-scaffold上增長的登陸註冊和token認證。能夠經過/login來訪問登錄部分邏輯以及token驗證功能。 O(∩_∩)O哈哈~