乐闻世界logo
搜索文章和话题

JWT 在微服务架构中如何使用

2月21日 17:52

JWT 在微服务架构中的使用需要考虑分布式系统的特殊性,以下是完整的实现方案:

微服务架构中的 JWT 挑战

1. 服务间认证

  • 如何在微服务之间安全地传递 JWT
  • 如何验证来自其他服务的请求

2. 密钥管理

  • 多个服务如何共享或获取公钥
  • 密钥轮换如何协调

3. Token 传播

  • 如何在服务调用链中传递 JWT
  • 如何处理 Token 过期

4. 权限控制

  • 如何实现细粒度的权限控制
  • 如何处理不同服务的权限需求

架构设计

1. 集中式认证服务

shell
┌─────────────┐ │ Client │ └──────┬──────┘ ┌─────────────┐ │ API Gateway│ └──────┬──────┘ ┌─────────────────┐ │ Auth Service │ (签发 JWT)└─────────────────┘ ┌─────────────────┐ │ JWK Set │ (公钥分发)└─────────────────┘

2. 服务间通信流程

shell
Client → API Gateway → Auth Service → JWT Service A → Service B Service C

实现方案

1. 使用 JWK (JSON Web Key) 分发公钥

Auth Service 实现

javascript
const jwt = require('jsonwebtoken'); const { generateKeyPairSync } = require('crypto'); // 生成密钥对 const { publicKey, privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); // 生成 JWK function generateJWK(publicKey, kid = 'key1') { const jwk = { kty: 'RSA', kid: kid, use: 'sig', alg: 'RS256', n: publicKey.match(/Modulus:\s*([^\n]+)/)?.[1], e: publicKey.match(/Exponent:\s*([^\n]+)/)?.[1] }; return jwk; } // JWK Set 端点 app.get('/.well-known/jwks.json', (req, res) => { const jwk = generateJWK(publicKey); res.json({ keys: [jwk] }); }); // 签发 JWT app.post('/auth/login', (req, res) => { const { username, password } = req.body; const user = validateUser(username, password); const token = jwt.sign( { userId: user.id, role: user.role, permissions: user.permissions }, privateKey, { algorithm: 'RS256', keyid: 'key1', expiresIn: '1h', issuer: 'auth-service', audience: 'microservices' } ); res.json({ token }); });

其他服务验证 JWT

