根据 Jax RS 响应的自定义动态标准隐藏 Jackson 字段
hide Jackson fields based on costume dynamic criteria for JaxRS Respose
想法很简单。我有一个对象,我想根据一些特定的角色隐藏一些字段。
我在系统中有角色"dog"、"cat"等
class Food{
String name;
@HideInfoForTheRoles({"dog", "cat"})
String age;
}
所以我想创建类似的东西:
public String hideForRole(T object, String role){
// return new json
}
或者我可以重写一些去国家化方法来强制 Jackson 根据我的注释隐藏字段?
您可以使用 @JsonView
。这可能是最简单的解决方案,因为 @JsonView
可以直接使用 JAX-RS。
或者,它可以用 BeanPropertyFilter
来实现,类似于我刚才放在一起的 。
开始定义注释:
@Documented
@Retention(RUNTIME)
@Target({FIELD})
public @interface HiddenForRoles {
String[] value();
}
然后定义你的BeanPropertyFilter
,它可以扩展SimpleBeanPropertyFilter
:
public class HiddenForRolesPropertyFilter extends SimpleBeanPropertyFilter {
private String allowedRole;
public HiddenForRolesPropertyFilter(String allowedRole) {
this.allowedRole = allowedRole;
}
@Override
public void serializeAsField(Object pojo, JsonGenerator jgen,
SerializerProvider provider,
PropertyWriter writer) throws Exception {
HiddenForRoles hiddenForRoles = writer.getAnnotation(HiddenForRoles.class);
if (hiddenForRoles != null) {
if (Arrays.asList(hiddenForRoles.value()).contains(allowedRole)) {
writer.serializeAsOmittedField(pojo, jgen, provider);
return;
}
}
// If no annotation is provided, the property will be serialized
writer.serializeAsField(pojo, jgen, provider);
}
}
根据您的需要在您的字段中放置 @HiddenForRoles
注释,并确保 class 注释为 @JsonFilter
:
@Data
@JsonFilter("hiddenForRolesPropertyFilter")
public class Foo {
private String bar;
@HiddenForRoles({"cat"})
private String biz;
}
最后,在 ContextResolver
中为 ObjectMapper
注册过滤器:
String currentUserRole = // Get role from the current user
FilterProvider filterProvider = new SimpleFilterProvider()
.addFilter("hiddenForRolesPropertyFilter",
new HiddenForRolesPropertyFilter(currentUserRole));
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
如果你想使你的过滤器"global",即应用于所有bean,你可以创建一个mix-in class并用@JsonFilter
注释它:
@JsonFilter("hiddenForRolesPropertyFilter")
public class HiddenForRolesPropertyFilterMixIn {
}
然后将混音class绑定到Object
:
mapper.addMixIn(Object.class, HiddenForRolesPropertyFilterMixIn.class);
创建支持 FIELD 和 METHOD 的注释
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface HideFor{
String[] roles() default{};
}
以及支持字段和方法注解的逻辑
public class AccessRestrictionFilter extends SimpleBeanPropertyFilter {
@Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
throws Exception {
if(writer.getAnnotation(HideFor.class)!=null && isHidable( Arrays.asList(writer.getAnnotation(HideFor.class).roles()))){
logger.debug("Found restriction on the getter method of the field: " + pojo + " Restriction For" + Arrays.toString(writer.getAnnotation(HideFor.class).roles()) );
return;
}
Field[] fields = jgen.getCurrentValue().getClass().getDeclaredFields();
Optional<Field> field = Arrays.stream(fields)
.filter(f-> f.getName().equalsIgnoreCase(writer.getName())).findAny();
if(field.isPresent() && field.get().getAnnotation(HideFor.class)!=null){
if(isHidable( Arrays.asList(writer.getAnnotation(HideFor.class).roles()))){
System.out.println("Found restriction on the field " + field.get().getName() + " Restriction For " + Arrays.toString(writer.getAnnotation(HideFor.class).roles()));
return;
}
}
writer.serializeAsField(pojo, jgen, provider);
}
private boolean isHidable(List<String> rolesToHide){ // imlement the logic // }
}
用法:
FilterProvider filterProvider = new SimpleFilterProvider().addFilter("AccessRestrictionFilter", new AccessRestrictionFilter());
new ObjectMapper().writer(filterProvider ).writeValueAsString(myObjToFilter);
我使用 Jersey/Spring,我的配置如下所示:
@Provider
@Produces({MediaType.APPLICATION_JSON})
public class JacksonJsonProvider extends JacksonJaxbJsonProvider {
public JacksonJsonProvider(AccessRestrictionFilter filter) {
ObjectMapper objectMapper = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.setFilterProvider(new SimpleFilterProvider().addFilter("AccessRestriction", filter));
setMapper(objectMapper);
}
}
和过滤器:
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Bean("accessRestrictionFilter")
public AccessRestrictionFilter accessRestrictionFilter(){
return new AccessRestrictionFilter();
}
注意:在过滤器中我使用了安全上下文,因为过滤器的这个范围是会话(不是共享状态而是为每个用户创建新对象)
这是我的 POJO:
@JsonFilter("AccessRestrictionFilter")
public class MyClass {
@HideFor(roles = {"ROLE_USER", "ROLE_EDITOR"})
private int val;
想法很简单。我有一个对象,我想根据一些特定的角色隐藏一些字段。
我在系统中有角色"dog"、"cat"等
class Food{
String name;
@HideInfoForTheRoles({"dog", "cat"})
String age;
}
所以我想创建类似的东西:
public String hideForRole(T object, String role){
// return new json
}
或者我可以重写一些去国家化方法来强制 Jackson 根据我的注释隐藏字段?
您可以使用 @JsonView
。这可能是最简单的解决方案,因为 @JsonView
可以直接使用 JAX-RS。
或者,它可以用 BeanPropertyFilter
来实现,类似于我刚才放在一起的
开始定义注释:
@Documented
@Retention(RUNTIME)
@Target({FIELD})
public @interface HiddenForRoles {
String[] value();
}
然后定义你的BeanPropertyFilter
,它可以扩展SimpleBeanPropertyFilter
:
public class HiddenForRolesPropertyFilter extends SimpleBeanPropertyFilter {
private String allowedRole;
public HiddenForRolesPropertyFilter(String allowedRole) {
this.allowedRole = allowedRole;
}
@Override
public void serializeAsField(Object pojo, JsonGenerator jgen,
SerializerProvider provider,
PropertyWriter writer) throws Exception {
HiddenForRoles hiddenForRoles = writer.getAnnotation(HiddenForRoles.class);
if (hiddenForRoles != null) {
if (Arrays.asList(hiddenForRoles.value()).contains(allowedRole)) {
writer.serializeAsOmittedField(pojo, jgen, provider);
return;
}
}
// If no annotation is provided, the property will be serialized
writer.serializeAsField(pojo, jgen, provider);
}
}
根据您的需要在您的字段中放置 @HiddenForRoles
注释,并确保 class 注释为 @JsonFilter
:
@Data
@JsonFilter("hiddenForRolesPropertyFilter")
public class Foo {
private String bar;
@HiddenForRoles({"cat"})
private String biz;
}
最后,在 ContextResolver
中为 ObjectMapper
注册过滤器:
String currentUserRole = // Get role from the current user
FilterProvider filterProvider = new SimpleFilterProvider()
.addFilter("hiddenForRolesPropertyFilter",
new HiddenForRolesPropertyFilter(currentUserRole));
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
如果你想使你的过滤器"global",即应用于所有bean,你可以创建一个mix-in class并用@JsonFilter
注释它:
@JsonFilter("hiddenForRolesPropertyFilter")
public class HiddenForRolesPropertyFilterMixIn {
}
然后将混音class绑定到Object
:
mapper.addMixIn(Object.class, HiddenForRolesPropertyFilterMixIn.class);
创建支持 FIELD 和 METHOD 的注释
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface HideFor{
String[] roles() default{};
}
以及支持字段和方法注解的逻辑
public class AccessRestrictionFilter extends SimpleBeanPropertyFilter {
@Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
throws Exception {
if(writer.getAnnotation(HideFor.class)!=null && isHidable( Arrays.asList(writer.getAnnotation(HideFor.class).roles()))){
logger.debug("Found restriction on the getter method of the field: " + pojo + " Restriction For" + Arrays.toString(writer.getAnnotation(HideFor.class).roles()) );
return;
}
Field[] fields = jgen.getCurrentValue().getClass().getDeclaredFields();
Optional<Field> field = Arrays.stream(fields)
.filter(f-> f.getName().equalsIgnoreCase(writer.getName())).findAny();
if(field.isPresent() && field.get().getAnnotation(HideFor.class)!=null){
if(isHidable( Arrays.asList(writer.getAnnotation(HideFor.class).roles()))){
System.out.println("Found restriction on the field " + field.get().getName() + " Restriction For " + Arrays.toString(writer.getAnnotation(HideFor.class).roles()));
return;
}
}
writer.serializeAsField(pojo, jgen, provider);
}
private boolean isHidable(List<String> rolesToHide){ // imlement the logic // }
}
用法:
FilterProvider filterProvider = new SimpleFilterProvider().addFilter("AccessRestrictionFilter", new AccessRestrictionFilter());
new ObjectMapper().writer(filterProvider ).writeValueAsString(myObjToFilter);
我使用 Jersey/Spring,我的配置如下所示:
@Provider
@Produces({MediaType.APPLICATION_JSON})
public class JacksonJsonProvider extends JacksonJaxbJsonProvider {
public JacksonJsonProvider(AccessRestrictionFilter filter) {
ObjectMapper objectMapper = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.setFilterProvider(new SimpleFilterProvider().addFilter("AccessRestriction", filter));
setMapper(objectMapper);
}
}
和过滤器:
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Bean("accessRestrictionFilter")
public AccessRestrictionFilter accessRestrictionFilter(){
return new AccessRestrictionFilter();
}
注意:在过滤器中我使用了安全上下文,因为过滤器的这个范围是会话(不是共享状态而是为每个用户创建新对象)
这是我的 POJO:
@JsonFilter("AccessRestrictionFilter")
public class MyClass {
@HideFor(roles = {"ROLE_USER", "ROLE_EDITOR"})
private int val;