[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
This commit is contained in:
Ger Roza 2019-07-06 00:42:56 -03:00 committed by Josh Cummings
parent 404d9b65be
commit cda387711f
3 changed files with 87 additions and 109 deletions

View File

@ -1,57 +1,12 @@
package org.baeldung.custom.config; 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.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; 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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration @Configuration
@EnableWebSecurity public class SecurityConfig {
@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;
}
@Bean @Bean
public PasswordEncoder encoder() { public PasswordEncoder encoder() {

View File

@ -3,14 +3,16 @@ package org.baeldung.custom.web;
import org.baeldung.custom.persistence.dao.OrganizationRepository; import org.baeldung.custom.persistence.dao.OrganizationRepository;
import org.baeldung.custom.persistence.model.Foo; import org.baeldung.custom.persistence.model.Foo;
import org.baeldung.custom.persistence.model.Organization; import org.baeldung.custom.persistence.model.Organization;
import org.baeldung.custom.security.MyUserPrincipal;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller; 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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; 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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
@ -23,14 +25,14 @@ public class MainController {
// @PostAuthorize("hasPermission(returnObject, 'read')") // @PostAuthorize("hasPermission(returnObject, 'read')")
@PreAuthorize("hasPermission(#id, 'Foo', 'read')") @PreAuthorize("hasPermission(#id, 'Foo', 'read')")
@RequestMapping(method = RequestMethod.GET, value = "/foos/{id}") @GetMapping("/foos/{id}")
@ResponseBody @ResponseBody
public Foo findById(@PathVariable final long id) { public Foo findById(@PathVariable final long id) {
return new Foo("Sample"); return new Foo("Sample");
} }
@PreAuthorize("hasPermission(#foo, 'write')") @PreAuthorize("hasPermission(#foo, 'write')")
@RequestMapping(method = RequestMethod.POST, value = "/foos") @PostMapping("/foos")
@ResponseStatus(HttpStatus.CREATED) @ResponseStatus(HttpStatus.CREATED)
@ResponseBody @ResponseBody
public Foo create(@RequestBody final Foo foo) { public Foo create(@RequestBody final Foo foo) {
@ -40,7 +42,7 @@ public class MainController {
// //
@PreAuthorize("hasAuthority('FOO_READ_PRIVILEGE')") @PreAuthorize("hasAuthority('FOO_READ_PRIVILEGE')")
@RequestMapping(method = RequestMethod.GET, value = "/foos") @GetMapping("/foos")
@ResponseBody @ResponseBody
public Foo findFooByName(@RequestParam final String name) { public Foo findFooByName(@RequestParam final String name) {
return new Foo(name); return new Foo(name);
@ -49,10 +51,18 @@ public class MainController {
// //
@PreAuthorize("isMember(#id)") @PreAuthorize("isMember(#id)")
@RequestMapping(method = RequestMethod.GET, value = "/organizations/{id}") @GetMapping("/organizations/{id}")
@ResponseBody @ResponseBody
public Organization findOrgById(@PathVariable final long id) { 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;
} }
} }

View File

@ -1,76 +1,89 @@
package org.baeldung.web; 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.Application;
import org.baeldung.custom.config.MvcConfig; import org.baeldung.custom.persistence.model.Foo;
import org.baeldung.custom.config.SecurityConfig; import org.junit.jupiter.api.Test;
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.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
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.context.SpringBootTest; 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) import com.fasterxml.jackson.databind.ObjectMapper;
@SpringBootTest(classes = {Application.class})
@WebAppConfiguration @SpringBootTest(classes = { Application.class })
@AutoConfigureMockMvc
public class CustomUserDetailsServiceIntegrationTest { public class CustomUserDetailsServiceIntegrationTest {
private static final String USERNAME = "user";
private static final String PASSWORD = "pass";
private static final String USERNAME2 = "user2";
@Autowired @Autowired
private UserRepository myUserRepository; private MockMvc mvc;
@Autowired
private AuthenticationProvider authenticationProvider;
@Autowired
private PasswordEncoder passwordEncoder;
//
@Test @Test
public void givenExistingUser_whenAuthenticate_thenRetrieveFromDb() { @WithUserDetails("john")
User user = new User(); public void givenUserWithReadPermissions_whenRequestUserInfo_thenRetrieveUserData() throws Exception {
user.setUsername(USERNAME); this.mvc.perform(get("/user").with(csrf()))
user.setPassword(passwordEncoder.encode(PASSWORD)); .andExpect(status().isOk())
.andExpect(jsonPath("$.user.privileges[0].name").value("FOO_READ_PRIVILEGE"))
myUserRepository.save(user); .andExpect(jsonPath("$.user.organization.name").value("FirstOrg"))
.andExpect(jsonPath("$.user.username").value("john"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(USERNAME, PASSWORD);
Authentication authentication = authenticationProvider.authenticate(auth);
assertEquals(authentication.getName(), USERNAME);
} }
@Test(expected = BadCredentialsException.class) @Test
public void givenIncorrectUser_whenAuthenticate_thenBadCredentialsException() { @WithUserDetails("tom")
User user = new User(); public void givenUserWithWritePermissions_whenRequestUserInfo_thenRetrieveUserData() throws Exception {
user.setUsername(USERNAME); this.mvc.perform(get("/user").with(csrf()))
user.setPassword(passwordEncoder.encode(PASSWORD)); .andExpect(status().isOk())
.andExpect(jsonPath("$.user.privileges").isArray())
myUserRepository.save(user); .andExpect(jsonPath("$.user.organization.name").value("SecondOrg"))
.andExpect(jsonPath("$.user.username").value("tom"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(USERNAME2, PASSWORD);
authenticationProvider.authenticate(auth);
} }
// @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 @Test
public void tearDown() { @WithAnonymousUser
myUserRepository.removeUserByUsername(USERNAME); 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;
} }
} }