日志审计功能实现

Posted 孤丨焰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日志审计功能实现相关的知识,希望对你有一定的参考价值。

1. 前言

日志审计功能就是将用户进行的增加、修改和删除操作内容、操作方法、操作人以及操作时间等统一格式后集中放入数据库存储,这样做是为了提高系统的安全性,方便系统发生事故后的溯源和恢复。

2. 日志审计实现

2.1 设计数据库

 下图为数据库中的结构:

2.2 自定义注解

 这里我们需要自己设计一个注解,作为AOP的切点,当执行被注解标记的方法时,则会进入AOP切面执行日志入库的操作。

 示例如下:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.lang.annotation.*;

@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface LogRecord 
    String module() default ""; // 操作模块
    String type() default "";  // 操作类型
    String desc() default "";  // 操作说明
    Class mapperClass() default BaseMapper.class; // 服务类

2.3 设计获取IOC容器的工具类

 具体IOC容器获取的方法请参考我的上一篇博客:Springboot获取IOC容器的方式

 示例如下:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextUtil implements ApplicationContextAware 

    private static ApplicationContext applicationContext;

    public SpringContextUtil() 
    

    /**
     * 设置上下文
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
        if (SpringContextUtil.applicationContext == null) 
            SpringContextUtil.applicationContext = applicationContext;
        

    

    /**
     * 获取上下文
     * @return
     */
    public static ApplicationContext getApplicationContext() 
        return applicationContext;
    

    /**
     * 通过名字获取上下文中的bean
     * @param name
     * @return
     */
    public static Object getBean(String name)
        return applicationContext.getBean(name);
    

    /**
     * 通过类型获取上下文中的bean
     * @param requiredType
     * @return
     */
    public static Object getBean(Class<?> requiredType)
        return applicationContext.getBean(requiredType);
    

2.4 标记需要审计的方法

 我们选择将LogRecord注解标注在Service层的方法上,只要Service层被标注的方法执行就会进行日志审计并入库。

 示例如下:

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class TestServiceImpl implements TestService 
    @Autowired
    private TestMapper testMapper;

    /**
     * 根据id修改testEntity中的数据
     * @param testEntity 测试用的实体类
     * @return
     */
    @LogRecord(module = "测试模块", type = "修改", desc = "根据testEntity中的id修改数据", mapperClass = TestMapper.class)
    public boolean updateTest(TestEntity testEntity) 
        int result = 0;
        try 
            result = testMapper.updateById(testEntity);
         catch (Exception e) 
            log.error("updateTest insert error:", e.toString());
        

        return result > 0;
    

2.5 使用AOP将操作记录入库

 最后,我们只需要利用前面铺垫的IOC容器即可实现入库操作。

 示例如下:

import com.google.gson.Gson;
import com.jd.jdt.kg.domain.entity.OperLog;
import com.jd.jdt.kg.service.context.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.lang.reflect.Method;

@Aspect
@Component
@Slf4j
public class ValidationAspect 

    @Autowired
    private LogAuditMapper logAuditMapper;      // 用于向日志审计库中存放数据

    @Pointcut("@annotation(com.guyan.entity.LogRecord)")
    public void logRecordPointCut() 
    

    @AfterReturning(value = "logRecordPointCut()", returning = "result")
    public void afterReturning(ProceedingJoinPoint joinPoint, Object result) throws Throwable 
        log.info("logRecord after begin!");
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperLog operLog = method.getAnnotation(OperLog.class);
        // 用于json格式转换
        Gson gson = new Gson();
        // Mapper层类类
        Class mapperClass = operLog.mapperClass();
        // mapper层实例名
        String simpleNameToLower = mapperClass.getSimpleName().substring(0,1).toLowerCase().concat(mapperClass.getSimpleName().substring(1));
        // 从Spring的容器中获取实例
        Object instance = SpringContextUtil.getApplicationContext().getBean(simpleNameToLower);
        // 获取到要执行的方法
        Method selectById = mapperClass.getMethod("selectById", Serializable.class);
        // 类名
        String className = joinPoint.getTarget().getClass().getName();
        // 方法名
        String methodName = method.getName();
        // 创建日志实体
        LogAuditEntity logAuditEntity = new LogAuditEntity();
        logAuditEntity.setMethod(className + "." + methodName);
        logAuditEntity.setMethodReq(gson.toJson(joinPoint.getArgs()));
        logAuditEntity.setModule(operLog.operModule());
        logAuditEntity.setType(operLog.operType());
        logAuditEntity.setDesc(operLog.operDesc());
        // 根据方法返回值截取id值
        Long id = getIdByReturn(result);
        Object invoke = selectById.invoke(instance, id);
        if (invoke == null) 
            invoke = "";
        
        logAuditEntity.setReturn(gson.toJson(invoke));

        try 
            logAuditMapper.insert(operLogEntity);
         catch (Throwable e) 
            log.error("logRecord Around error:", gson.toJson(e));
        

        log.info("logRecord after end!");
    

    /**
     * 根据方法返回值截取id
     * @param result
     * @return
     */
    private Long getIdByReturn(Object result) 
        Long id = null;
        // 由于每个人的方法返回值结构不同,故此方法体内容根据返回值结构自己更改
        return id;
    


 大家可以根据自己需求自定义getIdByReturn方法、LogAuditMapper类和LogAuditEntity类。

3. 结尾

都看到最后了,求求大家点个赞再走吧!你的支持是博主创作的最大动力。

以上是关于日志审计功能实现的主要内容,如果未能解决你的问题,请参考以下文章

日志审计功能实现

日志审计功能实现

SpringBoot使用SpringAOP实现日志审计等功能

达梦数据库审计

达梦数据库审计

达梦数据库审计