Springboot + AOP + 注解做全局日志拦截并且排除敏感属性以及接口相应时间

前言

我们在使用接口的时候需要有日志记录,然后我们也希望一些敏感参数不要出现在我们的日志里面保护用户的隐私

废话不多说我们直接上代码开干

依赖的选择,我们可以直接使用Springboot继承的aop依赖,可以单独导入我们aop依赖织入包
然后我们还需要导入阿里巴巴的fastjson包

<dependency>
	<groupId>com.alibaba.fastjson2</groupId>
	<artifactId>fastjson2</artifactId>
	<version>2.0.29</version>
</dependency>

<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.9.6</version>
</dependency>

1、首先我们需要定义一个日志注解

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

    /**
     * 操作的模块
     */
    public String businessType() default "";

    /**
     * 排除指定的请求参数
     */
    public String[] excludeParamNames() default {};
}

2、我们需要去定义我们的切面类

/**
 * description : 操作日志记录处理
 *
 * @author : 沐光
 * @date : 2024-08-28
 */
@Aspect
@Component
public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

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

    // 计算操作消耗时间
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<>("cost time");


    // 定义切面
    @Pointcut("@annotation(com.wky.book.common.annotation.SystemLog)")
    public void pointCut() {}

    // 处理请求之前
    @Before( value = "@annotation(systemLog)")
    public void before(JoinPoint joinPoint, SystemLog systemLog) throws Exception
    {
        TIME_THREADLOCAL.set(System.currentTimeMillis());
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Optional.of(requestAttributes.getRequest()).get();
        String businessType = systemLog.businessType();

        logger.info("+++++++++++++++++++++++++++++++++++ Start +++++++++++++++++++++++++++++++++++");
        // 打印描述信息
        logger.info("Description    : {}", businessType);
        // 打印请求 url
        logger.info("URL            : {}", request.getRequestURL().toString());
        // 打印 Http method
        logger.info("HTTP Method    : {}", request.getMethod());
        // 打印请求的接口以及类型
        logger.info("request path   : {}", request.getRequestURI());
        // 打印调用 controller 的全路径以及执行方法
        logger.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        // 打印请求的 IP
        logger.info("IP             : {}", request.getRemoteAddr());
        // 打印请求入参,我们需要排除掉我们的敏感信息,首先从我们的注解从获取参数
        logger.info("Request Args   : {}", argsArrayToString(joinPoint.getArgs(), systemLog.excludeParamNames()));
    }


    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable
    {
        Object result = point.proceed();
        logger.info("Response Args  : {}", JSON.toJSON(result));
        logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - TIME_THREADLOCAL.get());
        return result;
    }

    /**
     * description 排除敏感信息,并且返回字符串
     * @author 沐光
     * @date 2024/8/28
     */
    private String argsArrayToString(Object[] args, String[] excludeParamNames)
    {
        String params = "";
        if (args != null && args.length > 0){
            for (Object arg : args) {
                if (!StringUtils.isEmpty(arg) && !isFilterObject(arg)){
                    try{
                        String jsonString = JSON.toJSONString(arg, excludePropertyPreFilter(excludeParamNames));
                        params += jsonString + " ";
                    }catch (Exception e){

                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * description 忽略敏感属性
     * @return com.wky.book.common.filter.PropertyPreExcludeFilter
     * @author 沐光
     * @date 2024/8/28
     */
    private PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) {
        return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(excludeParamNames, EXCLUDE_PROPERTIES));
    }

    /**
     * description 判断这个参数是不是要过滤的,如果是我们返回true
     * @author 沐光
     * @date 2024/8/28
     */
    @SuppressWarnings("rawtypes")
    private boolean isFilterObject(final Object arg)
    {
        Class<?> clazz = arg.getClass();
        if (clazz.isArray()){
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) arg;
            for (Object value : collection)
            {
                return value instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) arg;
            for (Object value : map.entrySet())
            {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return arg instanceof MultipartFile || arg instanceof HttpServletRequest || arg instanceof HttpServletResponse || arg instanceof BindingResult;
    }

}

3、最终的我们需要去定义一个我们过滤敏感属性的过滤器

因为我们使用了fastjson里面的一个方法去过滤我们敏感字段,所以我们需要去创建这个过滤器

/**
 * description : 排除敏感信息
 *
 * @author : 沐光
 * @date : 2024-08-28
 */
public class PropertyPreExcludeFilter extends SimplePropertyPreFilter
{
    public PropertyPreExcludeFilter()
    {
    }

    public PropertyPreExcludeFilter addExcludes(String... filters)
    {
        for (int i = 0; i < filters.length; i++)
        {
            this.getExcludes().add(filters[i]);
        }
        return this;
    }
}

4、总结

如果大家感兴趣的话可以去看一下fastjson的源码,主要是去看这个一行代码
String jsonString = JSON.toJSONString(arg, excludePropertyPreFilter(excludeParamNames));
里面的这个类方法大家会明白了!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值