利用Jackson的JsonFilter来实现动态过滤数据列。
也就是说,同一个实体,你配置了不同的@JsonFilter,通过Jackson展现的结果可以是不一样的。
举个栗子:
@lombok.Data
public class User{
String username;
String password;
Integer age;
String gender;
String blog;
}
默认不做任何配置的话,通过Jackson序列化出来的结果是:
{
"username" : "tomcatandjerry",
"password" : "123456",
"age" : 36,
"gender" : "男",
"blog" : ""
}
可是password不应该要展示,方法有多种:
方法1:在不想序列化的字段上加注解JsonProperty:
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)//Jackson
@JSONField(serialize = false)//fastjson
String password;
方法2:
2.1在User类上面加注解JsonFilter:
@JsonFilter("non-password")
public class User {
...
}
2.2 配置FilterProvider
测试方法&配置如下:
public class JsonFilterTest {
private ObjectMapper setupJsonFilter(){
ObjectMapper mapper = new ObjectMapper();
String[] beanProperties = new String[]{"password"};
String nonPasswordFilterName = "non-password";//需要跟User类上的注解@JsonFilter("non-password")里面的一致
FilterProvider filterProvider = new SimpleFilterProvider()
.addFilter(nonPasswordFilterName, SimpleBeanPropertyFilter.serializeAllExcept(beanProperties));
//serializeAllExcept 表示序列化全部,除了指定字段
//filterOutAllExcept 表示过滤掉全部,除了指定的字段
mapper.setFilterProvider(filterProvider);
return mapper;
}
@Test
public void testJsonFilter() throws JsonProcessingException {
User user = new User();
user.setUsername("tomcatandjerry");
user.setPassword("123456");
user.setAge(36);
user.setGender("男");
System.out.println(setupJsonFilter().writeValueAsString(user));
}
}
打印测试结果:
{
"username" : "tomcatandjerry",
"age" : 36,
"gender" : "男",
"blog" : ""
}
小结:
看上去似乎使用@JsonProperty更简单。
但是当有一堆字段需要配置,而且整个项目都需要统一处理的时候,后者@JsonFilter是一个不错的选择。
扩展:
同一个API,如果我想不同的人看到不一样的结果呢?
比如同一个用户API,有的展示username+age, 有的展示username+gender等
这个时候JsonFilter就非常适合了。
有人可能会问:不对啊?一个对象只能配置一个JsonFilter,怎么动态切换不同的Filter?
对的,一个对象只能配置一个JsonFilter,但只要稍加修改,就能实现??
思路:
既然一个对象只能配置一个JsonFilter,那么靠一个对象来动态展示不同的属性是不可能的。
我们可以多写几个对象,都继承User对象,不同的子类里面使用不同的JsonFilter
@JsonFilter("normal-user")
public class UserNormal extends User{
//空class,里面没有任何属性
}
@JsonFilter("admin")
public class UserAdmin extends User{
//空class,里面没有任何属性
}
利用Spring的切点,根据当前用户的角色,替换返回值为不同的子类
原本:
public class UserService{
public User get(String id){
}
}
利用切点(可以自定义注解,加到方法上,切在注解上面),替换返回的对象为子类:
具体需要用到的:
- 扫描并缓存子类
- @Aspect切点,@Around(value=“比如:自定义注解”)
- 利用反射,创建出子类对象,BeanUtils.copyProperties
这样看似调用userService.get(“id”)返回的是User对象,其实可能已经替换成某一个子类了。
在ObjectMapper配置多个Filter,就实现了动态展示不同属性,且对开发人员透明。
小结:
优点: 对开发透明
缺点:一个对象需要写多个子类,虽然是空class
这也算是一种数据列权限控制的一种解决方案吧。