审计日志注解

1、针对增删改接口进行可视化日志审查

2、过滤敏感字段

package com.bytello.customizer.aspect;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.bytello.customizer.common.annotation.Log;
import com.bytello.customizer.common.enums.Op;
import com.bytello.customizer.common.pojo.entity.SysOpLogDo;
import com.bytello.customizer.common.pojo.entity.UserDo;
import com.bytello.customizer.common.utils.FastJsonUtil;
import com.bytello.customizer.common.utils.ServletUtil;
import com.bytello.customizer.holder.UserHolder;
import com.bytello.customizer.manager.AsyncManager;
import com.bytello.customizer.manager.factory.AsyncFactory;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志记录
 *
 * @author wuqinhong
 * @email wuqinhong@cvte.com
 * @date 2022-11-14 09:34:45
 */
@Aspect
@Component
public class LogAspect {

    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    /**
     * 排除敏感属性字段
     */
    public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"};

    // 配置织入点
    @Pointcut("@annotation(com.bytello.customizer.common.annotation.Log)")
    public void logPointCut() {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
        try {
            // 获取当前的用户
            UserDo currentUser = UserHolder.get();
            // *========数据库日志=========*//
            SysOpLogDo opLog = new SysOpLogDo();
            opLog.setStatus(Op.Status.SUCCESS.ordinal());
            // 请求的地址
            HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String ip = req.getRemoteAddr();
            opLog.setOpIp(ip);
            // 操作时间
            opLog.setOpTime(new Date());
            // 请求地址
            opLog.setOpUrl(StringUtils.substring(ServletUtil.getRequest().getRequestURI(), 0, 255));
            if (currentUser != null) {
                opLog.setOpName(currentUser.getAccount());
            }
            // 获取异常信息
            if (e != null) {
                opLog.setStatus(Op.Status.FAIL.ordinal());
                opLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            opLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            opLog.setRequestMethod(ServletUtil.getRequest().getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, opLog, jsonResult);
            // 保存数据库
            AsyncManager.instance().execute(AsyncFactory.recordOp(opLog));
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log   日志
     * @param opLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOpLogDo opLog, Object jsonResult) throws Exception {
        // 设置标题
        opLog.setTitle(log.title());
        //设置操作类型
        opLog.setOpType(log.opType().ordinal());
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData()) {
            // 获取参数的信息,传入到数据库中。
            //setRequestValue(joinPoint, opLog);
            setRequestBody(opLog);
        }
        // 是否需要保存response,参数和值
        if (log.isSaveResponseData() && jsonResult != null) {
            opLog.setJsonResult(StringUtils.substring(JSONObject.toJSONString(jsonResult), 0, 2000));
        }
    }

    /**
     * 获取请求体,放到log中
     *
     * @param opLog
     */
    private void setRequestBody(SysOpLogDo opLog){
        String requestJson = ServletUtil.getRequestBody();
        //过滤敏感属性
        HashMap<String, String> map = FastJsonUtil.json2Map(requestJson);
        String filtered = JSONObject.toJSONString(map, excludePropertyPreFilter());
        opLog.setOpParam(filtered);
    }

    /**
     * 忽略敏感属性
     */
    public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter() {
        return new PropertyPreFilters().addFilter().addExcludes(EXCLUDE_PROPERTIES);
    }

}