MyBatis拦截器优雅实现数据脱敏

这篇具有很好参考价值的文章主要介绍了MyBatis拦截器优雅实现数据脱敏。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

现代网络环境中,敏感数据的处理是至关重要的。敏感数据包括个人身份信息、银行账号、手机号码等,泄露这些数据可能导致用户隐私泄露、财产损失等严重后果。因此,对敏感数据进行脱敏处理是一种必要的安全措施。
比如页面上常见的敏感数据都是加*遮挡处理过的,如下图所示。
MyBatis拦截器优雅实现数据脱敏

接下来本文将以Spring Boot和MyBatis框架实现返回数据的脱敏处理。

脱敏工具

脱敏工具有很多种,本文主要介绍和使用hutool工具包提供的脱敏工具类DesensitizedUtil,它提供了常见的手机号、身份证号、银行卡、邮箱等脱敏的方法,将敏感数据部分加*处理。
使用方法如下:
maven项目需要导入hutool包依赖,坐标如下:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-core</artifactId>
    <version>5.8.4</version>
</dependency>
import cn.hutool.core.util.DesensitizedUtil;

public class SensitiveHutoolTest {
    public static void main(String[] args) {
        System.out.println(DesensitizedUtil.mobilePhone("13812345678"));
        System.out.println(DesensitizedUtil.idCardNum("110101200007283706", 3, 4));
        System.out.println(DesensitizedUtil.bankCard("6225809637392380845"));
        System.out.println(DesensitizedUtil.email("zhangsanfeng@test.com"));
    }
}

输出如下:

138****5678
110***********3706
6225 **** **** *** 0845
z***********@test.com

实现思路

  • 设计一个常用的脱敏类型枚举类。
  • 编写脱敏注解,包含脱敏类型,采用注解的方式对需要脱敏的字段进行标注。
  • 再编写脱敏注解相应的实现逻辑使用MyBatis的拦截器功能对查出的结果集采用反射的处理方式对结果进行脱敏处理,然后将脱敏处理后的结果集再返回。

代码实现

  1. 定义脱敏类型枚举

利用hutool工具包,对常见的手机号、身份证号、银行卡号、邮箱进行脱敏处理。还给了一个默认的DEFAULT枚举类型按原数据返回,同时自定义了一个CUSTOM枚举,可根据实现CustomMaskService这个接口去自定义脱敏逻辑。

注意:这里的CUSTOM只是举一个简单的例子,实际情况可能需要根据实际情况扩展输入输出参数等。

import cn.hutool.core.util.DesensitizedUtil;
import lombok.Getter;

public enum SensitiveTypeEnum {
    MOBILE("mobile", "手机号") {
        @Override
        public String maskSensitiveData(String data) {
            //手机号前3位后4位脱敏,中间部分加*处理,比如:138****5678
            return DesensitizedUtil.mobilePhone(data);
        }
    },
    IDENTIFY("identify", "身份证号") {
        @Override
        public String maskSensitiveData(String data) {
            //身份证前3位后4位脱敏,中间部分加*处理,比如:110***********3706
            return DesensitizedUtil.idCardNum(data, 3, 4);
        }
    },
    BANKCARD("bankcard", "银行卡号") {
        @Override
        public String maskSensitiveData(String data) {
            //银行卡号前4位后4位脱敏,中间部分加*处理,比如:6225 **** **** *** 0845
            return DesensitizedUtil.bankCard(data);
        }
    },

    EMAIL("email", "邮箱") {
        @Override
        public String maskSensitiveData(String data) {
            //邮箱@符号后明文显示,@符号前的字符串,只显示第一个字符,其余加*处理,比如:z***********@test.com
            return DesensitizedUtil.email(data);
        }
    },
    DEFAULT("default", "默认") {
        @Override
        public String maskSensitiveData(String data) {
            // 默认原值返回,其他这个也没啥意义^_^
            return data;
        }
    },
    CUSTOM("custom", "自定义") {
        @Override
        public String maskSensitiveData(String data, CustomMaskService customMaskService) {
            // 可以自定义处理的service,根据实际使用情况可能需要添加参数,调整一下即可
            return customMaskService.maskData(data);
        }
    };


    @Getter
    private String type;

    @Getter
    private String desc;

    SensitiveTypeEnum(String type, String desc) {
        this.type = type;
        this.desc = desc;
    }

    /**
     * 遮挡敏感数据
     *
     * @param data
     * @return
     */
    public String maskSensitiveData(String data) {
        return data;
    }

    public String maskSensitiveData(String data, CustomMaskService customMaskService) {
        return null;
    }
}
  1. 定义一个脱敏注解
import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SensitiveData {
    SensitiveTypeEnum type() default SensitiveTypeEnum.DEFAULT;
}
  1. 定义并配置一个mybatis拦截器。
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Slf4j
public class SensitiveDataInterceptor implements Interceptor {

