在 Node.js 中实现 JWT 认证通常使用 jsonwebtoken 库。以下是完整的实现步骤:
1. 安装依赖
bashnpm install jsonwebtoken npm install @types/jsonwebtoken --save-dev # TypeScript
2. 生成 JWT Token
javascriptconst jwt = require('jsonwebtoken'); const SECRET_KEY = 'your-secret-key'; // 生产环境应从环境变量读取 function generateToken(payload, expiresIn = '1h') { return jwt.sign(payload, SECRET_KEY, { expiresIn, issuer: 'your-app.com', audience: 'your-api' }); } // 使用示例 const user = { id: '123', username: 'john', role: 'admin' }; const token = generateToken(user, '2h'); console.log(token);
3. 验证 JWT Token
javascriptfunction verifyToken(token) { try { const decoded = jwt.verify(token, SECRET_KEY, { issuer: 'your-app.com', audience: 'your-api' }); return { success: true, decoded }; } catch (error) { if (error.name === 'TokenExpiredError') { return { success: false, error: 'Token expired' }; } else if (error.name === 'JsonWebTokenError') { return { success: false, error: 'Invalid token' }; } return { success: false, error: error.message }; } } // 使用示例 const result = verifyToken(token); if (result.success) { console.log('Decoded:', result.decoded); } else { console.log('Error:', result.error); }
4. Express 中间件实现
javascriptconst express = require('express'); const app = express(); // 认证中间件 function authMiddleware(req, res, next) { const authHeader = req.headers['authorization']; if (!authHeader) { return res.status(401).json({ error: 'No token provided' }); } const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : authHeader; const result = verifyToken(token); if (!result.success) { return res.status(401).json({ error: result.error }); } req.user = result.decoded; next(); } // 登录路由 - 生成 token app.post('/login', (req, res) => { const { username, password } = req.body; // 验证用户凭据(实际项目中查询数据库) if (username === 'admin' && password === 'password') { const token = generateToken({ id: '123', username, role: 'admin' }, '2h'); res.json({ success: true, token, expiresIn: '2h' }); } else { res.status(401).json({ error: 'Invalid credentials' }); } }); // 受保护的路由 app.get('/profile', authMiddleware, (req, res) => { res.json({ user: req.user, message: 'This is protected data' }); }); app.listen(3000, () => { console.log('Server running on port 3000'); });
5. Refresh Token 实现
javascriptconst crypto = require('crypto'); // 存储刷新令牌(生产环境使用 Redis) const refreshTokens = new Map(); function generateRefreshToken() { return crypto.randomBytes(40).toString('hex'); } // 登录时生成 access token 和 refresh token app.post('/login', (req, res) => { const { username, password } = req.body; if (username === 'admin' && password === 'password') { const accessToken = generateToken({ id: '123', username }, '15m'); // 短期 const refreshToken = generateRefreshToken(); refreshTokens.set(refreshToken, '123'); res.json({ accessToken, refreshToken, expiresIn: '15m' }); } else { res.status(401).json({ error: 'Invalid credentials' }); } }); // 刷新 token app.post('/refresh', (req, res) => { const { refreshToken } = req.body; if (!refreshToken || !refreshTokens.has(refreshToken)) { return res.status(401).json({ error: 'Invalid refresh token' }); } const userId = refreshTokens.get(refreshToken); const accessToken = generateToken({ id: userId }, '15m'); res.json({ accessToken, expiresIn: '15m' }); }); // 登出 - 删除 refresh token app.post('/logout', (req, res) => { const { refreshToken } = req.body; refreshTokens.delete(refreshToken); res.json({ success: true }); });
6. TypeScript 版本
typescriptimport jwt from 'jsonwebtoken'; interface TokenPayload { id: string; username: string; role?: string; } interface DecodedToken extends TokenPayload { iss: string; aud: string; iat: number; exp: number; } const SECRET_KEY = process.env.JWT_SECRET || 'your-secret-key'; export function generateToken( payload: TokenPayload, expiresIn: string | number = '1h' ): string { return jwt.sign(payload, SECRET_KEY, { expiresIn, issuer: 'your-app.com', audience: 'your-api' }); } export function verifyToken(token: string): { success: boolean; decoded?: DecodedToken; error?: string; } { try { const decoded = jwt.verify(token, SECRET_KEY) as DecodedToken; return { success: true, decoded }; } catch (error: any) { if (error.name === 'TokenExpiredError') { return { success: false, error: 'Token expired' }; } return { success: false, error: 'Invalid token' }; } }
7. 环境变量配置
bash# .env JWT_SECRET=your-super-secret-key-change-in-production JWT_EXPIRES_IN=1h JWT_REFRESH_EXPIRES_IN=7d
最佳实践
- 使用强密钥(至少 32 字符)
- 从环境变量读取密钥
- 设置合理的过期时间
- 实现 Refresh Token 机制
- 使用 HTTPS 传输
- 验证 issuer 和 audience
- 捕获并处理所有错误
- 记录认证失败的日志
通过以上实现,你可以在 Node.js 应用中安全地使用 JWT 进行身份验证。