SpringBoot-AOP记录日志+脱敏

1. 引入依赖

//脱敏工具包
implementation 'com.github.houbb:sensitive-core:0.0.9'
implementation 'com.alibaba:fastjson:1.2.75'

2. 配置文件

  • application.properties中配置需要脱敏的字段名称
exclude.properties=name,phoneNo,password

3. 注解类

package com.example.fisher.gradledemo.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({
    
    ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    
    

}

4. 切面类

  • 提供2种方式脱敏
  • fastjson脱敏字段不显示,sensitive-core将带有脱敏注解的属性字段部分显示***
package com.example.fisher.gradledemo.aspectj;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.example.fisher.gradledemo.dto.LogInfo;
import com.github.houbb.sensitive.core.api.SensitiveUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
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.HashMap;
import java.util.Map;
import java.util.Set;

@Slf4j
@Component
@Aspect
public class LogAspect {
    
    

    /**
     * 配置需要脱敏的字段名
     */
    @Value("${exclude.properties}")
    private Set<String> properties;

    public static final String DESENSITISE_MSG = "******";

    /**
     * 忽略敏感属性
     */
    public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter() {
    
    
        return new PropertyPreFilters().addFilter().addExcludes(properties.toArray(String[]::new));
    }

    /**
     * 包路径
     */
    @Pointcut("execution(public * com.example.fisher.gradledemo.sysuser.controller.*.*(..))")
    public void pc1() {
    
    }

    /**
     * 注解
     */
    @Pointcut("@annotation(com.example.fisher.gradledemo.annotation.Log)")
    public void pc2() {
    
    }

    @Around("pc1()||pc2()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
    
    
        // 入参使用fastjson进行脱敏,脱敏字段不显示
        LogInfo loginfo = getLoginfo(point);
        Object result = point.proceed();
        // 返回值使用sensitive-core工具包进行脱敏,脱敏字段部分显示***
        Object deSensitiveObject = getDeSensitiveObject2(null, result);
        loginfo.setResult(String.valueOf(deSensitiveObject));
        log.info("{}", loginfo);
        return result;
    }

    /**
     * 记录异常日志
     */
    @AfterThrowing(value = "pc1()||pc2()", throwing = "e")
    public void afterThrowing(JoinPoint point, Throwable e) {
    
    
        LogInfo loginfo = getLoginfo(point);
        log.error("{},{}", loginfo, e);
    }

    /**
     * 需要打印的日志信息
     * 
     * @param point
     * @return
     */
    private LogInfo getLoginfo(JoinPoint point) {
    
    
        String className = point.getTarget().getClass().getName();
        MethodSignature signature = (MethodSignature)point.getSignature();
        String methodName = signature.getName();
        HttpServletRequest request = getRequestAttributes().getRequest();
        LogInfo logInfo = new LogInfo();
        logInfo.setClassName(className);
        logInfo.setMethodName(methodName);
        logInfo.setUrl(request.getRequestURI());
        logInfo.setHttpMethod(request.getMethod());
        logInfo.setIpAddress(request.getRemoteAddr());
        // 获取入参
        Object[] args = point.getArgs();
        String[] parameterNames = signature.getParameterNames();
        Map<String, Object> inputParam = getInputParam(parameterNames, args);
        logInfo.setParameter(String.valueOf(inputParam));
        return logInfo;
    }

    /**
     * 获取入参
     * 
     * @param parameterNames
     * @param args
     * @return
     */
    private Map<String, Object> getInputParam(String[] parameterNames, Object[] args) {
    
    
        Map<String, Object> map = new HashMap<>(16);
        for (int i = 0; i < parameterNames.length; i++) {
    
    
            String parameterName = parameterNames[i];
            Object arg = args[i];
            Object deSensitiveObject = getDeSensitiveObject(parameterName, arg);
            map.put(parameterName, deSensitiveObject);
        }
        return map;
    }

    /**
     * 参数脱敏,如果是对象使用fastjson根据脱敏字段进行脱敏,字段不显示
     *
     * @param parameterName
     * @param arg
     * @return
     */
    private Object getDeSensitiveObject(String parameterName, Object arg) {
    
    
        Class<?> clazz = arg.getClass();
        if (arg instanceof MultipartFile || arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
    
    
            return "";
        } else if (String.class.isAssignableFrom(clazz)) {
    
    
            if (properties.contains(parameterName)) {
    
    
                return DESENSITISE_MSG;
            }
        } else if (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz)) {
    
    
            arg = JSONObject.toJSONString(arg, excludePropertyPreFilter());
        } else {
    
    
            arg = JSONObject.toJSONString(arg, excludePropertyPreFilter());
        }
        return arg;
    }

    /**
     * 参数脱敏,对象使用sensitive-core工具包进行脱敏,不同注解使用不同方式脱敏,比如密码不显示,手机号、姓名部分显示***
     *
     * @param parameterName
     * @param arg
     * @return
     */
    private Object getDeSensitiveObject2(String parameterName, Object arg) {
    
    
        Class<?> clazz = arg.getClass();
        if (arg instanceof MultipartFile || arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
    
    
            return "";
        } else if (String.class.isAssignableFrom(clazz)) {
    
    
            if (properties.contains(parameterName)) {
    
    
                return DESENSITISE_MSG;
            }
        } else if (Collection.class.isAssignableFrom(clazz) ) {
    
    
            arg = SensitiveUtil.desCopyCollection((Collection<?>)arg);
        }else if (Map.class.isAssignableFrom(clazz)){
    
    
            HashMap<Object, Object> hashMap = new HashMap<>(16);
            ((Map)arg).forEach((s, o) -> {
    
    
                if (properties.contains(String.valueOf(s))) {
    
    
                    hashMap.put(s, DESENSITISE_MSG);
                } else {
    
    
                    hashMap.put(s, o);
                }
            });
            arg = hashMap;
        }else {
    
    
            //当做对象处理
            arg = SensitiveUtil.desJson(arg);
        }
        return arg;
    }


    /**
     * 获取request对象
     * 
     * @return
     */
    public ServletRequestAttributes getRequestAttributes() {
    
    
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes)attributes;
    }

}

5. 需要脱敏的实体类

  • 在需要脱敏的属性上添加注解,例如@SensitiveStrategyChineseName,@SensitiveStrategyPhone等
  • 这里使用了mybatis-plus
package com.example.fisher.gradledemo.sysuser.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.github.houbb.sensitive.annotation.strategy.SensitiveStrategyChineseName;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

@SuppressWarnings("serial")
@Data
public class SysUser extends Model<SysUser> {
    
    

    @TableId(type = IdType.AUTO)
    private Long userId;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;

    @TableLogic
    @JsonIgnore
    private Integer delFlag;

    @SensitiveStrategyChineseName
    private String name;

    private Integer age;

    private String interest;

    /**
     * 获取主键值
     *
     * @return 主键值
     */
    @Override
    public Serializable pkVal() {
    
    
        return this.userId;
    }
    
}

6. 查看日志打印

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40977118/article/details/121797466