添加日志审计功能

master
xueqingkun 3 weeks ago
parent 2b6a7db695
commit 029e4124b9

@ -25,6 +25,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>

@ -0,0 +1,133 @@
package com.supervision.ai.service.hub.aop;
import cn.hutool.core.util.StrUtil;
import com.supervision.ai.service.hub.domain.AuditLog;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
@Slf4j
@Data
public class AuditLogDTO implements Serializable {
/**
* id
*/
private String id;
/**
* 0-1-
*/
private String recordType;
/**
* id
*/
private String userId;
private String appName;
/**
* ip
*/
private String ip;
/**
* GET POST PUT DELETE
*/
private String method;
/**
* url
*/
private String url;
/**
*
*/
private String requestParams;
/**
*
*/
private String response;
/**
*
*/
private String exceptionDesc;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private String createUserId;
/**
*
*/
private LocalDateTime updateTime;
/**
* id
*/
private String updateUserId;
private LocalDateTime startTime;
private LocalDateTime endTime;
public AuditLogDTO() {
}
public AuditLogDTO(HttpServletRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
this.appName = authentication.getName();
this.url = request.getRequestURI();
this.method = request.getMethod();
if (StrUtil.isNotBlank(request.getHeader("X-Real-IP"))){
this.ip = request.getHeader("X-Real-IP");
}else {
this.ip = request.getRemoteAddr();
}
this.startTime = LocalDateTime.now();
}
public AuditLog toAuditLog(){
AuditLog auditLog = new AuditLog();
auditLog.setId(id);
auditLog.setRecordType(recordType);
auditLog.setUserId(userId);
auditLog.setAppName(appName);
auditLog.setIp(ip);
auditLog.setMethod(method);
auditLog.setUrl(url);
auditLog.setRequestParams(requestParams);
auditLog.setCostTime((int)(this.evaluateCostTime()));
auditLog.setResponse(response);
auditLog.setExceptionDesc(exceptionDesc);
auditLog.setCreateTime(createTime);
auditLog.setCreateUserId(createUserId);
auditLog.setUpdateTime(updateTime);
auditLog.setUpdateUserId(updateUserId);
return auditLog;
}
public long evaluateCostTime() {
if (null == this.startTime || null == this.endTime){
return 0;
}
return ChronoUnit.MILLIS.between(this.startTime, this.endTime);
}
}

@ -0,0 +1,8 @@
package com.supervision.ai.service.hub.aop;
public class AuditLogThreadHolder {
public static final ThreadLocal<AuditLogDTO> AUDIT_LOG = new ThreadLocal<>();
}

@ -0,0 +1,210 @@
package com.supervision.ai.service.hub.aop;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.supervision.ai.service.hub.service.AuditLogService;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
*
*
* @author :liu
* create :2020-11-16 10:50
**/
@Component
@Aspect
@Slf4j
public class RequestLogAop {
@Autowired
private AuditLogService auditLogService;
@SuppressWarnings("all")
@Around("within(com..*..controller..*) && @within(org.springframework.web.bind.annotation.RestController)")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long start = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
AuditLogDTO auditLogDTO = new AuditLogDTO(request);
Map<String, Object> requestParams = getRequestParamsByProceedingJoinPoint(proceedingJoinPoint);
auditLogDTO.setRequestParams(JSONUtil.toJsonStr(requestParams));
auditLogDTO.setUserId((String) requestParams.get("userId"));
AuditLogThreadHolder.AUDIT_LOG.set(auditLogDTO);// 把数据缓存到treadLocal中
// 执行目标方法
Object result = proceedingJoinPoint.proceed();
// 目标方法执行后
RequestInfo requestInfo = new RequestInfo();
requestInfo.setUrl(request.getRequestURL().toString());
requestInfo.setHttpMethod(request.getMethod());
requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
proceedingJoinPoint.getSignature().getName()));
requestInfo.setRequestParams(requestParams);
Object response = proceedResult(result);
requestInfo.setResult(response);
requestInfo.setTimeCost(System.currentTimeMillis() - start);
log.info("Request Info: {}", JSONUtil.toJsonStr(requestInfo));
try {
auditLogDTO.setRecordType("0");
if (response instanceof String){
auditLogDTO.setResponse((String) response);
}
auditLogDTO.setEndTime(LocalDateTime.now());
auditLogService.saveAuditLog(auditLogDTO);
} catch (Exception e) {
log.error("保存审计信息出错,保存内容:{}",JSONUtil.toJsonStr(auditLogDTO), e);
}
return result;
}
private Object proceedResult(Object result) {
String value = null;
try {
value = JSONUtil.toJsonStr(result);
} catch (Exception e) {
value = result.toString();
}
if (StrUtil.isNotBlank(value) && value.length() > 10240){
value = value.substring(0, 1024) + "......" + value.substring(value.length() - 1024);
return value;
}
if (StrUtil.isNotEmpty(value)){
return value;
}
return result;
}
@SuppressWarnings("all")
@AfterThrowing(pointcut = "@within(org.springframework.web.bind.annotation.RestController)", throwing = "e")
public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
RequestErrorInfo requestErrorInfo = new RequestErrorInfo();
requestErrorInfo.setUrl(request.getRequestURL().toString());
requestErrorInfo.setHttpMethod(request.getMethod());
requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName()));
requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
requestErrorInfo.setException(e);
log.info("Error Request Info : {}", JSONUtil.toJsonStr(requestErrorInfo));
AuditLogDTO auditLogDTO = AuditLogThreadHolder.AUDIT_LOG.get();
if (null != auditLogDTO){
try {
auditLogDTO.setEndTime(LocalDateTime.now());
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
auditLogDTO.setExceptionDesc(sw.toString());
auditLogDTO.setRecordType("1");
auditLogService.saveAuditLog(auditLogDTO);
} catch (Exception ex) {
log.warn("保存审计信息出错,保存内容:{}",JSONUtil.toJsonStr(auditLogDTO), ex);
}
}
}
@After(value = "@within(org.springframework.web.bind.annotation.RestController)")
public void doAfter(JoinPoint joinPoint) {
// 清理线程变量
AuditLogThreadHolder.AUDIT_LOG.remove();
}
/**
*
*
* @param proceedingJoinPoint
* @return
*/
private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
//参数名
String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
//参数值
Object[] paramValues = proceedingJoinPoint.getArgs();
return buildRequestParam(paramNames, paramValues);
}
private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
//参数名
String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
//参数值
Object[] paramValues = joinPoint.getArgs();
return buildRequestParam(paramNames, paramValues);
}
private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
Map<String, Object> requestParams = new HashMap<>();
if (paramNames == null){
return requestParams;
}
for (int i = 0; i < paramNames.length; i++) {
Object value = paramValues[i];
if (value instanceof ServletRequest || value instanceof ServletResponse) {
//ServletRequest不能序列化从入参里排除否则报异常java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除否则报异常java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
//如果是文件对象
if (value instanceof MultipartFile) {
MultipartFile file = (MultipartFile) value;
//获取文件名
value = file.getOriginalFilename();
}
requestParams.put(paramNames[i], value);
}
return requestParams;
}
@Data
public static class RequestInfo {
private String url;
private String httpMethod;
private String classMethod;
private Object requestParams;
private Object result;
private Long timeCost;
}
@Data
public static class RequestErrorInfo {
private String url;
private String httpMethod;
private String classMethod;
private Object requestParams;
private RuntimeException exception;
}
}

