根据 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;