摘要:一前言,是為了在網(wǎng)絡(luò)環(huán)境間傳遞聲明而執(zhí)行的一種基于的開(kāi)放標(biāo)準(zhǔn)。用戶(hù)簽發(fā)添加中間件校驗(yàn)判斷是否可用獲取解密,獲取用戶(hù)名和認(rèn)證失敗中添加處理此處在開(kāi)發(fā)時(shí)需要過(guò)濾掉登錄接口,否則會(huì)導(dǎo)致驗(yàn)證永遠(yuǎn)失敗。前端處理前端開(kāi)發(fā)使用的是,發(fā)送請(qǐng)求使用的是。
一、前言
JWT(JSON Web Token),是為了在網(wǎng)絡(luò)環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開(kāi)放標(biāo)準(zhǔn)(RFC 7519)。
JWT不是一個(gè)新鮮的東西,網(wǎng)上相關(guān)的介紹已經(jīng)非常多了。不是很了解的可以在網(wǎng)上搜索一下相關(guān)信息。
同步到sau交流學(xué)習(xí)社區(qū):https://www.mwcxs.top/page/45...
Talk is cheap. Show me the code.
三、工作流程JWT本質(zhì)來(lái)說(shuō)是一個(gè)token。在前后端進(jìn)行HTTP連接時(shí)來(lái)進(jìn)行相應(yīng)的驗(yàn)證。
四、實(shí)現(xiàn)過(guò)程 1. 登錄成功生成JWT博客的后臺(tái)管理系統(tǒng)發(fā)起登錄請(qǐng)求,后端服務(wù)器校驗(yàn)成功之后,生成JWT認(rèn)證信息;
前端接收到JWT后進(jìn)行存儲(chǔ);
前端在之后每次接口調(diào)用發(fā)起HTTP請(qǐng)求時(shí),會(huì)將JWT放到HTTP的headers參數(shù)里的authorization中一起發(fā)送給后端;
后端接收到請(qǐng)求時(shí)會(huì)根據(jù)JWT中的信息來(lái)校驗(yàn)當(dāng)前發(fā)起HTTP請(qǐng)求的用戶(hù)是否是具有訪問(wèn)權(quán)限的,有訪問(wèn)權(quán)限時(shí)則交給服務(wù)器繼續(xù)處理,沒(méi)有時(shí)則直接返回401錯(cuò)誤。
說(shuō)明:以下代碼只保留了核心代碼,詳細(xì)代碼可在對(duì)應(yīng)文件中查看,下同。
// /server/api/admin/admin.controller.js const jwt = require("jsonwebtoken"); const config = require("../../config/config"); exports.login = async(ctx) => { // ... if (hashedPassword === hashPassword) { // ... // 用戶(hù)token const userToken = { name: userName, id: results[0].id }; // 簽發(fā)token const token = jwt.sign(userToken, config.tokenSecret, { expiresIn: "2h" }); // ... } // ... }2. 添加中間件校驗(yàn)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,獲取用戶(hù)名和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: "認(rèn)證失敗" }; } else { err.status = 404; ctx.body = { success: 0, message: "404" }; } } } }3. Koa.js中添加JWT處理
此處在開(kāi)發(fā)時(shí)需要過(guò)濾掉登錄接口(login),否則會(huì)導(dǎo)致JWT驗(yàn)證永遠(yuǎn)失敗。
// /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.前端處理
前端開(kāi)發(fā)使用的是Vue.js,發(fā)送HTTP請(qǐng)求使用的是axios。
(1) 登錄成功之后將JWT存儲(chǔ)到localStorage中(可根據(jù)個(gè)人需要存儲(chǔ),我個(gè)人是比較喜歡存儲(chǔ)到localStorage中)。
methods: { login: async function () { // ... let res = await api.login(this.userName, this.password); if (res.success === 1) { this.errMsg = ""; localStorage.setItem("SONG_EAGLE_TOKEN", res.token); this.$router.push({ path: "/postlist" }); } else { this.errMsg = res.message; } } }
(2) Vue.js的router(路由)跳轉(zhuǎn)前校驗(yàn)JWT是否存在,不存在則跳轉(zhuǎn)到登錄頁(yè)面。
// /src/router/index.js router.beforeEach((to, from, next) => { if (to.meta.requireAuth) { const token = localStorage.getItem("SONG_EAGLE_TOKEN"); if (token && token !== "null") { next(); } else { next("/login"); } } else { next(); } });
(3) axios攔截器中給HTTP統(tǒng)一添加Authorization信息
// /src/api/config.js axios.interceptors.request.use( config => { const token = localStorage.getItem("SONG_EAGLE_TOKEN"); if (token) { // Bearer是JWT的認(rèn)證頭部信息 config.headers.common["Authorization"] = "Bearer " + token; } return config; }, error => { return Promise.reject(error); } );
(4)axios攔截器在接收到HTTP返回時(shí)統(tǒng)一處理返回狀態(tài)
// /src/main.js axios.interceptors.response.use( response => { return response; }, error => { if (error.response.status === 401) { Vue.prototype.$msgBox.showMsgBox({ title: "錯(cuò)誤提示", content: "您的登錄信息已失效,請(qǐng)重新登錄", isShowCancelBtn: false }).then((val) => { router.push("/login"); }).catch(() => { console.log("cancel"); }); } else { Vue.prototype.$message.showMessage({ type: "error", content: "系統(tǒng)出現(xiàn)錯(cuò)誤" }); } return Promise.reject(error); } );五、總結(jié)
這個(gè)基本上就是JWT的流程。當(dāng)然單純的JWT并不是說(shuō)絕對(duì)安全的,不過(guò)對(duì)于一個(gè)個(gè)人博客系統(tǒng)的認(rèn)證來(lái)說(shuō)還是足夠的。
最后打個(gè)小廣告。目前正在開(kāi)發(fā)新版的個(gè)人博客中,包括兩部分:
【前端】(https://github.com/saucxs/son...
【后端】(https://github.com/saucxs/son...
都已在GitHub上開(kāi)源,目前在逐步完善功能中。歡迎感興趣的同學(xué)fork和star。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99358.html
摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥(niǎo)雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門(mén),久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥(niǎo)雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門(mén),久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...
閱讀 3059·2023-04-26 03:01
閱讀 3547·2023-04-25 19:54
閱讀 1600·2021-11-24 09:39
閱讀 1382·2021-11-19 09:40
閱讀 4261·2021-10-14 09:43
閱讀 2098·2019-08-30 15:56
閱讀 1504·2019-08-30 13:52
閱讀 1669·2019-08-29 13:05