平面設(shè)計比較好的網(wǎng)站廣告投放平臺系統(tǒng)
在現(xiàn)代web
應(yīng)用中,用戶身份認(rèn)證是非常重要且必不可少的一環(huán)。而使用Node.js
和Express
框架,可以方便地實現(xiàn)用戶身份認(rèn)證。而在這個過程中,jsonwebtoken
這個基于JWT
協(xié)議的模塊可以幫助我們實現(xiàn)安全且可靠的身份認(rèn)證機制,可以讓我們輕松地生成、解析和驗證JWT
。本文主要介紹如何在Node.js
的Express
框架中使用jsonwebtoken
模塊來實現(xiàn)用戶身份認(rèn)證。
一、什么是jwt
jwt介紹:https://restfulapi.cn/jwt
JWT
(JSON Web Token
)是一種基于JSON
的開放標(biāo)準(zhǔn),用于在網(wǎng)絡(luò)上以安全方式傳輸聲明。JWT
通常用于身份驗證和授權(quán)。它是一種可自包含的身份認(rèn)證機制,由三部分組成:頭部、載荷和簽名。JWT
的頭部和載荷都是使用Base64
編碼后的JSON
字符串,簽名是由頭部和載荷使用密鑰進(jìn)行哈希計算得到的字符串。JWT
可以在客戶端和服務(wù)器之間傳輸,并且可以在傳輸過程中防止數(shù)據(jù)被篡改。由于它是無狀態(tài)的,因此可以簡化Web
應(yīng)用程序的開發(fā)和維護(hù)。
二、jsonwebtoken基本使用
jsonwebtoken
是一個Node.js
庫,用于創(chuàng)建、解碼和驗證JWT
令牌(JSON Web Tokens
)。在Web
應(yīng)用中,這些令牌通常用于身份驗證和授權(quán)。
以下是如何在Node.js
應(yīng)用中使用jsonwebtoken
的基本步驟。
1. 安裝jsonwebtoken
首先,你需要使用npm或yarn將jsonwebtoken包添加到你的項目中:
npm install jsonwebtoken --save
或者
yarn add jsonwebtoken
2. 導(dǎo)入使用
然后,在你的代碼中導(dǎo)入jsonwebtoken:
const jwt = require('jsonwebtoken');
3. 創(chuàng)建token
創(chuàng)建一個令牌:
let token = jwt.sign({ foo: 'bar' }, 'shhhhh');
在上面的代碼中,我們使用jwt.sign
方法創(chuàng)建了一個新的令牌。
這個方法需要兩個參數(shù):
- 一個包含你的
payload
的對象(這個對象中的數(shù)據(jù)會被編碼到生成的令牌中)比如:userInfo
, - 用來簽名令牌的秘密字符串。
4. 驗證token
驗證一個令牌:
jwt.verify(token, 'shhhhh', (err, decoded) => {if (err) {// 處理錯誤} else {// 使用解碼后的令牌數(shù)據(jù)}
});
使用jwt.verify
方法可以解碼并驗證一個令牌。
該方法需要三個參數(shù):
- 解碼的令牌,
- 用于簽名的相同的秘密字符串,
- 操作完成時被調(diào)用的回調(diào)函數(shù)。
注意:
-
所有的令牌都應(yīng)該只通過
HTTPS
發(fā)送,以防止被攔截。 -
秘密字符串應(yīng)該在你的應(yīng)用中保持私密,不能讓其他人知道。
-
任何存儲在令牌中的信息都可以被任何擁有秘密的人解碼。因此,你永遠(yuǎn)不應(yīng)該在令牌中存儲敏感信息(如密碼或銀行賬戶信息)。
三、在express中應(yīng)用示例
在實際項目中,我們可以使用jsonwebtoken
模塊來實現(xiàn)身份認(rèn)證。jsonwebtoken
是基于JSON Web Tokens(JWT)
協(xié)議的實現(xiàn)模塊,可以用于生成、解析和驗證JWT
。JWT
是一種基于JSON
的開放標(biāo)準(zhǔn)(RFC 7519)
,用于在網(wǎng)絡(luò)上安全地傳輸聲明。
以下是一個使用express
和jsonwebtoken
實現(xiàn)身份認(rèn)證的示例:
const express = require('express');
const jwt = require('jsonwebtoken');const app = express();// 定義一個中間件函數(shù),用于身份認(rèn)證
function verifyToken(req, res, next) {// 從請求頭中獲取tokenconst token = req.headers['authorization'];if (!token) {return res.status(401).send({auth: false,message: 'No token provided.'});}// 驗證token是否有效jwt.verify(token, process.env.SECRET_KEY, (err, decoded) => {if (err) {return res.status(500).send({auth: false,message: 'Failed to authenticate token.'});}// 將解碼的用戶信息存儲到請求對象中req.userId = decoded.id;next();});
}// 定義一個路由,需要身份認(rèn)證
app.get('/api/protected', verifyToken, (req, res, next) => {// 返回受保護(hù)的數(shù)據(jù)res.status(200).send({message: 'Access granted.'});
});// 定義一個路由,用于登錄,返回token
app.post('/api/login', (req, res, next) => {// 從數(shù)據(jù)庫中獲取用戶信息const user = { id: 123 };// 生成tokenconst token = jwt.sign({ id: user.id }, process.env.SECRET_KEY, {expiresIn: 60 * 60 // token有效期為1小時});// 返回tokenres.status(200).send({auth: true,token: token});
});// 啟動服務(wù)器,監(jiān)聽端口
app.listen(3000, () => {console.log('Server started on port 3000');
});
在上述示例中,我們定義了一個中間件函數(shù)verifyToken
用于身份認(rèn)證。該函數(shù)從請求頭中獲取token
,并通過jsonwebtoken的verify
方法驗證token
是否有效。如果驗證通過,則將用戶信息存儲到請求對象中,以便后續(xù)路由函數(shù)使用。
我們還定義了兩個路由函數(shù),/api/protected
和/api/login
。/api/protected
需要身份認(rèn)證才能訪問,而/api/login
用于登錄,并返回生成的token
。
在實際項目中,我們應(yīng)該將SECRET_KEY
等敏感信息放在環(huán)境變量中,以提高安全性。此外,我們還可以使用jsonwebtoken
提供的其他功能,如驗證token
的簽名算法、payload
和header
等信息。
四、jwt邏輯抽離
在上面的示例中我們用jwt實現(xiàn)了身份認(rèn)證,但是這只是在一個接口中認(rèn)證,如果接口多了呢,每次都要將這個認(rèn)證的中間件加上嗎?這樣先不說開發(fā)實現(xiàn)的累不累,冗余不,單從維護(hù)層面來講,也是很不合理。所以我們應(yīng)該將認(rèn)證的邏輯抽離出來,以便于接口調(diào)用和維護(hù)。具體步驟如下:
1. 新建jwt.js文件
這個文件中就封裝三個函數(shù)
- token的生成
- token的認(rèn)證
- 排除哪些接口不需要認(rèn)證就可以訪問,比如
/login
,
// jwt.js
const jwt = require("jsonwebtoken");
const { promisify } = require("util");
const { uuid } = require("../config/config.default");
const tojwt = promisify(jwt.sign);
const verfiy = promisify(jwt.verify);// 生成token
module.exports.createToken = async (userinfo) => {var token = await tojwt({ userinfo }, uuid, {expiresIn: 60 * 60 * 24,});return token;
};// jwt認(rèn)證的中間件
const jwtAuthMiddleware = async (req, res, next) => {var token = req.headers.authorization;token = token ? token.split("Bearer ")[1] : null;if (!token) {return res.status(402).json({ error: "請傳入token" });}if (token) {try {let userinfo = await verfiy(token, uuid);req.user = userinfo;next();} catch (error) {res.status("402").json({ error: "無效的token" });}} else {next();}
};// 承認(rèn)的url排除列表
const jwtAuthExcluedList = ['/api/login', '/api/register'];
// 檢查排除列表的中間件
module.exports.jwtAuthExclued = (req, res, next) => {// 檢查請求 URL 是否在排除 jwtAuth 的列表里面if (jwtAuthExcluedList.includes(req.path)){next(); // 在列表里,跳過后續(xù)中間件} else { jwtAuthMiddleware(req, res, next); // 不在列表里,就調(diào)用jwt中間件進(jìn)行身份認(rèn)證}
};
這樣封裝以后,我們只需要在訪問路由之前,添加使用jwtAuthExclued
的中間件就可以實現(xiàn)對接口的token
認(rèn)證,在login
接口中調(diào)用createToken
生成token
可以了。
2. 在登錄接口中生成token
比如:
- loginController
// login
// 用戶登錄
exports.login = async (req, res) => {// 客戶端數(shù)據(jù)驗證// 鏈接數(shù)據(jù)庫查詢var dbBack = await User.findOne(req.body)if (!dbBack) {return res.status(402).json({ error: "郵箱或者密碼不正確" })}dbBack = dbBack.toJSON()dbBack.token = await createToken(dbBack)res.status(200).json(dbBack)
}
3. 添加全局認(rèn)證中間件
- app.js
const express = require("express");
const app = express();
const { jwtAuthExclued } = require("./util/jwt");
const router = require("./router");// 添加排除jwt中間件
app.use(jwtAuthExclued);
// 添加路由中間件
app.use('/api', router);
五、jwt和session對比
下面是JWT
和Session
的對比表格:
對比因素 | JWT | Session |
---|---|---|
存儲 | 存儲在客戶端,不需要服務(wù)器保持會話狀態(tài)。 | 存儲在服務(wù)器,需要服務(wù)器維護(hù)會話信息。 |
安全性 | 加密較嚴(yán)密,但如果token被竊取,攻擊者可以任意使用。 | 如果sessionID被竊取,攻擊者可以冒充用戶登陸。 |
性能 | 在每次請求時需要驗證和解碼token,性能較差。 | 只需查找sessionID就能獲取會話信息,性能較好。 |
擴展性 | 在多服務(wù)器或者跨域環(huán)境中更易擴展。 | 在多服務(wù)器環(huán)境中需要同步session,擴展性較差。 |
數(shù)據(jù)大小 | JWT的大小比sessionID大,因此需要更多的帶寬。 | sessionID大小穩(wěn)定,對帶寬需求較小。 |
到期時間 | 可以為每個token設(shè)置不同的過期時間。 | 所有session的過期時間通常相同。 |
客戶端存儲位置 | 可以存儲在Cookie, LocalStorage, SessionStorage中 | 存儲在Cookie中。 |
跨域問題 | 無跨域問題,且對于移動應(yīng)用而言友好。 | 跨域問題復(fù)雜,需要服務(wù)器支持CORS。 |
狀態(tài) | 無狀態(tài),服務(wù)器不需要保存用戶信息。 | 有狀態(tài),服務(wù)器需要保存用戶信息。 |
使用場景 | 用于認(rèn)證和信息交換,尤其適合單頁應(yīng)用(SPA)和前后端分離的項目 | 主要用于記錄用戶狀態(tài),適配傳統(tǒng)的后端渲染的Web服務(wù) |
總體上來說,JWT
適用于前后端分離的API
服務(wù),它可以簡化服務(wù)端的存儲需求,并提供更好的跨平臺兼容性和可擴展性,同時也能提供安全可靠的身份認(rèn)證機制。而Session
則適用于服務(wù)器渲染的Web應(yīng)用,它可以提供更高的安全性和可調(diào)用性,同時也能減輕客戶端的流量負(fù)擔(dān)。不同的場景需要選擇最適合的身份認(rèn)證方案,根據(jù)實際需求進(jìn)行選擇。
六、總結(jié)
在本文中,我們探討了如何在Node.js
的Express
框架中使用jsonwebtoken
來實現(xiàn)身份認(rèn)證。我們首先介紹了JWT
協(xié)議和jsonwebtoken
模塊的基礎(chǔ)知識,然后展示了如何在Express
中使用jsonwebtoken
來生成、解析和驗證JWT
。通過在實際項目中的舉例,我們演示了如何將jsonwebtoken
與路由中間件結(jié)合使用,實現(xiàn)基于token
的用戶身份認(rèn)證機制。使用jsonwebtoken
可以為我們的web應(yīng)用提供更安全和可靠的用戶身份認(rèn)證方案,同時jwt
的優(yōu)點也是很明顯的:無需在服務(wù)端保存session
信息,具有可擴展性和跨平臺兼容性等特點。通過本文的介紹,相信讀者已經(jīng)能夠掌握jsonwebtoken
的基本使用方法和原理,能夠在實際項目中使用jsonwebtoken
實現(xiàn)安全可靠的身份認(rèn)證機制。