# JWT在Node中怎么使用
## 目錄
- [JWT基礎概念](#jwt基礎概念)
- [什么是JWT](#什么是jwt)
- [JWT的組成結構](#jwt的組成結構)
- [JWT的工作原理](#jwt的工作原理)
- [Node.js環境準備](#nodejs環境準備)
- [初始化項目](#初始化項目)
- [安裝必要依賴](#安裝必要依賴)
- [JWT核心實現](#jwt核心實現)
- [生成JWT令牌](#生成jwt令牌)
- [驗證JWT令牌](#驗證jwt令牌)
- [處理令牌過期](#處理令牌過期)
- [實戰應用場景](#實戰應用場景)
- [用戶認證系統](#用戶認證系統)
- [API接口保護](#api接口保護)
- [跨服務通信](#跨服務通信)
- [安全最佳實踐](#安全最佳實踐)
- [密鑰管理方案](#密鑰管理方案)
- [令牌刷新機制](#令牌刷新機制)
- [常見攻擊防護](#常見攻擊防護)
- [性能優化技巧](#性能優化技巧)
- [負載壓縮策略](#負載壓縮策略)
- [緩存驗證結果](#緩存驗證結果)
- [調試與問題排查](#調試與問題排查)
- [常見錯誤分析](#常見錯誤分析)
- [日志記錄策略](#日志記錄策略)
- [擴展進階](#擴展進階)
- [自定義Claims](#自定義claims)
- [多因素認證集成](#多因素認證集成)
- [總結與展望](#總結與展望)
## JWT基礎概念
### 什么是JWT
JSON Web Token(JWT)是一種開放標準(RFC 7519),用于在各方之間安全地傳輸信息作為JSON對象。這種信息可以被驗證和信任,因為它是經過數字簽名的。
**典型特征**:
- 緊湊的URL安全表示形式
- 可自包含(包含所有必要信息)
- 適用于分布式系統的身份驗證
- 支持跨域身份驗證
### JWT的組成結構
標準JWT由三部分組成,通過點(.)連接:
Header.Payload.Signature
1. **Header**(頭部):
```json
{
"alg": "HS256",
"typ": "JWT"
}
Payload(負載): 包含聲明(claims),分為三類:
Signature(簽名): 通過指定算法對前兩部分簽名,例如:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
sequenceDiagram
participant Client
participant Server
Client->>Server: 登錄請求(用戶名/密碼)
Server->>Client: 返回JWT
Client->>Server: 業務請求(攜帶JWT)
Server->>Client: 返回業務數據
mkdir node-jwt-demo
cd node-jwt-demo
npm init -y
npm install jsonwebtoken bcryptjs dotenv express
npm install --save-dev @types/jsonwebtoken
關鍵包說明:
- jsonwebtoken:JWT核心庫
- bcryptjs:密碼哈希處理
- dotenv:環境變量管理
- express:Web框架
const jwt = require('jsonwebtoken');
require('dotenv').config();
function generateAccessToken(user) {
return jwt.sign(
{
userId: user.id,
email: user.email
},
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
}
// 示例用法
const user = { id: 123, email: 'test@example.com' };
const token = generateAccessToken(user);
console.log('Generated Token:', token);
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
console.error('Token verification failed:', err);
return res.sendStatus(403);
}
req.user = user;
next();
});
}
function generateTokenPair(user) {
const accessToken = jwt.sign(
{ userId: user.id },
process.env.ACCESS_TOKEN_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
}
完整登錄流程示例:
const express = require('express');
const bcrypt = require('bcryptjs');
const app = express();
app.post('/login', async (req, res) => {
try {
// 1. 驗證用戶憑證
const user = await User.findOne({ email: req.body.email });
if (!user) return res.status(401).send('Invalid credentials');
// 2. 驗證密碼
const validPassword = await bcrypt.compare(
req.body.password,
user.passwordHash
);
if (!validPassword) return res.status(401).send('Invalid credentials');
// 3. 生成令牌
const token = generateAccessToken(user);
// 4. 返回響應
res.json({
accessToken: token,
expiresIn: 3600
});
} catch (err) {
console.error('Login error:', err);
res.status(500).send('Internal Server Error');
}
});
保護路由示例:
app.get('/protected', authenticateToken, (req, res) => {
// 只有攜帶有效JWT才能訪問
res.json({
message: 'Protected data',
user: req.user
});
});
微服務間認證示例:
// 服務A生成令牌
const serviceToken = jwt.sign(
{ service: 'service-a' },
process.env.INTER_SERVICE_SECRET,
{ expiresIn: '5m' }
);
// 服務B驗證令牌
function verifyServiceToken(req, res, next) {
const token = req.headers['service-token'];
jwt.verify(token, process.env.INTER_SERVICE_SECRET, (err, decoded) => {
if (err || decoded.service !== 'service-a') {
return res.status(403).send('Forbidden');
}
next();
});
}
推薦做法: 1. 使用環境變量存儲密鑰
JWT_SECRET=your_strong_secret_here
ACCESS_TOKEN_SECRET=access_secret
REFRESH_TOKEN_SECRET=refresh_secret
app.post('/refresh', (req, res) => {
const refreshToken = req.body.token;
if (!refreshToken) return res.sendStatus(401);
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
const newAccessToken = generateAccessToken({
id: user.userId
});
res.json({ accessToken: newAccessToken });
});
});
防護策略: 1. CSRF防護:SameSite Cookie屬性 2. XSS防護:HttpOnly Cookie 3. 重放攻擊:使用jti(JWT ID)和nonce 4. 令牌劫持:短期有效期+HTTPS
對于大型payload:
const zlib = require('zlib');
function compressPayload(payload) {
return zlib.deflateSync(JSON.stringify(payload)).toString('base64');
}
function generateCompressedToken(user) {
const compressed = compressPayload(user);
return jwt.sign(
{ data: compressed },
process.env.JWT_SECRET
);
}
使用Redis緩存已驗證令牌:
const redis = require('redis');
const client = redis.createClient();
function cachedVerify(token) {
return new Promise((resolve, reject) => {
client.get(`jwt:${token}`, (err, cached) => {
if (cached) return resolve(JSON.parse(cached));
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) return reject(err);
client.setex(`jwt:${token}`, 300, JSON.stringify(decoded));
resolve(decoded);
});
});
});
}
| 錯誤 | 原因 | 解決方案 |
|---|---|---|
| Invalid token | 令牌格式錯誤 | 檢查Authorization頭格式 |
| Token expired | 令牌過期 | 實現刷新令牌機制 |
| Invalid signature | 密鑰不匹配 | 驗證密鑰一致性 |
| Algorithm mismatch | 算法不一致 | 檢查header中的alg聲明 |
增強版驗證中間件:
function verboseAuth(req, res, next) {
const token = extractToken(req);
if (!token) {
console.warn('Authorization header missing');
return res.sendStatus(401);
}
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
console.error(`JWT verification failed: ${err.name}`);
console.debug('Token content:', jwt.decode(token));
return res.sendStatus(403);
}
console.log(`Authenticated user: ${decoded.userId}`);
req.user = decoded;
next();
});
}
添加業務特定聲明:
function generateCustomToken(user) {
return jwt.sign(
{
// 標準聲明
sub: user.id,
iat: Math.floor(Date.now() / 1000),
// 自定義聲明
'https://yourdomain.com/is_premium': user.isPremium,
roles: ['user', 'editor']
},
process.env.JWT_SECRET,
{ algorithm: 'HS256' }
);
}
結合MFA的令牌生成:
function generateMfaToken(user, mfaVerified = false) {
return jwt.sign(
{
sub: user.id,
mfa: mfaVerified
},
process.env.JWT_SECRET,
{
expiresIn: mfaVerified ? '8h' : '15m'
}
);
}
最佳實踐建議:對于新項目,建議采用JWT作為認證基礎,但必須配合HTTPS、合理的密鑰管理和完善的令牌生命周期管理方案。
完整代碼示例:可參考GitHub倉庫 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。