@ -20,7 +20,7 @@ public class AuthController {
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
try {
String token = authService.login(request.getUsername(), request.getPassword());
String token = authService.login(request.getAppName(), request.getPassword());
Map<String, String> response = new HashMap<>();
response.put("token", token);
return ResponseEntity.ok(response);
@ -31,7 +31,7 @@ public class AuthController {
@Data
public static class LoginRequest {
private String username;
private String appName;
private String password;
}
}

@ -11,7 +11,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@RestController
@RequestMapping("/comfyui")
@RequestMapping("/model")
@RequiredArgsConstructor
public class ComfyUIController {
@ -29,9 +29,9 @@ public class ComfyUIController {
return R.ok(taskId);
}
@GetMapping("/queryHistory")
public R<List<TaskResultResVo>> queryHistory(@RequestParam(name = "promptId", required = false) String promptId) {
List<TaskResultResVo> taskResultResVos = comfyUIService.queryHistory(promptId);
@GetMapping("/getTaskById")
public R<List<TaskResultResVo>> queryHistory(@RequestParam(name = "taskId", required = false) String taskId) {
List<TaskResultResVo> taskResultResVos = comfyUIService.queryHistory(taskId);
return R.ok(taskResultResVos);
}

@ -1,7 +1,7 @@
package com.supervision.ai.service.hub.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.supervision.ai.service.hub.domain.SysUser;
import com.supervision.ai.service.hub.domain.SysApp;
import com.supervision.ai.service.hub.service.impl.SysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
@ -30,7 +30,7 @@ public class SysUserController {
public ResponseEntity<?> getCurrentUserDetails() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
SysUser user = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username));
SysApp user = sysUserService.getOne(new LambdaQueryWrapper<SysApp>().eq(SysApp::getAppName, username));
if (user == null) {
return ResponseEntity.status(404).body("用户不存在");
}

@ -0,0 +1,96 @@
package com.supervision.ai.service.hub.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Data;
/**
*
* @TableName audit_log
*/
@TableName(value ="audit_log")
@Data
public class AuditLog implements Serializable {
/**
* id
*/
@TableId
private String id;
/**
* 0-1-
*/
private String recordType;
/**
*
*/
private String appName;
/**
* id
*/
private String userId;
/**
* ip
*/
private String ip;
/**
* GET POST PUT DELETE
*/
private String method;
/**
* url
*/
private String url;
/**
*
*/
private String requestParams;
/**
* ms
*/
private Integer costTime;
/**
*
*/
private String response;
/**
*
*/
private String exceptionDesc;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private String createUserId;
/**
*
*/
private LocalDateTime updateTime;
/**
* id
*/
private String updateUserId;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

@ -7,12 +7,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
@Data
@TableName("sys_user")
public class SysUser {
@TableName("sys_app")
public class SysApp {
@TableId(type = IdType.AUTO)
private Long id; // 主键ID
private String username; // 用户名
private String appName; // 应用名称
@JsonIgnore
private String password; // 密码 (加密存储)
private String status; // 状态 (1表示正常0表示禁用)
}

@ -0,0 +1,18 @@
package com.supervision.ai.service.hub.mapper;
import com.supervision.ai.service.hub.domain.AuditLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author Administrator
* @description audit_log()Mapper
* @createDate 2025-04-08 13:21:37
* @Entity com.supervision.ai.service.hub.domain.AuditLog
*/
public interface AuditLogMapper extends BaseMapper<AuditLog> {
}

@ -1,10 +1,10 @@
package com.supervision.ai.service.hub.mapper;
import com.supervision.ai.service.hub.domain.SysUser;
import com.supervision.ai.service.hub.domain.SysApp;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
public interface SysUserMapper extends BaseMapper<SysApp> {
// 继承BaseMapper后CRUD方法可直接使用无需额外定义
}

@ -0,0 +1,15 @@
package com.supervision.ai.service.hub.service;
import com.supervision.ai.service.hub.aop.AuditLogDTO;
import com.supervision.ai.service.hub.domain.AuditLog;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author Administrator
* @description audit_log()Service
* @createDate 2025-04-08 13:21:37
*/
public interface AuditLogService extends IService<AuditLog> {
void saveAuditLog(AuditLogDTO auditLogDTO);
}

@ -0,0 +1,32 @@
package com.supervision.ai.service.hub.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.supervision.ai.service.hub.aop.AuditLogDTO;
import com.supervision.ai.service.hub.domain.AuditLog;
import com.supervision.ai.service.hub.service.AuditLogService;
import com.supervision.ai.service.hub.mapper.AuditLogMapper;
import org.springframework.stereotype.Service;
/**
* @author Administrator
* @description audit_log()Service
* @createDate 2025-04-08 13:21:37
*/
@Service
public class AuditLogServiceImpl extends ServiceImpl<AuditLogMapper, AuditLog>
implements AuditLogService{
@Override
public void saveAuditLog(AuditLogDTO auditLogDTO) {
if (null == auditLogDTO){
return;
}
AuditLog auditLog = auditLogDTO.toAuditLog();
this.save(auditLog);
}
}

@ -2,7 +2,7 @@ package com.supervision.ai.service.hub.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.supervision.ai.service.hub.domain.SysUser;
import com.supervision.ai.service.hub.domain.SysApp;
import com.supervision.ai.service.hub.mapper.SysUserMapper;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.GrantedAuthority;
@ -16,10 +16,9 @@ import java.util.Collections;
import java.util.List;
import static com.supervision.ai.service.hub.constant.UserConstant.USER_STATUS_DISABLED;
import static com.supervision.ai.service.hub.constant.UserConstant.USER_STATUS_ENABLED;
@Service
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> implements UserDetailsService {
public class SysUserService extends ServiceImpl<SysUserMapper, SysApp> implements UserDetailsService {
/**
*
@ -31,7 +30,7 @@ public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> implemen
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser user = this.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username));
SysApp user = this.getOne(new LambdaQueryWrapper<SysApp>().eq(SysApp::getAppName, username));
if (user == null) {
throw new UsernameNotFoundException("用户不存在: " + username);
}
@ -42,7 +41,7 @@ public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> implemen
// **扩展点**:如需加载用户角色权限,可在此处查询 sys_user_role 表关联的角色,并将角色加入 authorities 列表
List<GrantedAuthority> authorities = Collections.emptyList();
// 使用Spring Security提供的User对象作为UserDetails返回
return new User(user.getUsername(), user.getPassword(), authorities);
return new User(user.getAppName(), user.getPassword(), authorities);
}
}

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.supervision.ai.service.hub.mapper.AuditLogMapper">
<resultMap id="BaseResultMap" type="com.supervision.ai.service.hub.domain.AuditLog">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="recordType" column="record_type" jdbcType="VARCHAR"/>
<result property="appName" column="app_name" jdbcType="VARCHAR"/>
<result property="userId" column="user_id" jdbcType="VARCHAR"/>
<result property="ip" column="ip" jdbcType="VARCHAR"/>
<result property="method" column="method" jdbcType="VARCHAR"/>
<result property="url" column="url" jdbcType="VARCHAR"/>
<result property="requestParams" column="request_params" jdbcType="VARCHAR"/>
<result property="costTime" column="cost_time" jdbcType="INTEGER"/>
<result property="response" column="response" jdbcType="VARCHAR"/>
<result property="exceptionDesc" column="exception_desc" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="createUserId" column="create_user_id" jdbcType="VARCHAR"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="updateUserId" column="update_user_id" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,record_type,app_name,
user_id,ip,
method,url,request_params,
cost_time,response,exception_desc,
create_time,create_user_id,update_time,
update_user_id
</sql>
</mapper>
Loading…
Cancel
Save