🎁 福利时间
如果你正在备战面试或者想要学习其他知识,给大家推荐一个宝藏知识库,作者整理了一些列 Java 程序员需要掌握的核心知识,有需要的自取不谢。
知识库地址:https://farerboy.com/


一、脱敏的重要性
在现代应用中,保护用户敏感数据是至关重要的。敏感数据包括但不限于:
- 手机号
- 身份证号
- 银行卡号
- 姓名
- 邮箱地址
- 密码
脱敏处理可以有效防止敏感信息泄露,保护用户隐私,同时满足合规要求(如GDPR、PCI DSS等)。
二、实现方案
2.1 基于注解的脱敏方案
核心注解
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sensitive {
SensitiveType type();
int prefix() default 3; // 保留前缀长度
int suffix() default 4; // 保留后缀长度
char mask() default '*'; // 掩码字符
}
脱敏类型枚举
java
public enum SensitiveType {
PHONE, // 手机号
ID_CARD, // 身份证号
BANK_CARD, // 银行卡号
NAME, // 姓名
EMAIL, // 邮箱
PASSWORD // 密码
}
脱敏处理器接口
java
public interface SensitiveHandler {
String handle(String value, int prefix, int suffix, char mask);
}
脱敏处理器实现
java
import org.springframework.stereotype.Component;
@Component
public class SensitiveHandlerImpl implements SensitiveHandler {
@Override
public String handle(String value, int prefix, int suffix, char mask) {
if (value == null || value.length() <= prefix + suffix) {
return value;
}
StringBuilder result = new StringBuilder();
result.append(value.substring(0, prefix));
int maskLength = value.length() - prefix - suffix;
for (int i = 0; i < maskLength; i++) {
result.append(mask);
}
result.append(value.substring(value.length() - suffix));
return result.toString();
}
}
脱敏工具类
java
import java.lang.reflect.Field;
public class SensitiveUtils {
public static <T> T desensitize(T object, SensitiveHandler handler) {
if (object == null) {
return null;
}
Class<?> clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Sensitive.class)) {
Sensitive sensitive = field.getAnnotation(Sensitive.class);
field.setAccessible(true);
try {
Object value = field.get(object);
if (value instanceof String) {
String maskedValue = handler.handle(
(String) value,
sensitive.prefix(),
sensitive.suffix(),
sensitive.mask()
);
field.set(object, maskedValue);
}
} catch (IllegalAccessException e) {
// 处理异常
}
}
}
return object;
}
}
2.2 基于AOP的脱敏方案
切面类
java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SensitiveDataAspect {
@Autowired
private SensitiveHandler sensitiveHandler;
@Around("execution(* com.example.controller.*.*(..))")
public Object handleSensitiveData(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
return SensitiveUtils.desensitize(result, sensitiveHandler);
}
}
2.3 基于拦截器的脱敏方案
拦截器类
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class SensitiveDataInterceptor implements HandlerInterceptor {
@Autowired
private SensitiveHandler sensitiveHandler;
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 处理完成后的操作
}
}
响应包装类
java
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
public class SensitiveResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer;
private ServletOutputStream out;
private PrintWriter writer;
public SensitiveResponseWrapper(HttpServletResponse response) {
super(response);
buffer = new ByteArrayOutputStream();
out = new WrappedOutputStream(buffer);
writer = new PrintWriter(buffer);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
@Override
public PrintWriter getWriter() throws IOException {
return writer;
}
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
}
@Override
public void reset() {
buffer.reset();
}
public byte[] getContent() throws IOException {
flushBuffer();
return buffer.toByteArray();
}
private class WrappedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos;
public WrappedOutputStream(ByteArrayOutputStream bos) {
this.bos = bos;
}
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener listener) {
}
}
}
拦截器配置
java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SensitiveDataInterceptor())
.addPathPatterns("/**");
}
}
三、使用示例
3.1 基于注解的使用
java
public class User {
private Long id;
@Sensitive(type = SensitiveType.NAME)
private String name;
@Sensitive(type = SensitiveType.PHONE)
private String phone;
@Sensitive(type = SensitiveType.ID_CARD)
private String idCard;
@Sensitive(type = SensitiveType.BANK_CARD)
private String bankCard;
@Sensitive(type = SensitiveType.EMAIL)
private String email;
// getters and setters
}
// 使用方式
@Autowired
private SensitiveHandler sensitiveHandler;
public User getUser() {
User user = userService.getUser();
return SensitiveUtils.desensitize(user, sensitiveHandler);
}
3.2 基于AOP的使用
java
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUser(id);
// 返回结果会被AOP自动脱敏
}
}
3.3 基于拦截器的使用
java
// 只需配置拦截器,所有HTTP响应都会被自动脱敏
四、方案比较
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 基于注解 | 灵活性高,可针对具体字段定制 | 需要手动调用脱敏方法 | 单个对象脱敏 |
| 基于AOP | 自动处理,无需手动调用 | 只能拦截特定方法 | 控制器方法返回值脱敏 |
| 基于拦截器 | 全局处理,覆盖所有HTTP响应 | 实现复杂,性能开销较大 | 全局HTTP响应脱敏 |
五、扩展与优化
5.1 自定义脱敏规则
java
// 实现自定义脱敏处理器
public class CustomSensitiveHandler implements SensitiveHandler {
@Override
public String handle(String value, int prefix, int suffix, char mask) {
// 实现自定义脱敏逻辑
return "自定义脱敏结果";
}
}
5.2 脱敏类型扩展
java
public enum SensitiveType {
// 原有类型
PHONE, ID_CARD, BANK_CARD, NAME, EMAIL, PASSWORD,
// 新增类型
ADDRESS, // 地址
BIRTHDAY // 生日
}
5.3 性能优化
- 使用缓存存储反射结果
- 批量处理脱敏操作
- 只对需要脱敏的字段进行处理
六、总结
本文介绍了三种Spring Boot应用中敏感数据脱敏的实现方案:
-
基于注解的脱敏方案:通过自定义注解和反射实现,灵活性高,可针对具体字段定制脱敏规则。
-
基于AOP的脱敏方案:通过切面拦截方法执行,自动对返回结果进行脱敏处理,无需手动调用。
-
基于拦截器的脱敏方案:通过拦截HTTP响应,对所有响应数据进行脱敏处理,实现全局脱敏。
每种方案都有其适用场景,开发者可以根据具体需求选择合适的方案,或结合使用多种方案以达到最佳效果。
通过这些方案,可以有效地保护用户敏感数据,防止信息泄露,同时满足合规要求,为应用提供更加安全可靠的用户体验。