I have some fields in a model that I only want to be returned when the logged in user has the role ROLE_ADMIN
. I can use @JsonIgnore
but that hides it for everyone. How can I make it hide dynamically?

- 7,792
- 12
- 62
- 108
-
1http://stackoverflow.com/questions/11376304/right-way-to-write-json-deserializer-in-spring-or-extend-it .. does this help ? – ArunM Apr 17 '16 at 11:01
2 Answers
You should use Jackson Json Views technology to acheive it - it allows to choose a different set of fields to be serialized programatically. It is also supported by Spring
Consider you have a class Model
with two properties: commonField
which should be available for everyone and secretField
which should be available only for certain users. You should create an hierarchy of views (any classes would work) and specify which field is available in which view using @JsonView
annotation
package com.stackoverflow.jsonview;
import com.fasterxml.jackson.annotation.JsonView;
public class Model {
public static class Public {}
public static class Secret extends Public {}
@JsonView(Public.class)
private String commonField;
@JsonView(Secret.class)
private String secretField;
public Model() {
}
public Model(String commonField, String secretField) {
this.commonField = commonField;
this.secretField = secretField;
}
public String getCommonField() {
return commonField;
}
public void setCommonField(String commonField) {
this.commonField = commonField;
}
public String getSecretField() {
return secretField;
}
public void setSecretField(String secretField) {
this.secretField = secretField;
}
}
Now you can specify the view you want to use in concrete ObjectMapper
package com.stackoverflow.jsonview;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*/
public class ModelTest {
@Test
public void testSecretField() throws JsonProcessingException {
Model model = new Model("commonField","secretField");
assertEquals("{\"commonField\":\"commonField\",\"secretField\":\"secretField\"}", new ObjectMapper().writerWithView(Model.Secret.class).writeValueAsString(model));
assertEquals("{\"commonField\":\"commonField\"}", new ObjectMapper().writerWithView(Model.Public.class).writeValueAsString(model));
}
}
I am not sure if you can use declaratie approach to make spring choose the right view based on user role out of the box, so probably you will have to write some code like this:
@RequestMapping("/data")
public String getData(HttpServletRequest request) {
Model model = service.getModel();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper = request.isUserInRole("ROLE_ADMIN") ? objectMapper.writerWithView(Model.Secret.class) : objectMapper.writerWithView(Model.Public.class);
return objectMapper.writeValueAsString(model);
}

- 4,458
- 32
- 53
-
I saw this website, but I couldn't figure out how to actually use it to do what I wanted. Could you provide some sample code that hides a certain property of a model? – Tometoyou Apr 17 '16 at 11:04
-
@Tometoyou I have updated my answer with an example - we are using this pproach in our current project and it proved to be very usefull and convenient – bedrin Apr 17 '16 at 11:32
I solved this after literally a full month of trying various things. I'm working with Spring 4.3.1 and boot, with data being returned in Hal using a pagedrepository.
extend
RepositoryRestMvcConfiguration
asMyRepositoryRestMvcConfiguration
and add@Configuration
to the class, make sure your starter class has@EnableWebMvc
add this to
MyRepositoryRestMvcConfiguration
- extendTypeConstrainedMappingJackson2HttpMessageConverter
asMyResourceSupportHttpMessageConverter
add this to
MyRepositoryRestMvcConfiguration
@Override @Bean public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter() { ArrayList<MediaType> mediaTypes = new ArrayList<MediaType>(); mediaTypes.add(MediaTypes.HAL_JSON); if (config().useHalAsDefaultJsonMediaType()) { mediaTypes.add(MediaType.APPLICATION_JSON); } int order = config().useHalAsDefaultJsonMediaType() ? Ordered.LOWEST_PRECEDENCE - 10 : Ordered.LOWEST_PRECEDENCE - 1; TypeConstrainedMappingJackson2HttpMessageConverter converter = new MyResourceSupportHttpMessageConverter( order); converter.setObjectMapper(halObjectMapper()); converter.setSupportedMediaTypes(mediaTypes); converter.getObjectMapper().addMixIn(Object.class, MyFilteringMixin.class); final FilterProvider myRestrictionFilterProvider = new SimpleFilterProvider() .addFilter("MyFilteringMixin", new MyPropertyFilter()).setFailOnUnknownId(false); converter.getObjectMapper().setFilterProvider(myRestrictionFilterProvider); return converter; }
Create an empty Mixin
package filters; import com.fasterxml.jackson.annotation.JsonFilter; @JsonFilter("MyFilteringMixin") public class MyFilteringMixin {}
Create an empty Mixin create class
MyPropertyFilter
extendingSimpleBeanPropertyFilter
and override adapt this methodserializeAsField(Object, JsonGenerator, SerializerProvider, PropertyWriter)
you need to call eithersuper.serializeAsField(pPojo, pJgen, pProvider, pWriter)
orpWriter.serializeAsOmittedField(pPojo, pJgen, pProvider)
depending on whether you wish to include or discard this particular field.
I added an annotation to the particular fields I wanted to alter and interrogated that annotation when deciding which of these two to call. I injected the security role and stored permitted roles in the annotation.
This alters what Hal shares out to the caller, not what Hal is holding in its repository. Thus you can morph it depending on who the caller is.

- 216
- 1
- 11