From cda387711f41bad751fba4b7f1611b84a24b6e84 Mon Sep 17 00:00:00 2001 From: Ger Roza Date: Sat, 6 Jul 2019 00:42:56 -0300 Subject: [PATCH] [BAEL-15425] Update Spring Security Database article (#7227) * Simplified and updated module, pom and or.baeldung.custom project * *disabled csrf in custom package for existing junit live test * fixed Integration test for new simplified spring context * Reverted pom file to its original version * modifications to address PR comments: * enabling CSRF * using annotations to obtain principal * using requestmapping shorthands --- .../custom/config/SecurityConfig.java | 47 +------ .../baeldung/custom/web/MainController.java | 24 +++- ...stomUserDetailsServiceIntegrationTest.java | 125 ++++++++++-------- 3 files changed, 87 insertions(+), 109 deletions(-) diff --git a/spring-security-mvc-boot/src/main/java/org/baeldung/custom/config/SecurityConfig.java b/spring-security-mvc-boot/src/main/java/org/baeldung/custom/config/SecurityConfig.java index f2da5af92c..739e4d3417 100644 --- a/spring-security-mvc-boot/src/main/java/org/baeldung/custom/config/SecurityConfig.java +++ b/spring-security-mvc-boot/src/main/java/org/baeldung/custom/config/SecurityConfig.java @@ -1,57 +1,12 @@ package org.baeldung.custom.config; -import org.baeldung.custom.security.MyUserDetailsService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.dao.DaoAuthenticationProvider; -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.builders.WebSecurity; -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; @Configuration -@EnableWebSecurity -@ComponentScan("org.baeldung.security") -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - @Autowired - private MyUserDetailsService userDetailsService; - - @Override - protected void configure(final AuthenticationManagerBuilder auth) throws Exception { - auth.authenticationProvider(authenticationProvider()); - } - - @Override - public void configure(WebSecurity web) throws Exception { - web.ignoring().antMatchers("/resources/**"); - } - - @Override - protected void configure(final HttpSecurity http) throws Exception { - // @formatter:off - http.authorizeRequests() - .antMatchers("/login").permitAll() - .antMatchers("/admin").hasRole("ADMIN") - .anyRequest().authenticated() - .and().formLogin().permitAll() - .and().csrf().disable(); - ; - // @formatter:on - } - - @Bean - public DaoAuthenticationProvider authenticationProvider() { - final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); - authProvider.setUserDetailsService(userDetailsService); - authProvider.setPasswordEncoder(encoder()); - return authProvider; - } +public class SecurityConfig { @Bean public PasswordEncoder encoder() { diff --git a/spring-security-mvc-boot/src/main/java/org/baeldung/custom/web/MainController.java b/spring-security-mvc-boot/src/main/java/org/baeldung/custom/web/MainController.java index 6572954427..74de45d1a8 100644 --- a/spring-security-mvc-boot/src/main/java/org/baeldung/custom/web/MainController.java +++ b/spring-security-mvc-boot/src/main/java/org/baeldung/custom/web/MainController.java @@ -3,14 +3,16 @@ package org.baeldung.custom.web; import org.baeldung.custom.persistence.dao.OrganizationRepository; import org.baeldung.custom.persistence.model.Foo; import org.baeldung.custom.persistence.model.Organization; +import org.baeldung.custom.security.MyUserPrincipal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; @@ -23,14 +25,14 @@ public class MainController { // @PostAuthorize("hasPermission(returnObject, 'read')") @PreAuthorize("hasPermission(#id, 'Foo', 'read')") - @RequestMapping(method = RequestMethod.GET, value = "/foos/{id}") + @GetMapping("/foos/{id}") @ResponseBody public Foo findById(@PathVariable final long id) { return new Foo("Sample"); } @PreAuthorize("hasPermission(#foo, 'write')") - @RequestMapping(method = RequestMethod.POST, value = "/foos") + @PostMapping("/foos") @ResponseStatus(HttpStatus.CREATED) @ResponseBody public Foo create(@RequestBody final Foo foo) { @@ -40,7 +42,7 @@ public class MainController { // @PreAuthorize("hasAuthority('FOO_READ_PRIVILEGE')") - @RequestMapping(method = RequestMethod.GET, value = "/foos") + @GetMapping("/foos") @ResponseBody public Foo findFooByName(@RequestParam final String name) { return new Foo(name); @@ -49,10 +51,18 @@ public class MainController { // @PreAuthorize("isMember(#id)") - @RequestMapping(method = RequestMethod.GET, value = "/organizations/{id}") + @GetMapping("/organizations/{id}") @ResponseBody public Organization findOrgById(@PathVariable final long id) { - return organizationRepository.findById(id).orElse(null); + return organizationRepository.findById(id) + .orElse(null); + } + + @PreAuthorize("hasPermission(#id, 'Foo', 'read')") + @GetMapping("/user") + @ResponseBody + public MyUserPrincipal retrieveUserDetails(@AuthenticationPrincipal MyUserPrincipal principal) { + return principal; } } diff --git a/spring-security-mvc-boot/src/test/java/org/baeldung/web/CustomUserDetailsServiceIntegrationTest.java b/spring-security-mvc-boot/src/test/java/org/baeldung/web/CustomUserDetailsServiceIntegrationTest.java index fb25175902..d16acc729a 100644 --- a/spring-security-mvc-boot/src/test/java/org/baeldung/web/CustomUserDetailsServiceIntegrationTest.java +++ b/spring-security-mvc-boot/src/test/java/org/baeldung/web/CustomUserDetailsServiceIntegrationTest.java @@ -1,76 +1,89 @@ package org.baeldung.web; -import static org.junit.Assert.assertEquals; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.apache.http.HttpHeaders; import org.baeldung.custom.Application; -import org.baeldung.custom.config.MvcConfig; -import org.baeldung.custom.config.SecurityConfig; -import org.baeldung.custom.persistence.dao.UserRepository; -import org.baeldung.custom.persistence.model.User; -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.baeldung.custom.persistence.model.Foo; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.test.web.servlet.MockMvc; -@RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = {Application.class}) -@WebAppConfiguration +import com.fasterxml.jackson.databind.ObjectMapper; + +@SpringBootTest(classes = { Application.class }) +@AutoConfigureMockMvc public class CustomUserDetailsServiceIntegrationTest { - private static final String USERNAME = "user"; - private static final String PASSWORD = "pass"; - private static final String USERNAME2 = "user2"; - @Autowired - private UserRepository myUserRepository; - - @Autowired - private AuthenticationProvider authenticationProvider; - - @Autowired - private PasswordEncoder passwordEncoder; - - // + private MockMvc mvc; @Test - public void givenExistingUser_whenAuthenticate_thenRetrieveFromDb() { - User user = new User(); - user.setUsername(USERNAME); - user.setPassword(passwordEncoder.encode(PASSWORD)); - - myUserRepository.save(user); - - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(USERNAME, PASSWORD); - Authentication authentication = authenticationProvider.authenticate(auth); - - assertEquals(authentication.getName(), USERNAME); + @WithUserDetails("john") + public void givenUserWithReadPermissions_whenRequestUserInfo_thenRetrieveUserData() throws Exception { + this.mvc.perform(get("/user").with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.user.privileges[0].name").value("FOO_READ_PRIVILEGE")) + .andExpect(jsonPath("$.user.organization.name").value("FirstOrg")) + .andExpect(jsonPath("$.user.username").value("john")); } - @Test(expected = BadCredentialsException.class) - public void givenIncorrectUser_whenAuthenticate_thenBadCredentialsException() { - User user = new User(); - user.setUsername(USERNAME); - user.setPassword(passwordEncoder.encode(PASSWORD)); - - myUserRepository.save(user); - - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(USERNAME2, PASSWORD); - authenticationProvider.authenticate(auth); + @Test + @WithUserDetails("tom") + public void givenUserWithWritePermissions_whenRequestUserInfo_thenRetrieveUserData() throws Exception { + this.mvc.perform(get("/user").with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.user.privileges").isArray()) + .andExpect(jsonPath("$.user.organization.name").value("SecondOrg")) + .andExpect(jsonPath("$.user.username").value("tom")); } - // + @Test + @WithUserDetails("john") + public void givenUserWithReadPermissions_whenRequestFoo_thenRetrieveSampleFoo() throws Exception { + this.mvc.perform(get("/foos/1").with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value("Sample")); + } - @After - public void tearDown() { - myUserRepository.removeUserByUsername(USERNAME); + @Test + @WithAnonymousUser + public void givenAnonymous_whenRequestFoo_thenRetrieveUnauthorized() throws Exception { + this.mvc.perform(get("/foos/1").with(csrf())) + .andExpect(status().isUnauthorized()); + } + + @Test + @WithUserDetails("john") + public void givenUserWithReadPermissions_whenCreateNewFoo_thenForbiddenStatusRetrieved() throws Exception { + this.mvc.perform(post("/foos").with(csrf()) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .content(asJsonString(new Foo()))) + .andExpect(status().isForbidden()); + } + + @Test + @WithUserDetails("tom") + public void givenUserWithWritePermissions_whenCreateNewFoo_thenOkStatusRetrieved() throws Exception { + this.mvc.perform(post("/foos").with(csrf()) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .content(asJsonString(new Foo()))) + .andExpect(status().isCreated()); + } + + private static String asJsonString(final Object obj) throws Exception { + final ObjectMapper mapper = new ObjectMapper(); + final String jsonContent = mapper.writeValueAsString(obj); + return jsonContent; } }