部署保险端项目和大屏
This commit is contained in:
212
insurance_backend/middleware/auth.js
Normal file
212
insurance_backend/middleware/auth.js
Normal file
@@ -0,0 +1,212 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const User = require('../models/User');
|
||||
const Role = require('../models/Role');
|
||||
const responseFormat = require('../utils/response');
|
||||
|
||||
// JWT认证中间件
|
||||
const jwtAuth = async (req, res, next) => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
console.log('Authorization header:', authHeader);
|
||||
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json(responseFormat.error('未提供有效的认证token'));
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7);
|
||||
console.log('提取的token:', token);
|
||||
console.log('token类型:', typeof token);
|
||||
console.log('token长度:', token.length);
|
||||
|
||||
// 首先尝试固定token验证
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
fixed_token: token,
|
||||
status: 'active'
|
||||
},
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'role',
|
||||
attributes: ['id', 'name', 'permissions']
|
||||
}]
|
||||
});
|
||||
|
||||
if (user) {
|
||||
// 固定token验证成功
|
||||
req.user = {
|
||||
id: user.id,
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
role_id: user.role_id,
|
||||
role: user.role,
|
||||
permissions: user.role ? user.role.permissions : [],
|
||||
type: 'fixed_token'
|
||||
};
|
||||
return next();
|
||||
}
|
||||
|
||||
// 如果固定token验证失败,尝试JWT验证
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
|
||||
if (decoded.type !== 'access') {
|
||||
return res.status(401).json(responseFormat.error('Token类型错误'));
|
||||
}
|
||||
|
||||
// 验证用户是否存在且状态正常
|
||||
const jwtUser = await User.findOne({
|
||||
where: {
|
||||
id: decoded.id,
|
||||
status: 'active'
|
||||
},
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'role',
|
||||
attributes: ['id', 'name', 'permissions']
|
||||
}]
|
||||
});
|
||||
|
||||
if (!jwtUser) {
|
||||
return res.status(401).json(responseFormat.error('用户不存在或已被禁用'));
|
||||
}
|
||||
|
||||
req.user = {
|
||||
id: decoded.id,
|
||||
userId: decoded.id,
|
||||
username: decoded.username,
|
||||
role_id: decoded.role_id,
|
||||
role: jwtUser.role,
|
||||
permissions: decoded.permissions || (jwtUser.role ? jwtUser.role.permissions : []),
|
||||
type: 'jwt'
|
||||
};
|
||||
|
||||
return next();
|
||||
} catch (jwtError) {
|
||||
if (jwtError.name === 'TokenExpiredError') {
|
||||
return res.status(401).json(responseFormat.error('Token已过期', 'TOKEN_EXPIRED'));
|
||||
} else if (jwtError.name === 'JsonWebTokenError') {
|
||||
return res.status(401).json(responseFormat.error('Token无效'));
|
||||
}
|
||||
throw jwtError;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Token验证错误:', error);
|
||||
return res.status(500).json(responseFormat.error('服务器内部错误'));
|
||||
}
|
||||
};
|
||||
|
||||
// 权限检查中间件
|
||||
const checkPermission = (resource, action) => {
|
||||
return async (req, res, next) => {
|
||||
try {
|
||||
console.log(`权限检查 - 资源: ${resource}, 操作: ${action}, 用户:`, req.user);
|
||||
const user = req.user;
|
||||
|
||||
if (!user || !user.role_id) {
|
||||
console.log('权限检查失败 - 用户角色信息缺失');
|
||||
return res.status(403).json(responseFormat.error('用户角色信息缺失'));
|
||||
}
|
||||
|
||||
let permissions = [];
|
||||
|
||||
// 优先使用JWT中的权限信息
|
||||
if (user.permissions) {
|
||||
if (typeof user.permissions === 'string') {
|
||||
try {
|
||||
permissions = JSON.parse(user.permissions);
|
||||
} catch (e) {
|
||||
console.log('JWT权限解析失败:', e.message);
|
||||
permissions = [];
|
||||
}
|
||||
} else if (Array.isArray(user.permissions)) {
|
||||
permissions = user.permissions;
|
||||
}
|
||||
}
|
||||
|
||||
const requiredPermission = `${resource}:${action}`;
|
||||
|
||||
// 首先检查JWT中的权限
|
||||
let hasPermission = permissions.includes(requiredPermission) ||
|
||||
permissions.includes('*:*') ||
|
||||
permissions.includes('*');
|
||||
|
||||
// 如果JWT中没有权限信息,或者JWT权限不足,从数据库查询最新权限
|
||||
if (permissions.length === 0 || !hasPermission) {
|
||||
console.log('JWT权限不足或为空,从数据库获取最新权限...');
|
||||
const { Role, RolePermission, Permission } = require('../models');
|
||||
const userRole = await Role.findByPk(user.role_id);
|
||||
|
||||
if (!userRole) {
|
||||
console.log('权限检查失败 - 用户角色不存在, role_id:', user.role_id);
|
||||
return res.status(403).json(responseFormat.error('用户角色不存在'));
|
||||
}
|
||||
|
||||
// 从RolePermission表获取权限
|
||||
const rolePermissions = await RolePermission.findAll({
|
||||
where: {
|
||||
role_id: user.role_id,
|
||||
granted: true
|
||||
},
|
||||
include: [{
|
||||
model: Permission,
|
||||
as: 'permission',
|
||||
attributes: ['code']
|
||||
}]
|
||||
});
|
||||
|
||||
permissions = rolePermissions.map(rp => rp.permission.code);
|
||||
console.log('从RolePermission表获取的最新权限:', permissions);
|
||||
|
||||
// 重新检查权限
|
||||
hasPermission = permissions.includes(requiredPermission) ||
|
||||
permissions.includes('*:*') ||
|
||||
permissions.includes('*');
|
||||
}
|
||||
|
||||
console.log('权限检查 - 用户权限:', permissions, '需要权限:', requiredPermission, '是否有权限:', hasPermission);
|
||||
|
||||
// 检查权限或超级管理员权限
|
||||
if (!hasPermission) {
|
||||
console.log('权限检查失败 - 权限不足');
|
||||
return res.status(403).json(responseFormat.error('权限不足'));
|
||||
}
|
||||
|
||||
console.log('权限检查通过');
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(500).json(responseFormat.error('权限验证失败'));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 可选认证中间件(不强制要求认证)
|
||||
const optionalAuth = (req, res, next) => {
|
||||
const token = req.headers.authorization?.replace('Bearer ', '');
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
req.user = decoded;
|
||||
} catch (error) {
|
||||
// 令牌无效,但不阻止请求
|
||||
console.warn('可选认证令牌无效:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
// 别名导出以匹配路由中的使用
|
||||
const authenticateToken = jwtAuth;
|
||||
const requirePermission = (permission) => {
|
||||
const [resource, action] = permission.split(':');
|
||||
return checkPermission(resource, action);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
jwtAuth,
|
||||
checkPermission,
|
||||
optionalAuth,
|
||||
authenticateToken,
|
||||
requirePermission
|
||||
};
|
||||
119
insurance_backend/middleware/fixedTokenAuth.js
Normal file
119
insurance_backend/middleware/fixedTokenAuth.js
Normal file
@@ -0,0 +1,119 @@
|
||||
const User = require('../models/User');
|
||||
const Role = require('../models/Role');
|
||||
|
||||
/**
|
||||
* 固定Token认证中间件
|
||||
* 支持JWT token和固定token两种认证方式
|
||||
*/
|
||||
const fixedTokenAuth = async (req, res, next) => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
if (!authHeader) {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
message: '未提供认证token'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查是否为Bearer token格式
|
||||
if (authHeader.startsWith('Bearer ')) {
|
||||
const token = authHeader.substring(7);
|
||||
|
||||
// 首先尝试固定token验证
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
fixed_token: token,
|
||||
status: 'active'
|
||||
},
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'role',
|
||||
attributes: ['id', 'name', 'permissions']
|
||||
}]
|
||||
});
|
||||
|
||||
if (user) {
|
||||
// 固定token验证成功
|
||||
req.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
role_id: user.role_id,
|
||||
role: user.role,
|
||||
permissions: user.role ? user.role.permissions : []
|
||||
};
|
||||
return next();
|
||||
}
|
||||
|
||||
// 如果固定token验证失败,尝试JWT验证
|
||||
const jwt = require('jsonwebtoken');
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
|
||||
if (decoded.type !== 'access') {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
message: 'Token类型错误'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证用户是否存在且状态正常
|
||||
const jwtUser = await User.findOne({
|
||||
where: {
|
||||
id: decoded.userId,
|
||||
status: 'active'
|
||||
},
|
||||
include: [{
|
||||
model: Role,
|
||||
as: 'role',
|
||||
attributes: ['id', 'name', 'permissions']
|
||||
}]
|
||||
});
|
||||
|
||||
if (!jwtUser) {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
message: '用户不存在或已被禁用'
|
||||
});
|
||||
}
|
||||
|
||||
req.user = {
|
||||
id: jwtUser.id,
|
||||
username: jwtUser.username,
|
||||
role_id: jwtUser.role_id,
|
||||
role: jwtUser.role,
|
||||
permissions: decoded.permissions || (jwtUser.role ? jwtUser.role.permissions : [])
|
||||
};
|
||||
|
||||
return next();
|
||||
} catch (jwtError) {
|
||||
if (jwtError.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
code: 'TOKEN_EXPIRED',
|
||||
message: 'Token已过期'
|
||||
});
|
||||
} else if (jwtError.name === 'JsonWebTokenError') {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
message: 'Token无效'
|
||||
});
|
||||
}
|
||||
throw jwtError;
|
||||
}
|
||||
} else {
|
||||
return res.status(401).json({
|
||||
status: 'error',
|
||||
message: 'Token格式错误,请使用Bearer格式'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Token验证错误:', error);
|
||||
return res.status(500).json({
|
||||
status: 'error',
|
||||
message: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = fixedTokenAuth;
|
||||
302
insurance_backend/middleware/operationLogger.js
Normal file
302
insurance_backend/middleware/operationLogger.js
Normal file
@@ -0,0 +1,302 @@
|
||||
const { OperationLog } = require('../models');
|
||||
|
||||
/**
|
||||
* 操作日志记录中间件
|
||||
* 自动记录用户的操作行为
|
||||
*/
|
||||
class OperationLogger {
|
||||
/**
|
||||
* 记录操作日志的中间件
|
||||
*/
|
||||
static logOperation(options = {}) {
|
||||
return async (req, res, next) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
// 保存原始的res.json方法
|
||||
const originalJson = res.json;
|
||||
|
||||
// 重写res.json方法以捕获响应数据
|
||||
res.json = function(data) {
|
||||
const endTime = Date.now();
|
||||
const executionTime = endTime - startTime;
|
||||
|
||||
// 异步记录操作日志,不阻塞响应
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
await OperationLogger.recordLog(req, res, data, executionTime, options);
|
||||
} catch (error) {
|
||||
console.error('记录操作日志失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 调用原始的json方法
|
||||
return originalJson.call(this, data);
|
||||
};
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录操作日志
|
||||
*/
|
||||
static async recordLog(req, res, responseData, executionTime, options) {
|
||||
try {
|
||||
// 如果用户未登录,不记录日志
|
||||
if (!req.user || !req.user.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取操作类型
|
||||
const operationType = OperationLogger.getOperationType(req.method, req.url, options);
|
||||
|
||||
// 获取操作模块
|
||||
const operationModule = OperationLogger.getOperationModule(req.url, options);
|
||||
|
||||
// 获取操作内容
|
||||
const operationContent = OperationLogger.getOperationContent(req, operationType, operationModule, options);
|
||||
|
||||
// 获取操作目标
|
||||
const operationTarget = OperationLogger.getOperationTarget(req, responseData, options);
|
||||
|
||||
// 获取操作状态
|
||||
const status = OperationLogger.getOperationStatus(res.statusCode, responseData);
|
||||
|
||||
// 获取错误信息
|
||||
const errorMessage = OperationLogger.getErrorMessage(responseData, status);
|
||||
|
||||
// 记录操作日志
|
||||
await OperationLog.logOperation({
|
||||
user_id: req.user.id,
|
||||
operation_type: operationType,
|
||||
operation_module: operationModule,
|
||||
operation_content: operationContent,
|
||||
operation_target: operationTarget,
|
||||
request_method: req.method,
|
||||
request_url: req.originalUrl || req.url,
|
||||
request_params: {
|
||||
query: req.query,
|
||||
body: OperationLogger.sanitizeRequestBody(req.body),
|
||||
params: req.params
|
||||
},
|
||||
response_status: res.statusCode,
|
||||
response_data: OperationLogger.sanitizeResponseData(responseData),
|
||||
ip_address: OperationLogger.getClientIP(req),
|
||||
user_agent: req.get('User-Agent') || '',
|
||||
execution_time: executionTime,
|
||||
status: status,
|
||||
error_message: errorMessage
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('记录操作日志时发生错误:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作类型
|
||||
*/
|
||||
static getOperationType(method, url, options) {
|
||||
if (options.operation_type) {
|
||||
return options.operation_type;
|
||||
}
|
||||
|
||||
// 根据URL和HTTP方法推断操作类型
|
||||
if (url.includes('/login')) return 'login';
|
||||
if (url.includes('/logout')) return 'logout';
|
||||
if (url.includes('/export')) return 'export';
|
||||
if (url.includes('/import')) return 'import';
|
||||
if (url.includes('/approve')) return 'approve';
|
||||
if (url.includes('/reject')) return 'reject';
|
||||
|
||||
switch (method.toUpperCase()) {
|
||||
case 'GET':
|
||||
return 'view';
|
||||
case 'POST':
|
||||
return 'create';
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
return 'update';
|
||||
case 'DELETE':
|
||||
return 'delete';
|
||||
default:
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作模块
|
||||
*/
|
||||
static getOperationModule(url, options) {
|
||||
if (options.operation_module) {
|
||||
return options.operation_module;
|
||||
}
|
||||
|
||||
// 从URL中提取模块名
|
||||
const pathSegments = url.split('/').filter(segment => segment && segment !== 'api');
|
||||
if (pathSegments.length > 0) {
|
||||
return pathSegments[0].replace(/-/g, '_');
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作内容
|
||||
*/
|
||||
static getOperationContent(req, operationType, operationModule, options) {
|
||||
if (options.operation_content) {
|
||||
return options.operation_content;
|
||||
}
|
||||
|
||||
const actionMap = {
|
||||
'login': '用户登录',
|
||||
'logout': '用户退出',
|
||||
'view': '查看',
|
||||
'create': '创建',
|
||||
'update': '更新',
|
||||
'delete': '删除',
|
||||
'export': '导出',
|
||||
'import': '导入',
|
||||
'approve': '审批通过',
|
||||
'reject': '审批拒绝'
|
||||
};
|
||||
|
||||
const moduleMap = {
|
||||
'users': '用户',
|
||||
'roles': '角色',
|
||||
'insurance': '保险',
|
||||
'policies': '保单',
|
||||
'claims': '理赔',
|
||||
'system': '系统',
|
||||
'operation_logs': '操作日志',
|
||||
'devices': '设备',
|
||||
'device_alerts': '设备告警'
|
||||
};
|
||||
|
||||
const action = actionMap[operationType] || operationType;
|
||||
const module = moduleMap[operationModule] || operationModule;
|
||||
|
||||
return `${action}${module}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作目标
|
||||
*/
|
||||
static getOperationTarget(req, responseData, options) {
|
||||
if (options.operation_target) {
|
||||
return options.operation_target;
|
||||
}
|
||||
|
||||
// 尝试从请求参数中获取ID
|
||||
if (req.params.id) {
|
||||
return `ID: ${req.params.id}`;
|
||||
}
|
||||
|
||||
// 尝试从响应数据中获取信息
|
||||
if (responseData && responseData.data) {
|
||||
if (responseData.data.id) {
|
||||
return `ID: ${responseData.data.id}`;
|
||||
}
|
||||
if (responseData.data.name) {
|
||||
return `名称: ${responseData.data.name}`;
|
||||
}
|
||||
if (responseData.data.username) {
|
||||
return `用户: ${responseData.data.username}`;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作状态
|
||||
*/
|
||||
static getOperationStatus(statusCode, responseData) {
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return 'success';
|
||||
} else if (statusCode >= 400 && statusCode < 500) {
|
||||
return 'failed';
|
||||
} else {
|
||||
return 'error';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*/
|
||||
static getErrorMessage(responseData, status) {
|
||||
if (status === 'success') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (responseData && responseData.message) {
|
||||
return responseData.message;
|
||||
}
|
||||
|
||||
if (responseData && responseData.error) {
|
||||
return responseData.error;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理请求体数据(移除敏感信息)
|
||||
*/
|
||||
static sanitizeRequestBody(body) {
|
||||
if (!body || typeof body !== 'object') {
|
||||
return body;
|
||||
}
|
||||
|
||||
const sanitized = { ...body };
|
||||
const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth'];
|
||||
|
||||
sensitiveFields.forEach(field => {
|
||||
if (sanitized[field]) {
|
||||
sanitized[field] = '***';
|
||||
}
|
||||
});
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理响应数据(移除敏感信息)
|
||||
*/
|
||||
static sanitizeResponseData(data) {
|
||||
if (!data || typeof data !== 'object') {
|
||||
return data;
|
||||
}
|
||||
|
||||
// 只保留基本的响应信息,不保存完整的响应数据
|
||||
return {
|
||||
status: data.status,
|
||||
message: data.message,
|
||||
code: data.code
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP地址
|
||||
*/
|
||||
static getClientIP(req) {
|
||||
return req.ip ||
|
||||
req.connection.remoteAddress ||
|
||||
req.socket.remoteAddress ||
|
||||
(req.connection.socket ? req.connection.socket.remoteAddress : null) ||
|
||||
'127.0.0.1';
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建特定操作的日志记录器
|
||||
*/
|
||||
static createLogger(operationType, operationModule, operationContent) {
|
||||
return OperationLogger.logOperation({
|
||||
operation_type: operationType,
|
||||
operation_module: operationModule,
|
||||
operation_content: operationContent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OperationLogger;
|
||||
Reference in New Issue
Block a user