完善保险端前后端
This commit is contained in:
@@ -11,10 +11,22 @@ const jwtAuth = (req, res, next) => {
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
|
||||
// 检查Token类型,只接受访问令牌
|
||||
if (decoded.type && decoded.type !== 'access') {
|
||||
return res.status(401).json(responseFormat.error('无效的令牌类型'));
|
||||
}
|
||||
|
||||
req.user = decoded;
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(401).json(responseFormat.error('认证令牌无效或已过期'));
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
return res.status(401).json(responseFormat.error('认证令牌已过期', 'TOKEN_EXPIRED'));
|
||||
} else if (error.name === 'JsonWebTokenError') {
|
||||
return res.status(401).json(responseFormat.error('认证令牌无效', 'TOKEN_INVALID'));
|
||||
} else {
|
||||
return res.status(401).json(responseFormat.error('认证失败', 'AUTH_FAILED'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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