    @Autowired
    private CustomMaskService customMaskService;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        log.debug("进入数据脱敏拦截器...");
        if (result instanceof List) {
            List<?> resultList = (List<?>) result;
            for (Object obj : resultList) {
                doSensitiveFields(obj);
            }
        } else if (result instanceof Map) {
            Map<?, ?> resultMap = (Map<?, ?>) result;
            for (Object obj : resultMap.values()) {
                doSensitiveFields(obj);
            }
        } else {
            doSensitiveFields(result);
        }
        return result;
    }

    private void doSensitiveFields(Object obj) throws IllegalAccessException {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(SensitiveData.class)) {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value == null) {
                    continue;
                }
                SensitiveData sensitiveData = field.getAnnotation(SensitiveData.class);
                SensitiveTypeEnum type = sensitiveData.type();
                String result;
                if (type == SensitiveTypeEnum.CUSTOM) {
                    result = type.maskSensitiveData(value.toString(), customMaskService);
                } else {
                    result = type.maskSensitiveData(value.toString());
                }
                field.set(obj, result);
            }
        }
    }
}

拦截器注解里写明了要拦截的对象和方法,类:ResultSetHandler,方法:handleResultSets,它是在sql执行完成后拿到结果集并对结果集进行处理再返回。

  1. 配置mybatis及拦截器
import com.star95.project.study.mybatisplus.interceptor.SensitiveDataInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.star95.project.study.mybatisplus.mapper")
public class MyBatisPlusConfig {

    @Bean
    public SensitiveDataInterceptor sensitiveDataInterceptor() {
        return new SensitiveDataInterceptor();
    }
}

  1. 测试
import com.star95.project.study.mybatisplus.interceptor.SensitiveData;
import lombok.Data;

import static com.star95.project.study.mybatisplus.interceptor.SensitiveTypeEnum.*;

@Data
public class UserDto {
    /**
     * id
     */
    private Long id;
    /**
     * 姓名
     */
    @SensitiveData(type = CUSTOM)
    private String name;
    /**
     * 年龄
     */
    private Integer age;

    /**
     * 邮箱
     */
    @SensitiveData(type = EMAIL)
    private String email;

    /**
     * 手机号
     */
    @SensitiveData(type = MOBILE)
    private String mobile;

    /**
     * 身份证号
     */
    @SensitiveData(type = IDENTIFY)
    private String identify;

    /**
     * 银行卡号
     */
    @SensitiveData(type = BANKCARD)
    private String bankcard;
}
public interface CustomMaskService {
    String maskData(String data);
}
import cn.hutool.core.text.CharSequenceUtil;
import org.springframework.stereotype.Service;

@Service
public class CustomMaskServiceImpl implements CustomMaskService {
    @Override
    public String maskData(String data) {
        //第一个字符明文外,其他都加*处理
        return CharSequenceUtil.hide(data, 1, data.length());
    }
}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.star95.project.study.mybatisplus.dto.User;
import com.star95.project.study.mybatisplus.dto.UserDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select({"select * from user where id=#{id}"})
    UserDto getSpecialUser(String id);

    @Select({"select * from user"})
    List<UserDto> queryAll();
}
import com.star95.project.study.mybatisplus.dto.UserDto;
import com.star95.project.study.mybatisplus.mapper.UserMapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

@RestController
@RequestMapping("/sensitive")
public class SensitiveMybatisInterceptorTestController {
    @Resource
    private UserMapper userMapper;

    @GetMapping("/user/{id}")
    public Result<UserDto> queryUserInfo(@PathVariable String id) {
        return Result.success(userMapper.getSpecialUser(id));
    }

    @GetMapping("/userlist")
    public Result<List<UserDto>> queryUserList() {
        return Result.success(userMapper.queryAll());
    }
}

写了两个测试接口,一个是查询单个对象,一个是返回list集合列表,访问一下:
MyBatis拦截器优雅实现数据脱敏

MyBatis拦截器优雅实现数据脱敏

可以看到单个结果和集合列表都做了脱敏处理,这样功能就实现完成了。

其他

使用mybatis拦截器这种方式,是在数据持久层的逻辑处理,需要注意的是,查询结果返回的dto如果是共享的情况下,可能会把不需要脱敏的数据也给处理了,影响业务逻辑,所以使用过程中要注意区分。
另外也可在接口返回数据时进行脱敏处理,也就是所说的序列化方式,比如自定义Jackson、fastjson等的序列化逻辑同样可以完成数据脱敏。

总结

通过使用MyBatis拦截器,我们可以实现对敏感数据的优雅脱敏处理,保护用户隐私和数据安全。这种方式可以灵活应用于各种场景,提供了一种简单而强大的解决方案。在实际开发中,我们可以根据具体需求,定制化开发拦截器的逻辑,以满足不同的数据脱敏需求。
数据的隐私和安全是非常重要的,敏感数据除存储方面要加密外,再展示方便也要适当的做脱敏处理,本文只介绍了mybatis拦截器实现的一种数据脱敏方式,还有很多其他技术可以实现,可以自行搜索,根据实际情况选择合适的解决方案。文章来源地址https://www.toymoban.com/news/detail-736520.html

