在 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中如何对敏感数据进行加密解密