159 lines
3.8 KiB
JavaScript
Executable File
159 lines
3.8 KiB
JavaScript
Executable File
const jwt = require('jsonwebtoken')
|
|
const { Admin, User } = require('../models')
|
|
|
|
/**
|
|
* JWT 认证中间件(管理员)
|
|
*/
|
|
const authMiddleware = async (req, res, next) => {
|
|
try {
|
|
// 从 header 获取 token
|
|
const authHeader = req.header('Authorization')
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: '未提供认证令牌'
|
|
})
|
|
}
|
|
|
|
const token = authHeader.replace('Bearer ', '')
|
|
|
|
// 验证 token
|
|
const decoded = jwt.verify(token, process.env.JWT_SECRET)
|
|
|
|
// 查找管理员
|
|
const admin = await Admin.findById(decoded.adminId)
|
|
if (!admin) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: '管理员不存在'
|
|
})
|
|
}
|
|
|
|
// 检查状态
|
|
if (admin.status !== 'active') {
|
|
return res.status(403).json({
|
|
success: false,
|
|
error: '管理员账户已禁用'
|
|
})
|
|
}
|
|
|
|
// 将管理员信息附加到请求对象
|
|
req.admin = admin
|
|
next()
|
|
} catch (error) {
|
|
if (error.name === 'JsonWebTokenError') {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: '无效的认证令牌'
|
|
})
|
|
}
|
|
if (error.name === 'TokenExpiredError') {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: '认证令牌已过期'
|
|
})
|
|
}
|
|
next(error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 权限检查中间件
|
|
* @param {string} permission - 需要的权限
|
|
*/
|
|
const requirePermission = (permission) => {
|
|
return (req, res, next) => {
|
|
if (!req.admin) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: '未认证'
|
|
})
|
|
}
|
|
|
|
if (!req.admin.hasPermission(permission)) {
|
|
return res.status(403).json({
|
|
success: false,
|
|
error: '权限不足'
|
|
})
|
|
}
|
|
|
|
next()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 小程序用户 JWT 认证中间件
|
|
*/
|
|
const userAuthMiddleware = async (req, res, next) => {
|
|
try {
|
|
const authHeader = req.header('Authorization')
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: '未提供认证令牌'
|
|
})
|
|
}
|
|
|
|
const token = authHeader.replace('Bearer ', '')
|
|
|
|
const decoded = jwt.verify(token, process.env.JWT_SECRET)
|
|
|
|
// 兼容两种token格式:包含userId或openid
|
|
let user = null
|
|
if (decoded.userId) {
|
|
user = await User.findById(decoded.userId).select('+sessionKey') // vpay 签名需要
|
|
} else if (decoded.openid) {
|
|
user = await User.findOne({ openid: decoded.openid })
|
|
}
|
|
|
|
if (!user) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: '用户不存在或令牌无效'
|
|
})
|
|
}
|
|
|
|
req.user = user
|
|
// 优先使用 openid,如果没有则使用 userId 作为标识
|
|
req.openid = user.openid || user._id.toString()
|
|
next()
|
|
} catch (error) {
|
|
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: '认证令牌无效或已过期'
|
|
})
|
|
}
|
|
next(error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 可选的用户认证中间件(不强制要求登录)
|
|
*/
|
|
const optionalAuthMiddleware = async (req, res, next) => {
|
|
try {
|
|
const authHeader = req.header('Authorization')
|
|
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
const token = authHeader.replace('Bearer ', '')
|
|
const decoded = jwt.verify(token, process.env.JWT_SECRET)
|
|
const user = await User.findById(decoded.userId)
|
|
if (user) {
|
|
req.user = user
|
|
req.openid = user.openid
|
|
}
|
|
}
|
|
next()
|
|
} catch (error) {
|
|
// 即使认证失败也继续,只是不附加用户信息
|
|
next()
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
authMiddleware,
|
|
requirePermission,
|
|
userAuthMiddleware,
|
|
optionalAuthMiddleware
|
|
}
|