diff --git a/pom.xml b/pom.xml
index 787a03c2fb..58d57ade05 100644
--- a/pom.xml
+++ b/pom.xml
@@ -733,6 +733,7 @@
spring-security-mvc-boot
spring-security-mvc-custom
spring-security-mvc-digest-auth
+ spring-security-mvc-jsonview
spring-security-mvc-ldap
spring-security-mvc-login
spring-security-mvc-persisted-remember-me
diff --git a/spring-security-mvc-jsonview/.gitignore b/spring-security-mvc-jsonview/.gitignore
new file mode 100644
index 0000000000..83c05e60c8
--- /dev/null
+++ b/spring-security-mvc-jsonview/.gitignore
@@ -0,0 +1,13 @@
+*.class
+
+#folders#
+/target
+/neoDb*
+/data
+/src/main/webapp/WEB-INF/classes
+*/META-INF/*
+
+# Packaged files #
+*.jar
+*.war
+*.ear
\ No newline at end of file
diff --git a/spring-security-mvc-jsonview/pom.xml b/spring-security-mvc-jsonview/pom.xml
new file mode 100644
index 0000000000..7f1129128f
--- /dev/null
+++ b/spring-security-mvc-jsonview/pom.xml
@@ -0,0 +1,222 @@
+
+ 4.0.0
+
+ com.baeldung
+ spring-security-mvc-jsonview
+ 0.1-SNAPSHOT
+ spring-security-mvc-jsonview
+ war
+
+
+ com.baeldung
+ parent-spring-5
+ 0.0.1-SNAPSHOT
+ ../parent-spring-5
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.9.7
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.9.7
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.9.7
+
+
+
+
+
+ org.springframework.security
+ spring-security-web
+ ${org.springframework.security.version}
+
+
+ org.springframework.security
+ spring-security-config
+ ${org.springframework.security.version}
+
+
+ org.springframework.security
+ spring-security-taglibs
+ ${org.springframework.security.version}
+
+
+
+
+
+ org.springframework
+ spring-core
+ ${spring.version}
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+ org.springframework
+ spring-jdbc
+ ${spring.version}
+
+
+ org.springframework
+ spring-beans
+ ${spring.version}
+
+
+ org.springframework
+ spring-aop
+ ${spring.version}
+
+
+ org.springframework
+ spring-tx
+ ${spring.version}
+
+
+ org.springframework
+ spring-expression
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-web
+ ${spring.version}
+
+
+ org.springframework
+ spring-webmvc
+ ${spring.version}
+
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ ${javax.servlet.version}
+ provided
+
+
+
+ javax.servlet
+ jstl
+ ${jstl.version}
+ runtime
+
+
+
+
+
+ org.springframework
+ spring-test
+ ${spring.version}
+ test
+
+
+ org.springframework.security
+ spring-security-test
+ ${org.springframework.security.version}
+ test
+
+
+ com.jayway.jsonpath
+ json-path
+ 2.4.0
+ test
+
+
+
+
+
+ spring-security-mvc-jsonview
+
+
+ src/main/resources
+ true
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ ${maven-war-plugin.version}
+
+
+ default-war
+ prepare-package
+
+ false
+
+
+
+
+
+
+ org.codehaus.cargo
+ cargo-maven2-plugin
+ ${cargo-maven2-plugin.version}
+
+
+
+ ${project.build.directory}/${project.name}.war
+ war
+
+ /
+
+
+
+
+ 2400000
+ tomcat8x
+ embedded
+
+
+
+
+
+
+ 8084
+
+
+
+
+
+
+
+
+
+
+
+ 5.0.5.RELEASE
+
+
+ 3.1.0
+ 1.2
+
+
+ 2.6
+ 1.6.1
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-mvc-jsonview/src/main/java/com/baeldung/AppInitializer.java b/spring-security-mvc-jsonview/src/main/java/com/baeldung/AppInitializer.java
new file mode 100644
index 0000000000..4f38d190eb
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/main/java/com/baeldung/AppInitializer.java
@@ -0,0 +1,33 @@
+package com.baeldung;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+
+import org.springframework.web.WebApplicationInitializer;
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.context.support.GenericWebApplicationContext;
+import org.springframework.web.filter.DelegatingFilterProxy;
+import org.springframework.web.servlet.DispatcherServlet;
+
+public class AppInitializer implements WebApplicationInitializer {
+
+ @Override
+ public void onStartup(final ServletContext sc) throws ServletException {
+
+ AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
+
+ root.scan("com.baeldung");
+ sc.addListener(new ContextLoaderListener(root));
+
+ ServletRegistration.Dynamic appServlet = sc.addServlet("mvc", new DispatcherServlet(new GenericWebApplicationContext()));
+ appServlet.setLoadOnStartup(1);
+ appServlet.addMapping("/");
+
+ sc.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
+ .addMappingForUrlPatterns(null, false, "/*");
+
+ }
+
+}
diff --git a/spring-security-mvc-jsonview/src/main/java/com/baeldung/controller/ItemsController.java b/spring-security-mvc-jsonview/src/main/java/com/baeldung/controller/ItemsController.java
new file mode 100644
index 0000000000..16268a239b
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/main/java/com/baeldung/controller/ItemsController.java
@@ -0,0 +1,21 @@
+package com.baeldung.controller;
+
+import com.baeldung.model.Item;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RestController
+public class ItemsController {
+
+ @RequestMapping("/items")
+ public Collection- getItems() {
+ return Arrays.asList(new Item(1, "Item 1", "Frank"), new Item(2, "Item 2", "Bob"));
+ }
+
+}
diff --git a/spring-security-mvc-jsonview/src/main/java/com/baeldung/controller/View.java b/spring-security-mvc-jsonview/src/main/java/com/baeldung/controller/View.java
new file mode 100644
index 0000000000..10ae50adef
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/main/java/com/baeldung/controller/View.java
@@ -0,0 +1,24 @@
+package com.baeldung.controller;
+
+import com.baeldung.spring.AppConfig.Role;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class View {
+
+ public static final Map MAPPING = new HashMap<>();
+
+ static {
+ MAPPING.put(Role.ROLE_ADMIN, Admin.class);
+ MAPPING.put(Role.ROLE_USER, User.class);
+ }
+
+ public static class User {
+
+ }
+
+ public static class Admin extends User {
+
+ }
+}
diff --git a/spring-security-mvc-jsonview/src/main/java/com/baeldung/model/Item.java b/spring-security-mvc-jsonview/src/main/java/com/baeldung/model/Item.java
new file mode 100644
index 0000000000..002ee73e4f
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/main/java/com/baeldung/model/Item.java
@@ -0,0 +1,32 @@
+package com.baeldung.model;
+
+import com.baeldung.controller.View;
+import com.fasterxml.jackson.annotation.JsonView;
+
+public class Item {
+
+ @JsonView(View.User.class)
+ private int id;
+ @JsonView(View.User.class)
+ private String name;
+ @JsonView(View.Admin.class)
+ private String ownerName;
+
+ public Item(int id, String name, String ownerName) {
+ this.id = id;
+ this.name = name;
+ this.ownerName = ownerName;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getOwnerName() {
+ return ownerName;
+ }
+}
diff --git a/spring-security-mvc-jsonview/src/main/java/com/baeldung/spring/AppConfig.java b/spring-security-mvc-jsonview/src/main/java/com/baeldung/spring/AppConfig.java
new file mode 100644
index 0000000000..10b2d2447e
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/main/java/com/baeldung/spring/AppConfig.java
@@ -0,0 +1,51 @@
+package com.baeldung.spring;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.Arrays;
+
+@Configuration
+@EnableWebMvc
+@EnableWebSecurity
+@ComponentScan("com.baeldung")
+public class AppConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
+
+ @Override
+ protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
+ auth.inMemoryAuthentication()
+ .withUser("user").password(passwordEncoder().encode("userPass")).roles("USER")
+ .and()
+ .withUser("admin").password(passwordEncoder().encode("adminPass")).roles("ADMIN");
+ }
+
+ @Override
+ protected void configure(final HttpSecurity http) throws Exception {
+ http
+ .csrf().disable()
+ .authorizeRequests()
+ .anyRequest().authenticated()
+ .and().httpBasic();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+
+
+ public enum Role {
+ ROLE_USER,
+ ROLE_ADMIN
+ }
+}
diff --git a/spring-security-mvc-jsonview/src/main/java/com/baeldung/spring/SecurityJsonViewControllerAdvice.java b/spring-security-mvc-jsonview/src/main/java/com/baeldung/spring/SecurityJsonViewControllerAdvice.java
new file mode 100644
index 0000000000..66c7207e91
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/main/java/com/baeldung/spring/SecurityJsonViewControllerAdvice.java
@@ -0,0 +1,38 @@
+package com.baeldung.spring;
+
+import com.baeldung.controller.View;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.MappingJacksonValue;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.AbstractMappingJacksonResponseBodyAdvice;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RestControllerAdvice
+public class SecurityJsonViewControllerAdvice extends AbstractMappingJacksonResponseBodyAdvice {
+
+ @Override
+ protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
+ MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
+ if (SecurityContextHolder.getContext().getAuthentication() != null
+ && SecurityContextHolder.getContext().getAuthentication().getAuthorities() != null) {
+ Collection extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
+ List jsonViews = authorities.stream()
+ .map(GrantedAuthority::getAuthority)
+ .map(AppConfig.Role::valueOf)
+ .map(View.MAPPING::get)
+ .collect(Collectors.toList());
+ if (jsonViews.size() == 1) {
+ bodyContainer.setSerializationView(jsonViews.get(0));
+ }
+ throw new IllegalArgumentException("Ambiguous @JsonView declaration for roles "+ authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(",")));
+ }
+ }
+}
diff --git a/spring-security-mvc-jsonview/src/main/resources/logback.xml b/spring-security-mvc-jsonview/src/main/resources/logback.xml
new file mode 100644
index 0000000000..56af2d397e
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/main/resources/logback.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-mvc-jsonview/src/test/java/com/baeldung/SpringContextIntegrationTest.java b/spring-security-mvc-jsonview/src/test/java/com/baeldung/SpringContextIntegrationTest.java
new file mode 100644
index 0000000000..e22cd3d71a
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/test/java/com/baeldung/SpringContextIntegrationTest.java
@@ -0,0 +1,17 @@
+package com.baeldung;
+
+import com.baeldung.spring.AppConfig;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = AppConfig.class)
+@WebAppConfiguration
+public class SpringContextIntegrationTest {
+ @Test
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+}
diff --git a/spring-security-mvc-jsonview/src/test/java/com/baeldung/security/SpringSecurityJsonViewIntegrationTest.java b/spring-security-mvc-jsonview/src/test/java/com/baeldung/security/SpringSecurityJsonViewIntegrationTest.java
new file mode 100644
index 0000000000..626c8cb497
--- /dev/null
+++ b/spring-security-mvc-jsonview/src/test/java/com/baeldung/security/SpringSecurityJsonViewIntegrationTest.java
@@ -0,0 +1,85 @@
+package com.baeldung.security;
+
+import com.baeldung.spring.AppConfig;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.util.NestedServletException;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = AppConfig.class)
+@WebAppConfiguration
+public class SpringSecurityJsonViewIntegrationTest {
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mvc;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Before
+ public void setup() {
+ mvc = MockMvcBuilders
+ .webAppContextSetup(context)
+ .build();
+ }
+
+ @Test
+ @WithMockUser(username = "admin", password = "adminPass", roles = "ADMIN")
+ public void whenAdminRequests_thenOwnerNameIsPresent() throws Exception {
+ mvc.perform(get("/items"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[0].id").value(1))
+ .andExpect(jsonPath("$[0].name").value("Item 1"))
+ .andExpect(jsonPath("$[0].ownerName").exists());
+ }
+
+ @Test
+ @WithMockUser(username = "user", password = "userPass", roles = "USER")
+ public void whenUserRequests_thenOwnerNameIsAbsent() throws Exception {
+ mvc.perform(get("/items"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[0].id").value(1))
+ .andExpect(jsonPath("$[0].name").value("Item 1"))
+ .andExpect(jsonPath("$[0].ownerName").doesNotExist());
+ }
+
+ @Test
+ @WithMockUser(username = "user", password = "userPass", roles = {"ADMIN", "USER"})
+ public void whenMultipleRoles_thenExceptionIsThrown() throws Exception {
+ expectedException.expect(new BaseMatcher() {
+ @Override
+ public boolean matches(Object o) {
+ NestedServletException exception = (NestedServletException) o;
+ return exception.getCause() instanceof IllegalArgumentException && exception.getCause().getMessage().equals("Ambiguous @JsonView declaration for roles ROLE_ADMIN,ROLE_USER");
+ }
+
+ @Override
+ public void describeTo(Description description) {
+
+ }
+ });
+
+ mvc.perform(get("/items"))
+ .andExpect(status().isOk());
+
+ }
+}