到了这里,关于MyBatis拦截器优雅实现数据脱敏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 关于MyBatis拦截器失效问题的解决(多数据源、分页插件)

    最近做了一个备份被delete语句删除的数据的小插件,是用MyBatis的拦截器去做的。 但是发现在一个项目中会失效,没有去备份删除的数据,查看日志发现请求并没有进入到拦截器中,换句话说就是拦截器失效了。 百度了一下(哎,百度)发现说的最多的就是分页插件导致的,

    2024年02月14日
    浏览(29)
  • Springboot 自定义 Mybatis拦截器,实现 动态查询条件SQL自动组装拼接(玩具)

    ps:最近在参与3100保卫战,战况很激烈,刚刚打完仗,来更新一下之前写了一半的博客。 该篇针对日常写查询的时候,那些动态条件sql 做个简单的封装,自动生成(抛砖引玉,搞个小玩具,不喜勿喷)。 来看看我们平时写那些查询,基本上都要写的一些动态sql:   一个字段

    2024年02月12日
    浏览(39)
  • MyBatis 拦截器介绍

    MyBatis 提供了一种插件 (plugin) 的功能,虽然叫做插件,但其实这是拦截器功能。那么拦截器拦截 MyBatis 中的哪些内容呢? 我们进入官网看一看: MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: Executor

    2024年02月15日
    浏览(36)
  • 自定义MyBatis拦截器更改表名

    by emanjusaka from ​ https://www.emanjusaka.top/2023/10/mybatis-interceptor-update-tableName 彼岸花开可奈何 本文欢迎分享与聚合,全文转载请留下原文地址。 自定义MyBatis拦截器可以在方法执行前后插入自己的逻辑,这非常有利于扩展和定制 MyBatis 的功能。本篇文章实现自定义一个拦截器去改

    2024年02月08日
    浏览(33)
  • Mybatis拦截器注解@Intercepts与@Signature注解属性说明

    可能有些新手使用mybatis拦截器的时候可能没太懂@Signature注解中type,method,args的用法 首先mybatis拦截器可以拦截如下4中类型 Executor sql的内部执行器 ParameterHandler 拦截参数的处理 StatementHandler 拦截sql的构建 ResultSetHandler 拦截结果的处理 type:就是指定拦截器类型(ParameterHandl

    2024年02月05日
    浏览(35)
  • MyBatis拦截器-打印出真正执行的sql语句和执行结果

    目录 广而告之 背景 先看成品 实现步骤 第一步,实现Interceptor接口 ​编辑 第二步,给拦截器指定要拦截的方法签名 第三步,实现拦截器的intercept方法。 第四步,在mybatis-config.xml里配置上这个拦截器插件 第五步,禁用mybatis打印日志 给大家推荐一个好用的在线工具网站: 常

    2023年04月25日
    浏览(54)
  • 基于MybatisPlus拦截器实现数据库关键字处理及官方做法

    有些老的数据库当中可能会有些字段和数据库冲突,使用mybatisPlus执行Sql的时候有时候会执行失败,前段时间和群友讨论的时候他说遇到了这个问题,当时我提议让他用我以前写的一个自定义注解+mybatis拦截器实现权限控制里边的工具类改造一下。 他说不能实现,然后

    2024年04月25日
    浏览(32)
  • 数据权限拦截器,多租户拦截器

    WEB类型软件产品,在Java(SpringBoot)+MybatisPlus架构场景下,本文针对下面两个问题,提供解决方案: 多租户的产品,想在表内级别上,实现租户数据隔离(分表、分库方案不在本文讨论范围内)。 ToB、ToG类型的软件产品,需要实现数据权限鉴权。例如用户数据、部门数据、租户

    2024年02月02日
    浏览(33)
  • 自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)

    最近考虑myBatis中sql语句使用规范的问题,如果漏下条件或者写一些不规范语句会对程序性能造成很大影响。最好的方法就是利用代码进行限制,通过拦截器进行sql格式的判断在自测环节就能找到问题。写了个简单情景下的demo,并通过idea插件来将myBatis的mapper方法都打上拦截器

    2024年01月22日
    浏览(36)
  • SpringBoot加入拦截器——登录拦截器的实现

            拦截器 Interceptor 在 Spring MVC 中的地位等同于 Servlet 规范中的过滤器 Filter,拦截的是处理器的执行,由于是全局行为,因此常用于做一些通用的功能,如请求日志打印、权限控制等。         核心原理:AOP思想 preHandle:  预先处理,在目标的controller方法执行之前,进行

    2024年02月15日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包