在 Spring Boot 结合 MyBatis 的项目中,实现字段脱敏(如手机号、身份证号、银行卡号等敏感信息的部分隐藏)可以通过以下方案实现

在 Spring Boot 结合 MyBatis 的项目中,实现字段脱敏(如手机号、身份证号、银行卡号等敏感信息的部分隐藏)可以通过以下方案实现。以下是分步说明和完整代码示例:


一、实现方案选择

1. 方案一:自定义注解 + Jackson 序列化脱敏
  • 适用场景:数据返回给前端时动态脱敏,数据库存储原始数据。
  • 优点:无侵入性,通过注解灵活控制脱敏字段,与业务逻辑解耦。
  • 核心实现 :利用 Jackson 的 JsonSerializer 自定义序列化逻辑。
2. 方案二:MyBatis 类型处理器(TypeHandler)
  • 适用场景:数据库存储时加密或脱敏,查询时解密。
  • 优点:数据存储层直接处理,安全性高。
  • 缺点:需处理加解密逻辑,可能影响查询性能。
3. 方案三:AOP 拦截 Service 层返回结果
  • 适用场景:在业务层统一处理敏感字段。
  • 优点:集中控制脱敏逻辑。
  • 缺点:需遍历对象树,性能开销较大。

二、推荐方案一:Jackson 动态脱敏(完整代码)

1. 定义脱敏策略注解
java 复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
    /**
     * 脱敏策略类型
     */
    SensitiveStrategy strategy();
}
2. 定义脱敏策略枚举
java 复制代码
public enum SensitiveStrategy {
    /**
     * 手机号脱敏(保留前3后4位)
     */
    PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),

    /**
     * 身份证脱敏(保留前1后4位)
     */
    ID_CARD(s -> s.replaceAll("(\\d{1})\\d{13}(\\d{4})", "$1*************$2")),

    /**
     * 银行卡脱敏(保留前6后4位)
     */
    BANK_CARD(s -> s.replaceAll("(\\d{6})\\d{9}(\\d{4})", "$1*********$2"));

    private final Function<String, String> desensitizer;

    SensitiveStrategy(Function<String, String> desensitizer) {
        this.desensitizer = desensitizer;
    }

    public Function<String, String> getDesensitizer() {
        return desensitizer;
    }
}
3. 自定义 Jackson 序列化器
java 复制代码
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import java.io.IOException;

public class SensitiveSerializer extends JsonSerializer<String> implements ContextualSerializer {
    private SensitiveStrategy strategy;

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (strategy != null && value != null) {
            // 应用脱敏策略
            String desensitizedValue = strategy.getDesensitizer().apply(value);
            gen.writeString(desensitizedValue);
        } else {
            gen.writeString(value);
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
        Sensitive annotation = property.getAnnotation(Sensitive.class);
        if (annotation != null && property.getType().getRawClass() == String.class) {
            SensitiveSerializer serializer = new SensitiveSerializer();
            serializer.strategy = annotation.strategy();
            return serializer;
        }
        return prov.findNullValueSerializer(property);
    }
}
4. 在实体类中标记脱敏字段
java 复制代码
public class User {
    private String name;

    @Sensitive(strategy = SensitiveStrategy.PHONE)
    private String phone;

    @Sensitive(strategy = SensitiveStrategy.ID_CARD)
    private String idCard;

    // Getters and Setters
}
5. 注册自定义序列化器到 Jackson
java 复制代码
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {
    @Bean
    public SimpleModule sensitiveModule() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(String.class, new SensitiveSerializer());
        return module;
    }
}

三、测试结果

请求返回示例
json 复制代码
{
  "name": "张三",
  "phone": "138****5678",
  "idCard": "3***************1234"
}

四、方案二补充:MyBatis TypeHandler 脱敏(数据库存储加密)

1. 实现 TypeHandler
java 复制代码
@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class EncryptTypeHandler implements TypeHandler<String> {
    private static final String KEY = "your-secret-key-123";

    @Override
    public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        // 存储时加密
        ps.setString(i, encrypt(parameter));
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        // 查询时解密
        return decrypt(rs.getString(columnName));
    }

    private String encrypt(String data) {
        // 实现加密逻辑(如 AES)
        return "encrypted_" + data;
    }

    private String decrypt(String data) {
        // 实现解密逻辑
        return data.replace("encrypted_", "");
    }
}
2. 在字段上指定 TypeHandler
java 复制代码
public class User {
    @TableField(typeHandler = EncryptTypeHandler.class)
    private String bankCard;
}

五、方案选择建议

方案 适用场景 优点 缺点
Jackson 动态脱敏 响应数据脱敏 无侵入性,灵活配置 不适用于非 JSON 输出场景
MyBatis TypeHandler 数据库加密存储 数据存储安全 加解密影响查询性能
AOP 拦截 业务层统一处理 集中控制逻辑 性能开销较大

六、扩展优化

  1. 动态开关脱敏

    通过 @Sensitive 注解增加 enable 属性,结合配置中心动态控制是否脱敏:

    java 复制代码
    @Sensitive(strategy = SensitiveStrategy.PHONE, enable = "${sensitive.enable:true}")
  2. 嵌套对象脱敏

    自定义序列化器支持嵌套对象处理:

    java 复制代码
    public class User {
        @Sensitive(strategy = SensitiveStrategy.PHONE)
        private String phone;
        
        @Valid // 支持嵌套对象
        private Address address;
    }
  3. 日志脱敏

    结合 Logback 或 Log4j2 的 Converter 实现日志输出时的脱敏。


通过上述方案,可以灵活、安全地实现敏感字段的脱敏处理,根据实际需求选择合适的实现方式。

相关推荐
Asthenia041210 分钟前
无感刷新的秘密:Access Token 和 Refresh Token 的那些事儿
前端·后端
hxung24 分钟前
常用的 MyBatis 标签及其作用
mybatis
Asthenia041241 分钟前
面试复盘:聊聊epoll的原理、以及其相较select和poll的优势
后端
luckyext44 分钟前
SQLServer列转行操作及union all用法
运维·数据库·后端·sql·sqlserver·运维开发·mssql
Asthenia04121 小时前
ES:倒排索引的原理与写入分析
后端
圈圈编码2 小时前
Spring常用注解汇总
java·后端·spring
stark张宇2 小时前
PHP多版本共存终极填坑指南:一台服务器部署多实例的最佳实践
后端·php
You Only Live Once_23 小时前
SpringBoot万能启停脚本
spring boot
Lian_Aseubel3 小时前
Springboot整合Netty简单实现1对1聊天(vx小程序服务端)
java·spring boot·后端
m0_748254883 小时前
SpringBoot整合MQTT最详细版(亲测有效)
java·spring boot·后端