Node.js 應用:Koa2 使用 JWT 進行鑑權
前言
在前後端分離的開發中,透過 Restful API 進行資料互動時,如果沒有對 API 進行保護,那麼別人就可以很容易地獲取並呼叫這些 API 進行操作。那麼伺服器端要如何進行鑑權呢?
Json Web Token 簡稱為 JWT,它定義了一種用於簡潔、自包含的用於通訊雙方之間以 JSON 物件的形式安全傳遞資訊的方法。JWT 可以使用 HMAC 演算法或者是 RSA 的公鑰金鑰對進行簽名。
說得好像跟真的一樣,那麼到底要怎麼進行認證呢?
首先使用者登入時,輸入使用者名稱和密碼後請求伺服器登入介面,伺服器驗證使用者名稱密碼正確後,生成token並返回給前端,前端儲存token,並在後面的請求中把token帶在請求頭中傳給伺服器,伺服器驗證token有效,返回正確資料。
既然伺服器端使用 Koa2 框架進行開發,除了要使用到 jsonwebtoken 庫之外,還要使用一個 koa-jwt 中介軟體,該中介軟體針對 Koa 對 jsonwebtoken 進行了封裝,使用起來更加方便。下面就來看看是如何使用的。
生成token
這裡註冊了個 /login
的路由,用於使用者登入時獲取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: '引數不合法' } } 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: '使用者名稱或密碼錯誤' } } });module.exports = router;
在驗證了使用者名稱密碼正確之後,呼叫 jsonwebtoken 的 sign() 方法來生成token,接收三個引數,第一個是載荷,用於編碼後儲存在 token 中的資料,也是驗證 token 後可以拿到的資料;第二個是金鑰,自己定義的,驗證的時候也是要相同的金鑰才能解碼;第三個是options,可以設定 token 的過期時間。
獲取token
接下來就是前端獲取 token,這裡是在 vue.js 中使用 axios 進行請求,請求成功之後拿到 token 儲存到 localStorage 中。這裡登入成功後,還把當前時間存了起來,除了判斷 token 是否存在之外,還可以再簡單的判斷一下當前 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); } }) }
然後請求伺服器端API的時候,把 token 帶在請求頭中傳給伺服器進行驗證。每次請求都要獲取 localStorage 中的 token,這樣很麻煩,這裡使用了 axios 的請求攔截器,對每次請求都進行了取 token 放到 headers 中的操作。
axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); config.headers.common['Authorization'] = 'Bearer ' + token; return config; })
驗證token
透過 koa-jwt 中介軟體來進行驗證,用法也非常簡單
const koa = require('koa');const koajwt = require('koa-jwt');const app = new koa();// 錯誤處理app.use((ctx, next) => { return next().catch((err) => { if(err.status === 401){ ctx.status = 401; ctx.body = 'Protected resource, use Authorization header to get accessn'; }else{ throw err; } }) }) app.use(koajwt({ secret: 'my_token'}).unless({ path: [//user/login/] }));
透過 app.use 來呼叫該中介軟體,並傳入金鑰 {secret: 'my_token'}
,unless 可以指定哪些 URL 不需要進行 token 驗證。token 驗證失敗的時候會丟擲401錯誤,因此需要新增錯誤處理,而且要放在 app.use(koajwt()) 之前,否則不執行。
如果請求時沒有token或者token過期,則會返回401。
解析koa-jwt
我們上面使用 jsonwebtoken 的 sign() 方法來生成 token 的,那麼 koa-jwt 做了些什麼幫我們來驗證 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"'); } };
在 auth-header.js 中,判斷請求頭中是否帶了 authorization,如果有,將 token 從 authorization 中分離出來。如果沒有 authorization,則代表了客戶端沒有傳 token 到伺服器,這時候就丟擲 401 錯誤狀態。
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() 方法進行驗證返回結果。jsonwebtoken 的 sign() 方法來生成 token 的,而 verify() 方法則是用來認證和解析 token。如果 token 無效,則會在此方法被驗證出來。
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 中,呼叫 verify.js 的方法進行驗證並解析 token,拿到上面進行 sign() 的資料 {name: result.name, _id: result._id}
,並賦值給 ctx.state.user
,在控制器中便可以直接透過 ctx.state.user
拿到 name
和 _id
。
安全性
如果 JWT 的加密金鑰洩露的話,那麼就可以透過金鑰生成 token,隨意的請求 API 了。因此金鑰絕對不能存在前端程式碼中,不然很容易就能被找到。
在 HTTP 請求中,token 放在 header 中,中間者很容易可以透過抓包工具抓取到 header 裡的資料。而 HTTPS 即使能被抓包,但是它是加密傳輸的,所以也拿不到 token,就會相對安全了。
總結
這上面就是 jwt 基本的流程,這或許不是最完美的,但在大多數登入中使用已經足夠了。
上面的程式碼可能不夠具體,這裡使用 Koa + mongoose + vue.js 實現的一個例子 : ,可以做為參考。
原文出處:https://www.cnblogs.com/linxin/p/9491342.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2524/viewspace-2811990/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 理解JWT鑑權的應用場景及使用建議JWT
- .net core jwt 鑑權JWT
- ThinkJS JWT 鑑權實踐JSJWT
- Egg實現JWT鑑權JWT
- SpringCloud 2020.0.4 系列之 JWT使用者鑑權SpringGCCloudJWT
- 雲原生最佳實踐系列 6:MSE 雲原生閘道器使用 JWT 進行認證鑑權JWT
- jwt與session的登入鑑權JWTSession
- AspNetCore 成長雜記(一):JWT授權鑑權之生成JWT(其二)NetCoreJWT
- AspNetCore 成長雜記(一):JWT授權鑑權之生成JWT(其一)NetCoreJWT
- 使用 Dynatrace 對 Node.js 應用的效能資料進行分析Node.js
- 如何在SpringBoot中整合JWT(JSON Web Token)鑑權Spring BootJWTJSONWeb
- JWT登入鑑權:避免在使用者操作的過程中JWT到期跳轉登入JWT
- 介面鑑權之sign簽名校驗與JWT驗證JWT
- ThinkPHP5 使用 JWT 進行加密PHPJWT加密
- 升級到 Pulsar3.0 後深入瞭解 JWT 鑑權JWT
- JWT+Interceptor實現無狀態登入和鑑權JWT
- Spring Boot使用JWT進行token驗證Spring BootJWT
- .net core中使用jwt進行認證JWT
- 使用WebApi+Vue3從0到1搭建《許可權管理系統》:二、搭建JWT系統鑑權WebAPIVueJWT
- 關於 Node.js 應用裡使用 winston 進行日誌記錄的最佳實踐Node.js
- API 鑑權新姿勢 – 簽名鑑權API
- 使用 NestJS 開發 Node.js 應用Node.js
- 使用 HTTP/2 加速 Node.js 應用HTTPNode.js
- drf中jwt應用JWT
- 如何使用Nginx對Artifactory進行http應用NginxHTTP
- 如何在資料庫中進行RBAC許可權應用資料庫
- 使用VisualVM進行Java應用的效能測量LVMJava
- 使用火焰圖進行Java應用效能分析Java
- 使用位運算來做使用者鑑權
- 《Node.js設計模式》使用流進行編碼Node.js設計模式
- 教你使用Docker容器化Node.js應用程式DockerNode.js
- Nuxt 實現使用者鑑權登陸UX
- 我不應該用JWT的!JWT
- Node.js的Koa實現JWT使用者認證Node.jsJWT
- node.js應用RedisNode.jsRedis
- Vue 前端應用進行身份認證許可權控制的一種方法Vue前端
- 使用 JWT 身份驗證保護你的 Spring Boot 應用JWTSpring Boot
- SpringBoot--- 使用SpringSecurity進行授權認證Spring BootGse