在SpringBoot开发中,经常需要从JSON响应中排除敏感属性(如密码、身份证号等),FastJson提供的SimplePropertyPreFilter
及其扩展类是实现这一需求的常用方案。以下是详细的实现方法和最佳实践:
1. 基础实现方案
1.1 直接使用SimplePropertyPreFilter
最简单的实现方式是直接在Controller中创建SimplePropertyPreFilter
实例并指定要排除的字段:
sql
@GetMapping("/user")
public String getUser() {
User user = new User("张三", "123456", "1828381828225677");
SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
filter.getExcludes().add("password");
filter.getExcludes().add("idNo");
return JSON.toJSONString(user, filter);
}
这种方式简单直接,但需要在每个接口重复编写过滤逻辑。
1.2 使用PropertyPreExcludeFilter扩展类
可以创建一个继承自SimplePropertyPreFilter
的扩展类,提供更便捷的链式调用:
typescript
public class PropertyPreExcludeFilter extends SimplePropertyPreFilter {
public PropertyPreExcludeFilter addExcludes(String... filters) {
for (String filter : filters) {
this.getExcludes().add(filter);
}
return this;
}
}
// 使用示例
@GetMapping("/user")
public String getUser() {
User user = new User("张三", "123456", "1828381828225677");
PropertyPreExcludeFilter filter = new PropertyPreExcludeFilter()
.addExcludes("password", "idNo");
return JSON.toJSONString(user, filter);
}
这种方式通过链式调用简化了代码,提高了可读性。
2. 全局配置方案
2.1 配置FastJsonHttpMessageConverter
在SpringBoot中,可以通过配置FastJsonHttpMessageConverter
实现全局过滤:
java
@Configuration
public class FastJsonConfig {
@Bean
public FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
// 配置全局过滤器
PropertyPreExcludeFilter filter = new PropertyPreExcludeFilter()
.addExcludes("password", "idNo", "creditCard");
config.setSerializeFilters(filter);
converter.setFastJsonConfig(config);
return converter;
}
}
这种配置会对所有使用FastJson序列化的响应生效。
2.2 结合AOP实现动态过滤
对于需要根据不同接口动态过滤不同字段的场景,可以使用AOP:
java
@Aspect
@Component
public class SensitiveDataAspect {
@Around("@annotation(com.example.SensitiveData)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
SensitiveData sensitiveData = ((MethodSignature) joinPoint.getSignature())
.getMethod().getAnnotation(SensitiveData.class);
PropertyPreExcludeFilter filter = new PropertyPreExcludeFilter()
.addExcludes(sensitiveData.excludes());
return JSON.toJSONString(result, filter);
}
}
// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SensitiveData {
String[] excludes();
}
// 使用示例
@GetMapping("/user")
@SensitiveData(excludes = {"password", "idNo"})
public User getUser() {
return new User("张三", "123456", "1828381828225677");
}
这种方式提供了最大的灵活性,可以根据不同接口需求定制过滤规则。
3. 高级应用场景
3.1 结合注解实现条件过滤
可以自定义注解实现更智能的过滤,如根据字段值决定是否序列化:
java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomizeJsonExclude {
}
// 自定义过滤器
public class CustomPropertyPreFilter implements PropertyPreFilter {
@Override
public boolean apply(JSONSerializer serializer, Object source, String name) {
try {
Field field = source.getClass().getDeclaredField(name);
field.setAccessible(true);
// 检查是否有自定义注解
if (field.isAnnotationPresent(CustomizeJsonExclude.class)) {
Object value = field.get(source);
return value != null; // 值为null时不序列化
}
return true;
} catch (Exception e) {
return true;
}
}
}
// 配置到FastJsonConfig中
@Bean
public FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializeFilters(new CustomPropertyPreFilter());
converter.setFastJsonConfig(config);
return converter;
}
这种方式类似于Jackson的@JsonInclude
注解功能。
3.2 多过滤器组合使用
FastJson支持同时使用多个过滤器:
typescript
// 排除敏感字段的过滤器
PropertyPreExcludeFilter sensitiveFilter = new PropertyPreExcludeFilter()
.addExcludes("password", "idNo");
// 格式化BigDecimal的过滤器
ValueFilter decimalFilter = (object, name, value) -> {
if (value instanceof BigDecimal) {
return ((BigDecimal) value).setScale(2, RoundingMode.HALF_UP);
}
return value;
};
// 在Controller中使用
@GetMapping("/account")
public String getAccount() {
Account account = accountService.getCurrentAccount();
return JSON.toJSONString(account, sensitiveFilter, decimalFilter);
}
这种组合方式可以同时处理字段排除和值格式化等需求。
4. 最佳实践建议
- 性能考虑:频繁创建过滤器实例会影响性能,建议将常用过滤器配置为单例
- 安全基线 :定义项目统一的敏感字段清单,如
password
,idNo
,phone
,email
等 - 日志处理:在日志打印时也应使用相同的过滤逻辑,避免敏感信息泄露
- 文档维护:在API文档中明确标注哪些字段会被自动过滤
- 测试验证:编写测试用例验证过滤逻辑,特别是边界条件如null值、空字符串等
5. 与其他方案的对比
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
SimplePropertyPreFilter | 简单直接,无需配置 | 代码重复,不够灵活 | 简单项目或临时方案 |
全局配置 | 一次配置,全局生效 | 不够灵活,所有接口使用相同规则 | 敏感字段固定的项目 |
AOP+注解 | 高度灵活,可定制 | 实现复杂,需要额外配置 | 复杂项目,不同接口需要不同规则 |
Jackson注解 | 声明式,与SpringBoot集成好 | 需要切换JSON库 | 新项目或可接受Jackson的项目 |
在FastJson生态中,SimplePropertyPreFilter
及其扩展类提供了简单有效的敏感字段过滤方案,特别适合已经在使用FastJson的项目。