什么是 JSON Web Token(JWT)?
JSON Web Token(JWT)是一種用于在web上傳遞信息的標(biāo)準(zhǔn),它以JSON格式表示信息,通常用于身份驗(yàn)證和授權(quán)。
JWT由三個(gè)部分組成:Header(頭部)、Payload(負(fù)載)和Signature(簽名)。它們用點(diǎn)號(hào)分隔開(kāi),形成了一個(gè)JWT令牌。
JWT 的基本結(jié)構(gòu)
- Header
Header(頭部)是JWT結(jié)構(gòu)的第一部分,它是一個(gè)包含關(guān)于令牌的元數(shù)據(jù)的JSON對(duì)象。Header通常包含兩個(gè)主要字段:alg
和 typ
。
alg
(Algorithm)字段:這個(gè)字段指定了用于簽名JWT的加密算法。它可以是以下之一:
-
HS256
:HMAC-SHA256,使用密鑰進(jìn)行對(duì)稱加密。 -
RS256
:RSA-SHA256,使用RSA密鑰對(duì)進(jìn)行非對(duì)稱加密。 -
ES256
:ECDSA-SHA256,使用橢圓曲線數(shù)字簽名算法進(jìn)行非對(duì)稱加密,等等。
typ
(Type)字段:這個(gè)字段表示令牌的類型。對(duì)于JWT,這個(gè)字段的值通常是**JWT
**,用于指示這是一個(gè)JSON Web Token。
一個(gè)簡(jiǎn)單的 JWT 頭可以是下面這樣:
{
"typ":"JWT",
"alg":"HS256"
}
- Payload
Payload(負(fù)載)用于存儲(chǔ)實(shí)際的用戶數(shù)據(jù)和其他相關(guān)信息。Payload是一個(gè)包含鍵值對(duì)的JSON對(duì)象,它包含了有關(guān)JWT令牌的有用信息。
JWT 規(guī)定了7個(gè)官方字段:
- iss (issuer):簽發(fā)人
- exp (expiration time):過(guò)期時(shí)間
- sub (subject):主題
- aud (audience):受眾
- nbf (Not Before):生效時(shí)間
- iat (Issued At):簽發(fā)時(shí)間
- jti (JWT ID):編號(hào)
除了官方字段,你還可以在這個(gè)部分定義私有字段,一個(gè)Payload如下所示:
{
"userId":"123",
"iss": "your_app",
"sub": "user123",
"role": "admin",
"exp": 1699999999
}
- Signature
JWT的Signature(簽名)是JWT令牌的第三個(gè)部分,用于確保令牌的完整性和來(lái)源驗(yàn)證。Signature是通過(guò)將Header和Payload的組合(不包括分隔符**.
**)與一個(gè)密鑰進(jìn)行加密或哈希生成的值。
Signature生成方式:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
一個(gè)JWT示例
Header:
{
"alg" : "HS256",
"typ" : "JWT"
}
Payload:
{
"id" : 123,
"name" : "test"
}
Secret: your_secret
Header(經(jīng)過(guò)Base64編碼):
eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9
Payload(經(jīng)過(guò)Base64編碼):
eyJpZCI6IDEyMywgIm5hbWUiOiAidGVzdCJ9
使用提供的Secret對(duì)原始的Header和原始的Payload進(jìn)行加密生成Signature:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
"your_secret"
)
完整的的token需要吧這三部分拼起來(lái)如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTIzLCJuYW1lIjoidGVzdCJ9.oMyOEgY
iZosc0HYCkIjrqh_DH3CLlmIkIjOe-icpTg8
在Nodejs中使用JWT
1,環(huán)境配置
我們先來(lái)配置一下環(huán)境,首先初始化一個(gè)package.json文件存放我們用到的npm包:
npm init -y
然后安裝jsonwebtoken和express:npm install express jsonwebtoken
2,創(chuàng)建一個(gè)基礎(chǔ)的服務(wù)器
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is running on ${PORT} ...`);
});
這里我們使用了**express.json**
這個(gè)中間件,express.json()
是一個(gè) Express 中間件函數(shù),用于解析傳入請(qǐng)求體中的 JSON 數(shù)據(jù)。當(dāng)客戶端向服務(wù)器發(fā)送帶有 JSON 數(shù)據(jù)的 POST 請(qǐng)求時(shí),express.json()
中間件將從請(qǐng)求體中解析出 JSON 數(shù)據(jù),并將其添加到到 req.body
上。
3,在登錄之后下發(fā)token
// 用戶數(shù)據(jù)
const users = [
{ id: 1, username: "user1", password: "password1" },
{ id: 2, username: "user2", password: "password2" }
];
const jwtSecretKey = "your_jwt_secret_key";
// 登錄之后生成 JWT token
app.post("/user/login", async (req, res) => {
try {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ error: "用戶名或密碼錯(cuò)誤" });
}
const payload = {
userId: user.id,
};
//生成token 設(shè)置過(guò)期時(shí)間為 1 小時(shí)
const token = await jwt.sign(payload, jwtSecretKey, { expiresIn: '1h' });
res.json({ token });
} catch (error) {
res.status(500).json({ error: "登錄失敗" });
}
});
為了演示,我們的用戶數(shù)據(jù)是寫死的
/user/login路由會(huì)在用戶名和密碼通過(guò)校驗(yàn)之后,使用jwt.sign生成一個(gè)token,并且設(shè)置過(guò)期時(shí)間為一個(gè)小時(shí)
jwt.sign
函數(shù)用于創(chuàng)建一個(gè) JWT 令牌,它接受一個(gè)payload,并使用給定的密鑰將其簽名生成一個(gè)令牌字符串。
以下是 jwt.sign
的基本用法以及其參數(shù):
jwt.sign(payload, secretOrPrivateKey, [options, callback])
-
payload
:要存儲(chǔ)在token中的數(shù)據(jù),通常是一個(gè) JavaScript 對(duì)象,可以包含任意信息。 -
secretOrPrivateKey
:用于對(duì)令牌進(jìn)行簽名的密鑰。 -
options
(可選):一個(gè)包含選項(xiàng)的對(duì)象,用于配置生成的 JWT。常見(jiàn)的選項(xiàng)包括expiresIn
(過(guò)期時(shí)間)和algorithm
(簽名算法)等。 -
callback
(可選):一個(gè)回調(diào)函數(shù),用于異步生成 JWT。
然后使用curl請(qǐng)求該路由,響應(yīng)內(nèi)容如下:
4,創(chuàng)建isLogin中間件
async function isLogin(req, res, next) {
const tokenHeaderKey = 'Authorization';
const token = req.header(tokenHeaderKey)
if (!token) {
return res.status(401).json({ error: "用戶未登錄" });
}
const verified = await jwt.verify(token, jwtSecretKey);
if (verified) {
next()
} else {
return res.status(401).json({ error: "無(wú)效的token" });
}
}
isLogin
是一個(gè)用于驗(yàn)證用戶是否已登錄的中間件。它首先從請(qǐng)求頭中獲取 Authorization
字段的值,該值應(yīng)該是 JWT 令牌。然后使用 jwt.verify
函數(shù)驗(yàn)證令牌的有效性。如果驗(yàn)證通過(guò),將調(diào)用 next()
,表示用戶已登錄,然后繼續(xù)執(zhí)行后續(xù)的路由處理程序。如果驗(yàn)證失敗,返回 401 狀態(tài)碼,表示令牌無(wú)效。
5,創(chuàng)建需要身份驗(yàn)證的路由
// 獲取用戶信息
app.get("/user/:username", isLogin, async (req, res) => {
const username = req.params.username;
const user = users.find(u => u.username === username).map(u => ({ id: u.id, username: u.username}));
res.json(user);
});
**/user/:username
**是一個(gè)用于獲取用戶信息的路由。路由中的 :username
表示參數(shù),表示用戶的用戶名。
我們?cè)谶@個(gè)路由中添加了兩個(gè)中間件,首先通過(guò) isLogin
中間件驗(yàn)證用戶是否已登錄。如果用戶已登錄,才會(huì)進(jìn)入到下一個(gè)中間件,然后根據(jù)用戶名從 users
數(shù)組中查找用戶信息并作為響應(yīng)。
然后使用curl請(qǐng)求該路由,就能拿到用戶信息:
6,使用axios攜帶token請(qǐng)求用戶信息
import axios from "axios";
const token = localStorage.getItem("token");
const url = "http://localhost:3000/user/your_username"
const headers = {
"Authorization": token,
"Content-Type": "application/json",
"Accept": "application/json",
};
const getUserInfo = () => {
axios.get(url, { headers: headers })
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
}
在前端我們一般會(huì)使用axios來(lái)發(fā)起請(qǐng)求,只要把token的值放在http header中的Authorization字段即可。當(dāng)然除了放在Authorization之外,也可以放在其他header字段中,不過(guò)后端也需要從對(duì)應(yīng)的header字段取token。
完整代碼:
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is up and running on ${PORT} ...`);
});
// 用戶數(shù)據(jù)
const users = [
{ id: 1, username: "user1", password: "password1" },
{ id: 2, username: "user2", password: "password2" }
];
const jwtSecretKey = "your_jwt_secret_key";
// 登錄之后生成 JWT token
app.post("/user/login", async (req, res) => {
try {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ error: "用戶名或密碼錯(cuò)誤" });
}
const payload = {
userId: user.id,
};
//生成token 設(shè)置過(guò)期時(shí)間為 1 小時(shí)
const token = await jwt.sign(payload, jwtSecretKey, { expiresIn: '1h' });
res.json({ token });
} catch (error) {
res.status(500).json({ error: "登錄失敗" });
}
});
async function isLogin(req, res, next) {
const tokenHeaderKey = 'Authorization';
const token = req.header(tokenHeaderKey)
if (!token) {
return res.status(401).json({ error: "用戶未登錄" });
}
const verified = await jwt.verify(token, jwtSecretKey);
if (verified) {
next()
} else {
return res.status(401).json({ error: "無(wú)效的token" });
}
}
// 獲取用戶信息
app.get("/user/:username", isLogin, async (req, res) => {
const username = req.params.username;
const user = users.map(u => ({ id: u.id, username: u.username})).find(u => u.username === username)
res.json(user);
});
總結(jié)
這篇文章我們介紹了JWT原理以及在nodejs中使用JWT 進(jìn)行鑒權(quán),除了JWT之外還可以使用session管理用戶狀態(tài),感興趣的可以??這里:https://blog.csdn.net/AC_greener/article/details/130036699
參考文章:
https://github.com/auth0/node-jsonwebtoken文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-683093.html
https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-683093.html
到了這里,關(guān)于在Nodejs中使用JWT進(jìn)行鑒權(quán)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!