diff --git a/back/src/middleware/jwt.js b/back/src/middleware/jwt.js new file mode 100644 index 0000000..3018d50 --- /dev/null +++ b/back/src/middleware/jwt.js @@ -0,0 +1,70 @@ +const jwt = require('jsonwebtoken'); +const redisClient = require('../config/redis'); +const { standardResponse } = require('../utils/standardResponse'); + +const JWT_SECRET = 'cus'; // TODO + +// 生成 JWT token +function generateToken(payload, expiresIn) { + const signOptions = { + expiresIn: expiresIn + }; + return jwt.sign(payload, JWT_SECRET, signOptions); +} + +// 验证 JWT token +function verifyToken(token) { + return jwt.verify(token, JWT_SECRET); +} + +// 将 token 添加到 Redis 黑名单 +async function addTokenToBlacklist(token, expiresIn = 3600) { + await redisClient.setEx(`blacklist:${token}`, expiresIn, '1'); +} + +// 检查 token 是否在 Redis 黑名单中 +async function isTokenBlacklisted(token) { + return await redisClient.exists(`blacklist:${token}`) === 1; +} + +// JWT 认证中间件 +async function jwtAuth(req, res, next) { + const authHeader = req.headers.authorization; + + if (!authHeader) { + return res.status(401).json(standardResponse(401, '未提供认证信息')); + } + + const parts = authHeader.split(' '); + if (parts.length !== 2 || parts[0] !== 'Bearer') { + return res.status(401).json(standardResponse(401, '认证格式错误,应为:Bearer ')); + } + + const token = parts[1]; + + try { + if (await isTokenBlacklisted(token)) { + return res.status(401).json(standardResponse(401, 'token 已失效')); + } + + const decoded = verifyToken(token); + req.user = decoded; + req.token = token; + next(); + } catch (error) { + if (error.name === 'TokenExpiredError') { + return res.status(401).json(standardResponse(401, 'token 已过期')); + } + if (error.name === 'JsonWebTokenError') { + return res.status(401).json(standardResponse(401, '无效的 token')); + } + return res.status(500).json(standardResponse(401, '认证失败')); + } +} + +module.exports = { + generateToken, + verifyToken, + addTokenToBlacklist, + jwtAuth +}; \ No newline at end of file