前言
JWT(JSON Web Token),是爲了在網絡環境間傳遞聲明而執行的一種基於JSON的開放標準(RFC 7519)。javascript
更多的介紹和說明,以及各類原理,我在此就很少贅訴了。JWT不是一個新鮮的東西,網上相關的介紹已經很是多了。不是很瞭解的能夠在網上搜索一下相關信息。前端
源碼
Talk is cheap. Show me the code.java
工做流程
JWT本質來講是一個token。在先後端進行HTTP鏈接時來進行相應的驗證。ios
- 博客的後臺管理系統發起登陸請求,後端服務器校驗成功以後,生成JWT認證信息;
- 前端接收到JWT後進行存儲;
- 前端在以後每次接口調用發起HTTP請求時,會將JWT放到HTTP的headers參數裏的authorization中一塊兒發送給後端;
- 後端接收到請求時會根據JWT中的信息來校驗當前發起HTTP請求的用戶是不是具備訪問權限的,有訪問權限時則交給服務器繼續處理,沒有時則直接返回401錯誤。
實現過程
1. 登陸成功生成JWT
說明:如下代碼只保留了核心代碼,詳細代碼可在對應文件中查看,下同。git
// /server/api/admin/admin.controller.js const jwt = require('jsonwebtoken'); const config = require('../../config/config'); exports.login = async(ctx) => { // ... if (hashedPassword === hashPassword) { // ... // 用戶token const userToken = { name: userName, id: results[0].id }; // 簽發token const token = jwt.sign(userToken, config.tokenSecret, { expiresIn: '2h' }); // ... } // ... }
2. 添加中間件校驗JWT
// /server/middlreware/tokenError.js const jwt = require('jsonwebtoken'); const config = require('../config/config'); const util = require('util'); const verify = util.promisify(jwt.verify); /** * 判斷token是否可用 */ module.exports = function () { return async function (ctx, next) { try { // 獲取jwt const token = ctx.header.authorization; if (token) { try { // 解密payload,獲取用戶名和ID let payload = await verify(token.split(' ')[1], config.tokenSecret); ctx.user = { name: payload.name, id: payload.id }; } catch (err) { console.log('token verify fail: ', err) } } await next(); } catch (err) { if (err.status === 401) { ctx.status = 401; ctx.body = { success: 0, message: '認證失敗' }; } else { err.status = 404; ctx.body = { success: 0, message: '404' }; } } } }
3. Koa.js中添加JWT處理
此處在開發時須要過濾掉登陸接口(login),不然會致使JWT驗證永遠失敗。github
// /server/config/koa.js const jwt = require('koa-jwt'); const tokenError = require('../middlreware/tokenError'); // ... const app = new Koa(); app.use(tokenError()); app.use(bodyParser()); app.use(koaJson()); app.use(resource(path.join(config.root, config.appPath))); app.use(jwt({ secret: config.tokenSecret }).unless({ path: [/^\/backapi\/admin\/login/, /^\/blogapi\//] })); module.exports = app;
4.前端處理
前端開發使用的是Vue.js,發送HTTP請求使用的是axios。web
-
登陸成功以後將JWT存儲到localStorage中(可根據我的須要存儲,我我的是比較喜歡存儲到localStorage中)。json
methods: { login: async function () { // ... let res = await api.login(this.userName, this.password); if (res.success === 1) { this.errMsg = ''; localStorage.setItem('DON_BLOG_TOKEN', res.token); this.$router.push({ path: '/postlist' }); } else { this.errMsg = res.message; } } }
-
Vue.js的router(路由)跳轉前校驗JWT是否存在,不存在則跳轉到登陸頁面。axios
// /src/router/index.js router.beforeEach((to, from, next) => { if (to.meta.requireAuth) { const token = localStorage.getItem('DON_BLOG_TOKEN'); if (token && token !== 'null') { next(); } else { next('/login'); } } else { next(); } });
-
axios攔截器中給HTTP統一添加Authorization信息後端
// /src/fetch/fetch.js axios.interceptors.request.use( config => { const token = localStorage.getItem('DON_BLOG_TOKEN'); if (token) { // Bearer是JWT的認證頭部信息 config.headers.common['Authorization'] = 'Bearer ' + token; } return config; }, error => { return Promise.reject(error); } );
-
axios攔截器在接收到HTTP返回時統一處理返回狀態
// /src/main.js axios.interceptors.response.use( response => { return response; }, error => { if (error.response.status === 401) { Vue.prototype.$msgBox.showMsgBox({ title: '錯誤提示', content: '您的登陸信息已失效,請從新登陸', isShowCancelBtn: false }).then((val) => { router.push('/login'); }).catch(() => { console.log('cancel'); }); } else { Vue.prototype.$message.showMessage({ type: 'error', content: '系統出現錯誤' }); } return Promise.reject(error); } );
總結
到這裏整個流程就走完了。固然單純的JWT並非說絕對安全的,不過對於一個我的博客系統的認證來講仍是足夠的。
最後打個小廣告。目前正在開發新版的我的博客中,包括前端和後端兩部分,都已在GitHub上開源,目前在逐步完善功能中。歡迎感興趣的同窗fork和star。