【问题标题】:hide Jackson fields based on costume dynamic criteria for JaxRS Respose根据 Jax RS Response 的服装动态标准隐藏 Jackson 字段
【发布时间】:2019-12-31 02:01:07
【问题描述】:

想法很简单。我有一个对象,我想根据某些特定角色隐藏一些字段。

我在系统“狗”、“猫”等中有角色。

class Food{

  String name;

  @HideInfoForTheRoles({"dog", "cat"})
  String age;
}

所以我想创造这样的东西:

public String hideForRole(T object, String role){
// return new json
}

或者也许我可以覆盖一些非国有化方法来强制杰克逊根据我的注释隐藏字段?

【问题讨论】:

标签: jackson jersey jax-rs jackson2


【解决方案1】:

创建支持 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;

【讨论】:

    【解决方案2】:

    您可以使用@JsonView。这可能是最简单的解决方案,因为 @JsonView 可以与 JAX-RS 一起使用。


    或者,它可以通过BeanPropertyFilter 来实现,类似于我不久前整理的another solution

    开始定义你的注解:

    @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 注释放在您的字段中,并确保使用@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);
    

    如果你想让你的过滤器“全局”,即应用于所有bean,你可以创建一个混合类并用@JsonFilter注释它:

    @JsonFilter("hiddenForRolesPropertyFilter")
    public class HiddenForRolesPropertyFilterMixIn {
    
    }
    

    然后将mix-in类绑定到Object

    mapper.addMixIn(Object.class, HiddenForRolesPropertyFilterMixIn.class);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-27
      • 2014-05-13
      • 2018-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多