SpringBoot-AOP记录日志+脱敏

2023-11-06

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. 查看日志打印

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot-AOP记录日志+脱敏 的相关文章

随机推荐

  • 可变个数的参数

    1 用数组的方式来 例如 pulic void print String args for int i 0 i
  • Apache POI 4.1.0 发布,Office 文档的 Java API

    Apache POI 4 1 0 发布了 Apache POI 是用 Java 编写的开源跨平台的 Java API 提供 API 给 Java 程式对 Microsoft Office 格式档案读和写的功能 简而言之 你可以使用 Java
  • CSDN高校俱乐部第三届研讨会

    CSDN高校俱乐部第三届研讨会 于2013年6月6日在国家会议中心成功举办 感谢大家从全国各地远道而来参加 本次研讨会邀请了来自全国32所高校俱乐部的指导老师 同学以及优秀巡讲讲师和微软Imagine Cup 2013大赛负责人 会议开始先
  • 【MySQL高级篇笔记-数据库的设计规范(中) 】

    此笔记为尚硅谷MySQL高级篇部分内容 目录 一 为什么要数据库设计 二 范式 1 范式简介 2 范式都包括哪些 3 键和相关属性的概念 4 第一范式 1st NF 5 第二范式 2nd NF 6 第三范式 3rd NF 7 小结 三 反范
  • Flutter内存优化总结

    Flutter内存优化是一个非常复杂的问题 其中涉及多个方面的优化策略 下面将从以下几个方面对Flutter的内存优化进行具体实现的总结 一 减少Widget的创建和销毁 Widget的创建和销毁是Flutter中内存占用最大和最频繁的操作
  • C++学习笔记黑马程序员(有一些自己的思考)

    学习目标 掌握 C 入门知识 C 核心编程 掌握 STL 洛谷算法训练题 学习内容 C 入门知识 一 基本介绍 C 不同于C语言 这是一门面向对象的高级程序设计语言 二 面向对象与面向过程 什么是面向对象 对象又是什么 对象是对客观事物的抽
  • Flink_04_Watermark(个人总结)

    声明 1 本文为我的个人复习总结 并非那种从零基础开始普及知识 内容详细全面 言辞官方的文章 2 由于是个人总结 所以用最精简的话语来写文章 3 若有错误不当之处 请指出 时间语义 EventTime 在1 12版本中被设置成了默认 是事件
  • 求平方根问题 (C++ 实现)

    下面是用二分法和牛顿迭代法求一个正数的平方根 二分法 这里的题目稍微宽了一点点 包含了整数和小数的情况 这里二分法就不用多说了 如果中间值的平方与目标值在误差范围内 则返回 否则根据大小情况改变左 右区间的端点 include
  • 每日一题:选数

    选数 题目 Daimayuan Online Judge 原本我的思路是 大致题意就是从n个数中选取若干数 使得它们的和mod n等于0 任意选取 无关顺序 是可以跳着选的 也就是对于每一个数 有两种选择 选与不选 于是我想用01背包 但是
  • linux屏保默认图片,分享

    Ubuntu 4 10 Warty Warthog Ubuntu 5 04 Hoary Hedgehog Ubuntu 5 10 Breezy Badger Ubuntu 6 06 Dapper Drake Ubuntu 6 10 Edgy
  • 多个chatgpt模型

    GPT4All 简介 GPT4AllNomic AI Team 从 Alpaca 获得灵感 使用 GPT 3 5 Turbo OpenAI API 收集了大约 800 000 个提示 响应对 创建了 430 000 个助手式提示和生成训练对
  • SQL中in和not in遇到NULL值的查询情况

    首先 大家可以先试着做这道练习题 题目 给定一个表 T id 是树节点的编号 pid 是它父节点的 id 树中每个节点属于以下三种类型之一 叶子 如果这个节点没有任何孩子节点 根 如果这个节点是整棵树的根 即没有父节点 内部节点 如果这个节
  • 最简单的引入Vue看板娘教程

    最简单的引入Vue看板娘教程 一 项目引入 这里使用的是来自Evgo老哥的 vue live2d 使用简单 直接引入就好 具体的可以看老哥的文档 二 简单实现 在你的项目引入 npm install vue live2d 接着在想要引入看板
  • Unity3D关于iTween知识详解和接口总结

    目录 1 简介 2 物体移动方法 3 物体的颜色变化 4 摄像机淡入淡出 5 音频方法 6 Look类方法 7 旋转方法 8 物体大小缩放 9 晃动效果方法 10 值方法 11 外部工具方法 12 iTweenPath 1 简介 iTwee
  • Windows NT Session 概念的原理与应用浅析 [1] 遍历并获取信息

    转载自 http www cnblogs com flier archive 2004 07 19 25709 html 我在上一篇文章 DACL NULL or not NULL 中曾简要地介绍了 Windows 系统中 Session
  • SonarLint 默认扫描规则

    请看原文 原文链接 https blog csdn net jiaomubai article details 116596868 在平时写代码的时候 为了代码规范和减少 bug 的数量 使用 SonarLint 插件进行代码检查无疑是一个
  • tesseract-ocr

    转自 http www cnblogs com zcsor archive 2011 02 21 1959555 html 关于tesseract ocr3的训练和使用 众所周知 这是一个出色的字符识别软件 这个开源项目可以在http co
  • SpringBoot企业微信公众号开发案例一

    Java Spring Boot企业微信点餐系统 2 3 数据库设计 2 3 数据库设计 数据库表设计 3 1 开发环境搭建 3 1 开发环境搭建 virtualBox安装 ifconfig sequel pro连接数据库 虚拟机安装Mav
  • 【Linux系统安装RocketMQ并整合到SpringBoot项目】

    Linux系统安装RocketMQ并整合到SpringBoot项目 一 基本概念 1 1 NameServer 1 2 Broker 1 3 Message 1 3 Topic 1 4 Tag 1 5 Queue 1 6 MessageId
  • SpringBoot-AOP记录日志+脱敏

    目录 1 引入依赖 2 配置文件 3 注解类 4 切面类 5 需要脱敏的实体类 6 查看日志打印 1 引入依赖 脱敏工具包 implementation com github houbb sensitive core 0 0 9 imple