javascript
const jose = require('node-jose'); const jwt = require('jsonwebtoken'); let cachedPublicKey = null; let cacheExpiry = 0; async function getPublicKey() { if (cachedPublicKey && Date.now() < cacheExpiry) { return cachedPublicKey; } const response = await fetch('https://auth-service/.well-known/jwks.json'); const jwks = await response.json(); const keystore = await jose.JWK.createKeyStore(); await keystore.add(jwks.keys[0]); cachedPublicKey = keystore.get(jwks.keys[0].kid).toPEM(false); cacheExpiry = Date.now() + (5 * 60 * 1000); // 缓存5分钟 return cachedPublicKey; } async function verifyToken(token) { const publicKey = await getPublicKey(); try { const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'], issuer: 'auth-service', audience: 'microservices' }); return decoded; } catch (error) { throw new Error('Invalid token'); } }

2. API Gateway 集成

javascript
const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const app = express(); // 认证中间件 app.use(async (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; try { const decoded = await verifyToken(token); req.user = decoded; next(); } catch (error) { res.status(401).json({ error: 'Invalid token' }); } }); // 路由到不同服务 app.use('/api/users', createProxyMiddleware({ target: 'http://user-service:3001', changeOrigin: true })); app.use('/api/orders', createProxyMiddleware({ target: 'http://order-service:3002', changeOrigin: true })); app.use('/api/products', createProxyMiddleware({ target: 'http://product-service:3003', changeOrigin: true }));

3. 服务间 Token 传播

javascript
const axios = require('axios'); class ServiceClient { constructor(baseURL) { this.client = axios.create({ baseURL, timeout: 5000 }); this.client.interceptors.request.use(config => { const token = getCurrentToken(); // 从上下文获取当前 token if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }); } async get(url, config) { return this.client.get(url, config); } async post(url, data, config) { return this.client.post(url, data, config); } } // 使用示例 const userService = new ServiceClient('http://user-service:3001'); const orderService = new ServiceClient('http://order-service:3002'); async function getUserOrders(userId) { const user = await userService.get(`/users/${userId}`); const orders = await orderService.get(`/orders?userId=${userId}`); return { user: user.data, orders: orders.data }; }

4. 权限控制

javascript
// 基于角色的访问控制(RBAC) function requireRole(...roles) { return (req, res, next) => { if (!req.user) { return res.status(401).json({ error: 'Unauthorized' }); } if (!roles.includes(req.user.role)) { return res.status(403).json({ error: 'Forbidden' }); } next(); }; } // 基于权限的访问控制 function requirePermission(...permissions) { return (req, res, next) => { if (!req.user) { return res.status(401).json({ error: 'Unauthorized' }); } const userPermissions = req.user.permissions || []; const hasPermission = permissions.every(p => userPermissions.includes(p)); if (!hasPermission) { return res.status(403).json({ error: 'Forbidden' }); } next(); }; } // 使用示例 app.get('/admin/users', requireRole('admin'), (req, res) => { res.json({ users: [] }); }); app.post('/orders', requirePermission('order:create'), (req, res) => { res.json({ success: true }); });

5. 密钥轮换

javascript
const keyStore = { keys: new Map(), currentKeyId: null }; // 添加新密钥 function addNewKey() { const { publicKey, privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }); const kid = `key${Date.now()}`; keyStore.keys.set(kid, { publicKey, privateKey, createdAt: Date.now() }); keyStore.currentKeyId = kid; return kid; } // 获取当前密钥 function getCurrentKey() { return keyStore.keys.get(keyStore.currentKeyId); } // 获取所有公钥(用于验证) function getAllPublicKeys() { const keys = []; for (const [kid, key] of keyStore.keys.entries()) { keys.push({ kid, publicKey: key.publicKey, createdAt: key.createdAt }); } return keys; } // 签发 JWT(使用当前密钥) function signToken(payload) { const currentKey = getCurrentKey(); return jwt.sign(payload, currentKey.privateKey, { algorithm: 'RS256', keyid: keyStore.currentKeyId, expiresIn: '1h' }); } // 验证 JWT(尝试所有密钥) function verifyToken(token) { const decoded = jwt.decode(token, { complete: true }); const kid = decoded.header.kid; const key = keyStore.keys.get(kid); if (!key) { throw new Error('Key not found'); } return jwt.verify(token, key.publicKey, { algorithms: ['RS256'] }); } // 定期轮换密钥 setInterval(() => { addNewKey(); // 清理旧密钥(保留最近7天) const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000); for (const [kid, key] of keyStore.keys.entries()) { if (key.createdAt < sevenDaysAgo && kid !== keyStore.currentKeyId) { keyStore.keys.delete(kid); } } }, 30 * 24 * 60 * 60 * 1000); // 每30天轮换一次

6. 监控和日志

javascript
const { createLogger, format, transports } = require('winston'); const logger = createLogger({ format: format.combine( format.timestamp(), format.json() ), transports: [ new transports.Console(), new transports.File({ filename: 'auth.log' }) ] }); // 认证中间件添加日志 app.use(async (req, res, next) => { const startTime = Date.now(); try { const token = req.headers['authorization']?.replace('Bearer ', ''); if (token) { const decoded = await verifyToken(token); req.user = decoded; logger.info({ event: 'auth_success', userId: decoded.userId, path: req.path, method: req.method, ip: req.ip }); } next(); } catch (error) { const duration = Date.now() - startTime; logger.error({ event: 'auth_failure', error: error.message, path: req.path, method: req.method, ip: req.ip, duration }); res.status(401).json({ error: 'Unauthorized' }); } }); // 指标收集 const authMetrics = { totalRequests: 0, successfulAuth: 0, failedAuth: 0, avgAuthTime: 0 }; function updateMetrics(duration, success) { authMetrics.totalRequests++; if (success) { authMetrics.successfulAuth++; } else { authMetrics.failedAuth++; } authMetrics.avgAuthTime = (authMetrics.avgAuthTime * (authMetrics.totalRequests - 1) + duration) / authMetrics.totalRequests; } // 指标端点 app.get('/metrics', (req, res) => { res.json(authMetrics); });

最佳实践

  1. 使用非对称加密(RS256)而非对称加密(HS256)
  2. 实现 JWK 端点用于公钥分发
  3. 缓存公钥减少网络请求
  4. 实现密钥轮换机制
  5. 使用 API Gateway统一认证
  6. 实现细粒度权限控制
  7. 添加监控和日志
  8. 处理 Token 过期和刷新
  9. 使用 HTTPS传输
  10. 实现速率限制防止滥用

通过以上方案,可以在微服务架构中安全、高效地使用 JWT 进行认证和授权。

标签:JWT