Node.js Koa2使用JWT進(jìn)行鑒權(quán)的方法示例
前言
在前后端分離的開發(fā)中,通過 Restful API 進(jìn)行數(shù)據(jù)交互時(shí),如果沒有對(duì) API 進(jìn)行保護(hù),那么別人就可以很容易地獲取并調(diào)用這些 API 進(jìn)行操作。那么服務(wù)器端要如何進(jìn)行鑒權(quán)呢?
Json Web Token 簡(jiǎn)稱為 JWT,它定義了一種用于簡(jiǎn)潔、自包含的用于通信雙方之間以 JSON 對(duì)象的形式安全傳遞信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公鑰密鑰對(duì)進(jìn)行簽名。
說得好像跟真的一樣,那么到底要怎么進(jìn)行認(rèn)證呢?
首先用戶登錄時(shí),輸入用戶名和密碼后請(qǐng)求服務(wù)器登錄接口,服務(wù)器驗(yàn)證用戶名密碼正確后,生成token并返回給前端,前端存儲(chǔ)token,并在后面的請(qǐng)求中把token帶在請(qǐng)求頭中傳給服務(wù)器,服務(wù)器驗(yàn)證token有效,返回正確數(shù)據(jù)。
既然服務(wù)器端使用 Koa2 框架進(jìn)行開發(fā),除了要使用到 jsonwebtoken 庫之外,還要使用一個(gè) koa-jwt 中間件,該中間件針對(duì) Koa 對(duì) jsonwebtoken 進(jìn)行了封裝,使用起來更加方便。下面就來看看是如何使用的。
生成token
這里注冊(cè)了個(gè) /login 的路由,用于用戶登錄時(shí)獲取token。
const router = require('koa-router')(); const jwt = require('jsonwebtoken'); const userModel = require('../models/userModel.js'); router.post('/login', async (ctx) => { const data = ctx.request.body; if(!data.name || !data.password){ return ctx.body = { code: '000002', data: null, msg: '參數(shù)不合法' } } const result = await userModel.findOne({ name: data.name, password: data.password }) if(result !== null){ const token = jwt.sign({ name: result.name, _id: result._id }, 'my_token', { expiresIn: '2h' }); return ctx.body = { code: '000001', data: token, msg: '登錄成功' } }else{ return ctx.body = { code: '000002', data: null, msg: '用戶名或密碼錯(cuò)誤' } } }); module.exports = router;
在驗(yàn)證了用戶名密碼正確之后,調(diào)用 jsonwebtoken 的 sign() 方法來生成token,接收三個(gè)參數(shù),第一個(gè)是載荷,用于編碼后存儲(chǔ)在 token 中的數(shù)據(jù),也是驗(yàn)證 token 后可以拿到的數(shù)據(jù);第二個(gè)是密鑰,自己定義的,驗(yàn)證的時(shí)候也是要相同的密鑰才能解碼;第三個(gè)是options,可以設(shè)置 token 的過期時(shí)間。
獲取token
接下來就是前端獲取 token,這里是在 vue.js 中使用 axios 進(jìn)行請(qǐng)求,請(qǐng)求成功之后拿到 token 保存到 localStorage 中。這里登錄成功后,還把當(dāng)前時(shí)間存了起來,除了判斷 token 是否存在之外,還可以再簡(jiǎn)單的判斷一下當(dāng)前 token 是否過期,如果過期,則跳登錄頁面
submit(){ axios.post('/login', { name: this.username, password: this.password }).then(res => { if(res.code === '000001'){ localStorage.setItem('token', res.data); localStorage.setItem('token_exp', new Date().getTime()); this.$router.push('/'); }else{ alert(res.msg); } }) }
然后請(qǐng)求服務(wù)器端API的時(shí)候,把 token 帶在請(qǐng)求頭中傳給服務(wù)器進(jìn)行驗(yàn)證。每次請(qǐng)求都要獲取 localStorage 中的 token,這樣很麻煩,這里使用了 axios 的請(qǐng)求攔截器,對(duì)每次請(qǐng)求都進(jìn)行了取 token 放到 headers 中的操作。
axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); config.headers.common['Authorization'] = 'Bearer ' + token; return config; })
驗(yàn)證token
通過 koa-jwt 中間件來進(jìn)行驗(yàn)證,用法也非常簡(jiǎn)單
const koa = require('koa'); const koajwt = require('koa-jwt'); const app = new koa(); // 錯(cuò)誤處理 app.use((ctx, next) => { return next().catch((err) => { if(err.status === 401){ ctx.status = 401; ctx.body = 'Protected resource, use Authorization header to get access\n'; }else{ throw err; } }) }) app.use(koajwt({ secret: 'my_token' }).unless({ path: [/\/user\/login/] }));
通過 app.use 來調(diào)用該中間件,并傳入密鑰 {secret: 'my_token'} ,unless 可以指定哪些 URL 不需要進(jìn)行 token 驗(yàn)證。token 驗(yàn)證失敗的時(shí)候會(huì)拋出401錯(cuò)誤,因此需要添加錯(cuò)誤處理,而且要放在 app.use(koajwt()) 之前,否則不執(zhí)行。
如果請(qǐng)求時(shí)沒有token或者token過期,則會(huì)返回401。
解析koa-jwt
我們上面使用 jsonwebtoken 的 sign() 方法來生成 token 的,那么 koa-jwt 做了些什么幫我們來驗(yàn)證 token。
resolvers/auth-header.js module.exports = function resolveAuthorizationHeader(ctx, opts) { if (!ctx.header || !ctx.header.authorization) { return; } const parts = ctx.header.authorization.split(' '); if (parts.length === 2) { const scheme = parts[0]; const credentials = parts[1]; if (/^Bearer$/i.test(scheme)) { return credentials; } } if (!opts.passthrough) { ctx.throw(401, 'Bad Authorization header format. Format is "Authorization: Bearer <token>"'); } };
在 auth-header.js 中,判斷請(qǐng)求頭中是否帶了 authorization,如果有,將 token 從 authorization 中分離出來。如果沒有 authorization,則代表了客戶端沒有傳 token 到服務(wù)器,這時(shí)候就拋出 401 錯(cuò)誤狀態(tài)。
verify.js
const jwt = require('jsonwebtoken'); module.exports = (...args) => { return new Promise((resolve, reject) => { jwt.verify(...args, (error, decoded) => { error ? reject(error) : resolve(decoded); }); }); };
在 verify.js 中,使用 jsonwebtoken 提供的 verify() 方法進(jìn)行驗(yàn)證返回結(jié)果。jsonwebtoken 的 sign() 方法來生成 token 的,而 verify() 方法則是用來認(rèn)證和解析 token。如果 token 無效,則會(huì)在此方法被驗(yàn)證出來。
index.js
const decodedToken = await verify(token, secret, opts); if (isRevoked) { const tokenRevoked = await isRevoked(ctx, decodedToken, token); if (tokenRevoked) { throw new Error('Token revoked'); } } ctx.state[key] = decodedToken; // 這里的key = 'user' if (tokenKey) { ctx.state[tokenKey] = token; }
在 index.js 中,調(diào)用 verify.js 的方法進(jìn)行驗(yàn)證并解析 token,拿到上面進(jìn)行 sign() 的數(shù)據(jù) {name: result.name, _id: result._id} ,并賦值給 ctx.state.user ,在控制器中便可以直接通過 ctx.state.user 拿到 name 和 _id 。
安全性
- 如果 JWT 的加密密鑰泄露的話,那么就可以通過密鑰生成 token,隨意的請(qǐng)求 API 了。因此密鑰絕對(duì)不能存在前端代碼中,不然很容易就能被找到。
- 在 HTTP 請(qǐng)求中,token 放在 header 中,中間者很容易可以通過抓包工具抓取到 header 里的數(shù)據(jù)。而 HTTPS 即使能被抓包,但是它是加密傳輸?shù)?,所以也拿不?token,就會(huì)相對(duì)安全了。
總結(jié)
這上面就是 jwt 基本的流程,這或許不是最完美的,但在大多數(shù)登錄中使用已經(jīng)足夠了。
上面的代碼可能不夠具體,這里使用 Koa + mongoose + vue.js 實(shí)現(xiàn)的一個(gè)例子 :jwt-demo ,可以做為參考。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Node.js自動(dòng)生成API文檔的實(shí)現(xiàn)
本文主要介紹了Node.js自動(dòng)生成API文檔,包含基于swagger-jsdoc+swagger-ui-express快速實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03Node.js版本升級(jí)如何修改模塊默認(rèn)的保存位置
這篇文章主要給大家介紹了關(guān)于Node.js版本升級(jí)如何修改模塊默認(rèn)的保存位置,文中通過代碼以及圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用node.js具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-05-05Nodejs腳本快速導(dǎo)出MySQL數(shù)據(jù)庫數(shù)據(jù)
在數(shù)據(jù)庫管理和數(shù)據(jù)遷移的過程中,常常需要將數(shù)據(jù)庫中的表數(shù)據(jù)和結(jié)構(gòu)進(jìn)行導(dǎo)出,本文將使用Node.js編寫一個(gè)腳本實(shí)現(xiàn)快速從MySQL數(shù)據(jù)庫中導(dǎo)出所有表的數(shù)據(jù)和結(jié)構(gòu),并保存為單獨(dú)的SQL文件,需要的可以參考下2024-10-10Node.js實(shí)現(xiàn)鏈?zhǔn)交卣{(diào)
這篇文章介紹了Node.js實(shí)現(xiàn)鏈?zhǔn)交卣{(diào)的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07node實(shí)現(xiàn)登錄圖片驗(yàn)證碼的示例代碼
這篇文章主要介紹了node實(shí)現(xiàn)登錄圖片驗(yàn)證碼的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04Nodejs 構(gòu)建Cluster集群多線程Worker threads
這篇文章主要為大家介紹了Nodejs 構(gòu)建Cluster集群多線程Worker threads示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10NodeJs環(huán)境安裝與配置的實(shí)現(xiàn)步驟
本文主要介紹了NodeJs環(huán)境安裝與配置,包括配置環(huán)境和配置國內(nèi)鏡像,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-01-01Node.js報(bào)錯(cuò)信息Error:?Cannot?find?module?'XXX'問題及解
這篇文章主要介紹了Node.js報(bào)錯(cuò)信息Error:?Cannot?find?module?'XXX'問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10