在 SpringBoot 中如何对数据进行脱敏显示

1、背景

基于数据安全与隐私保护、防止信息泄露风险、遵守隐私法规、增强用户安全感等方面的考虑,现在的应用,一般会对对个人信息或敏感数据字段进行脱敏,比如将手机号或信用卡号等中间的若干位数字替换为***等;对于一大段文本,比如日志信息中,很多时候可能不消息把用户的个人信息以及认证token、相关密钥等都打印了出来,此时也需要对日志信息进行脱敏处理。

在日志中,一般会带有一些关键字特征,比如日志中出现token或Authorization等关键子,后面跟着的就是token的值;比如如打印用户对象信息时(格式化为json字符换打印),用户对象信息中可能包含了email、password等。

下面的示例中,主要针对日志信息给出的数据脱敏再展示的方案。

2、创建脱敏数据处理器

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(String.class)
public class SensitiveInfoTypeHandler extends BaseTypeHandler<String> {
    // 关键字列表,实际开发中,关键字列表可以做成配置或在表中存储,这样可以更灵活
    private static final String KEYWORD_PATTERN = "token|Authorization|password|email|keyword1|keyword2|keyword3";
  
    private static final Pattern PATTERN = Pattern.compile(KEYWORD_PATTERN);

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        // 一般个人信息或敏感数据都要加密存储,可以参考文末给出的数据加解密处理方案,这里不处理
        ps.setString(i, parameter);
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 对结果进行脱敏
        return desensitize(rs.getString(columnName));
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        // 对结果进行脱敏
        return desensitize(rs.getString(columnIndex));
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        // 对结果进行脱敏
        return desensitize(cs.getString(columnIndex));
    }

    private String desensitize(String value) {
        if (value == null) {
            return null;
        }
        
        // 如果字段中存在指定的关键字,则对关键字后面10位字符串使用***代替
        Matcher matcher = PATTERN.matcher(value);
        StringBuilder result = new StringBuilder(value);
        int offset = 0;
        while (matcher.find()) {
            int start = matcher.start() + matcher.group().length();
            int end = Math.min(start + 10, result.length());
            result.replace(start + offset, end + offset, "***");
            offset += "***".length() - (end - start);
        }
        
        // 返回脱敏后的数据
        return result.toString();
    }
}

在上述代码中:

  • KEYWORD_PATTERN 定义了一个包含多个关键字的正则表达式模式,关键字之间用 | 分隔。
  • desensitize 方法中,通过 Pattern 和 Matcher 来查找字符串中所有匹配关键字的位置。
  • 使用 StringBuilder 来修改原始字符串,将关键字后的 10 位字符串替换为 ***。在替换过程中,考虑到替换字符串长度变化对后续位置的影响,通过 offset 变量进行修正。

3、在 Mapper XML 中使用

示例中对请求头req_headers、响应体resp_body字段应用脱敏处理器

    <resultMap id="BaseResultMap" type="com.example.demo.entity.Log">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="url" property="url" jdbcType="VARCHAR"/>
        <result column="req_headers" property="reqHeaders" jdbcType="VARCHAR" typeHandler="com.example.demo.typehandler.SensitiveInfoTypeHandler"/>
        <result column="resp_headers" property="respHeaders" jdbcType="VARCHAR"/>
        <result column="resp_body" property="respBody" jdbcType="VARCHAR" typeHandler="com.example.demo.typehandler.SensitiveInfoTypeHandler"/>
        <result column="cost_time" property="costTime" jdbcType="BIGINT"/>
    </resultMap>

<select id="selectUser" resultMap="BaseResultMap">
    SELECT id, url,
           req_headers,
           resp_headers,
           resp_body,
           cost_time
    FROM logs
</select>

4、扩展

通过以上步骤,在 Spring Boot 应用中使用 MyBatis 时,就可以对查询结果字段进行脱敏处理。但很多时候,个人信息等数据可能都需要做加密存储,使用时再解密,即需要对数据进行加解密的处理,如果需要脱敏展示,可以一并解密同时脱敏。对数据进行加解密,可以参考SpringBoot中如何对敏感数据进行加密解密

相关文章

自定义代码生成器(上)(推荐几个代码自动生成的工具)

1 概述1.1 介绍在项目开发过程中,有很多业务模块的代码是具有一定规律性的,例如controller控制器、service接口、service实现类、mapper接口、model实体类等等,这部分代...

Redis性能优化:scan命令替换keys命令的用法和原理解析

一、keys命令用法keys命令用于返回指定的正则表达式所匹配的所有key的列表,其所检索的是Redis当前所使用的数据库(默认为0号数据库)的所有key,用法如下:1. *匹配数据库中所有key...

MyBatis十大高频坑点避雷指南:程序员血泪总结

1. #{ }和${ }混淆使用问题:#{}是预编译处理,${}是字符串替换。使用${}可能导致SQL注入。错误示例:@Select("select * from user where nam...

八股文面试题-JAVA基础(八股文 面试)

1. JDK 和 JRE 有什么区别?JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。JRE:Java Runtime Envi...

Gateway网关在url参数带有特殊字符的情况下转发失败(响应400)

本文主要分享了,SpringCloud Gateway网关在url参数带有空格或者特殊字符的情况下,转发失败导致响应错误码400的解决方案。响应400错误码的2种场景:1.参数带空格,Gateway会...