BAEL-5533: Spring security exceptions
This commit is contained in:
		
							parent
							
								
									908387dbfb
								
							
						
					
					
						commit
						3816e3371a
					
				| @ -0,0 +1,13 @@ | ||||
| package com.baeldung.global.exceptionhandler; | ||||
| 
 | ||||
| import org.springframework.boot.SpringApplication; | ||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
| 
 | ||||
| @SpringBootApplication | ||||
| public class Application { | ||||
| 
 | ||||
|     public static void main(String[] args) { | ||||
|         SpringApplication.run(Application.class, args); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| package com.baeldung.global.exceptionhandler.controller; | ||||
| 
 | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.http.ResponseEntity; | ||||
| import org.springframework.web.bind.annotation.PostMapping; | ||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| 
 | ||||
| import com.baeldung.global.exceptionhandler.handler.RestResponse; | ||||
| 
 | ||||
| @RestController | ||||
| @RequestMapping | ||||
| public class LoginController { | ||||
| 
 | ||||
|     @PostMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE) | ||||
|     public ResponseEntity<RestResponse> login() { | ||||
|         return ResponseEntity.ok(new RestResponse("Success")); | ||||
|     } | ||||
| 
 | ||||
|     @PostMapping(value = "/login-handler", produces = MediaType.APPLICATION_JSON_VALUE) | ||||
|     public ResponseEntity<RestResponse> loginWithExceptionHandler() { | ||||
|         return ResponseEntity.ok(new RestResponse("Success")); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| package com.baeldung.global.exceptionhandler.handler; | ||||
| 
 | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.http.ResponseEntity; | ||||
| import org.springframework.security.core.AuthenticationException; | ||||
| import org.springframework.web.bind.annotation.ControllerAdvice; | ||||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||||
| import org.springframework.web.bind.annotation.ResponseBody; | ||||
| import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | ||||
| 
 | ||||
| @ControllerAdvice | ||||
| public class DefaultExceptionHandler extends ResponseEntityExceptionHandler { | ||||
| 
 | ||||
|     @ExceptionHandler({ AuthenticationException.class }) | ||||
|     @ResponseBody | ||||
|     public ResponseEntity<RestError> handleAuthenticationException(Exception ex) { | ||||
| 
 | ||||
|         RestError re = new RestError(HttpStatus.UNAUTHORIZED.toString(), "Authentication failed at controller advice"); | ||||
|         return ResponseEntity.status(HttpStatus.UNAUTHORIZED) | ||||
|             .body(re); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package com.baeldung.global.exceptionhandler.handler; | ||||
| 
 | ||||
| public class RestError { | ||||
| 
 | ||||
|     String errorCode; | ||||
|     String errorMessage; | ||||
| 
 | ||||
|     public RestError(String errorCode, String errorMessage) { | ||||
|         super(); | ||||
|         this.errorCode = errorCode; | ||||
|         this.errorMessage = errorMessage; | ||||
|     } | ||||
| 
 | ||||
|     public String getErrorCode() { | ||||
|         return errorCode; | ||||
|     } | ||||
| 
 | ||||
|     public void setErrorCode(String errorCode) { | ||||
|         this.errorCode = errorCode; | ||||
|     } | ||||
| 
 | ||||
|     public String getErrorMessage() { | ||||
|         return errorMessage; | ||||
|     } | ||||
| 
 | ||||
|     public void setErrorMessage(String errorMessage) { | ||||
|         this.errorMessage = errorMessage; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| package com.baeldung.global.exceptionhandler.handler; | ||||
| 
 | ||||
| public class RestResponse { | ||||
| 
 | ||||
|     String message; | ||||
| 
 | ||||
|     public RestResponse(String message) { | ||||
|         super(); | ||||
|         this.message = message; | ||||
|     } | ||||
| 
 | ||||
|     public String getMessage() { | ||||
|         return message; | ||||
|     } | ||||
| 
 | ||||
|     public void setMessage(String message) { | ||||
|         this.message = message; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| package com.baeldung.global.exceptionhandler.security; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.io.OutputStream; | ||||
| 
 | ||||
| import javax.servlet.ServletException; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
| 
 | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.security.core.AuthenticationException; | ||||
| import org.springframework.security.web.AuthenticationEntryPoint; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| import com.baeldung.global.exceptionhandler.handler.RestError; | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
| 
 | ||||
| @Component("customAuthenticationEntryPoint") | ||||
| public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||||
| 
 | ||||
|     @Override | ||||
|     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { | ||||
| 
 | ||||
|         RestError re = new RestError(HttpStatus.UNAUTHORIZED.toString(), "Authentication failed"); | ||||
|          | ||||
|         response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||||
|         response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||||
|         OutputStream responseStream = response.getOutputStream(); | ||||
|         ObjectMapper mapper = new ObjectMapper(); | ||||
|         mapper.writeValue(responseStream, re); | ||||
|         responseStream.flush(); | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,58 @@ | ||||
| package com.baeldung.global.exceptionhandler.security; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Qualifier; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| 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.core.userdetails.User; | ||||
| import org.springframework.security.core.userdetails.UserDetails; | ||||
| import org.springframework.security.core.userdetails.UserDetailsService; | ||||
| import org.springframework.security.provisioning.InMemoryUserDetailsManager; | ||||
| import org.springframework.security.web.AuthenticationEntryPoint; | ||||
| 
 | ||||
| @Configuration | ||||
| @EnableWebSecurity | ||||
| public class CustomSecurityConfig extends WebSecurityConfigurerAdapter { | ||||
| 
 | ||||
|     @Autowired | ||||
|     @Qualifier("customAuthenticationEntryPoint") | ||||
|     AuthenticationEntryPoint authEntryPoint; | ||||
| 
 | ||||
|     @Bean | ||||
|     public UserDetailsService userDetailsService() { | ||||
|         UserDetails admin = User.withUsername("admin") | ||||
|             .password("password") | ||||
|             .roles("ADMIN") | ||||
|             .build(); | ||||
|         InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager(); | ||||
|         userDetailsManager.createUser(admin); | ||||
|         return userDetailsManager; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void configure(HttpSecurity http) throws Exception { | ||||
|         http.requestMatchers() | ||||
|             .antMatchers("/login") | ||||
|             .and() | ||||
|             .authorizeRequests() | ||||
|             .anyRequest() | ||||
|             .hasRole("ADMIN") | ||||
|             .and() | ||||
|             .httpBasic() | ||||
|             .and() | ||||
|             .exceptionHandling() | ||||
|             .authenticationEntryPoint(authEntryPoint); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void configure(AuthenticationManagerBuilder auth) throws Exception { | ||||
|         auth.inMemoryAuthentication() | ||||
|             .withUser("admin") | ||||
|             .password("password") | ||||
|             .roles("ADMIN"); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| package com.baeldung.global.exceptionhandler.security; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import javax.servlet.ServletException; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Qualifier; | ||||
| import org.springframework.security.core.AuthenticationException; | ||||
| import org.springframework.security.web.AuthenticationEntryPoint; | ||||
| import org.springframework.stereotype.Component; | ||||
| import org.springframework.web.servlet.HandlerExceptionResolver; | ||||
| 
 | ||||
| @Component("delegatedAuthenticationEntryPoint") | ||||
| public class DelegatedAuthenticationEntryPoint implements AuthenticationEntryPoint { | ||||
| 
 | ||||
|     @Autowired | ||||
|     @Qualifier("handlerExceptionResolver") | ||||
|     private HandlerExceptionResolver resolver; | ||||
| 
 | ||||
|     @Override | ||||
|     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { | ||||
|         resolver.resolveException(request, response, null, authException); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| package com.baeldung.global.exceptionhandler.security; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Qualifier; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.core.annotation.Order; | ||||
| 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.web.AuthenticationEntryPoint; | ||||
| 
 | ||||
| @Configuration | ||||
| @EnableWebSecurity | ||||
| @Order(101) | ||||
| public class DelegatedSecurityConfig extends WebSecurityConfigurerAdapter { | ||||
| 
 | ||||
|     @Autowired | ||||
|     @Qualifier("delegatedAuthenticationEntryPoint") | ||||
|     AuthenticationEntryPoint authEntryPoint; | ||||
| 
 | ||||
|     @Override | ||||
|     protected void configure(HttpSecurity http) throws Exception { | ||||
|         http.requestMatchers() | ||||
|             .antMatchers("/login-handler") | ||||
|             .and() | ||||
|             .authorizeRequests() | ||||
|             .anyRequest() | ||||
|             .hasRole("ADMIN") | ||||
|             .and() | ||||
|             .httpBasic() | ||||
|             .and() | ||||
|             .exceptionHandling() | ||||
|             .authenticationEntryPoint(authEntryPoint); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void configure(AuthenticationManagerBuilder auth) throws Exception { | ||||
|         auth.inMemoryAuthentication() | ||||
|             .withUser("admin") | ||||
|             .password("password") | ||||
|             .roles("ADMIN"); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,52 @@ | ||||
| package com.baeldung.global.exceptionhandler; | ||||
| 
 | ||||
| import static org.hamcrest.Matchers.is; | ||||
| import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; | ||||
| import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; | ||||
| import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||||
| 
 | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; | ||||
| import org.springframework.context.annotation.Import; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.security.test.context.support.WithMockUser; | ||||
| import org.springframework.test.context.junit4.SpringRunner; | ||||
| import org.springframework.test.web.servlet.MockMvc; | ||||
| 
 | ||||
| import com.baeldung.global.exceptionhandler.controller.LoginController; | ||||
| import com.baeldung.global.exceptionhandler.handler.RestError; | ||||
| import com.baeldung.global.exceptionhandler.security.CustomAuthenticationEntryPoint; | ||||
| import com.baeldung.global.exceptionhandler.security.DelegatedAuthenticationEntryPoint; | ||||
| import com.baeldung.global.exceptionhandler.security.DelegatedSecurityConfig; | ||||
| 
 | ||||
| @RunWith(SpringRunner.class) | ||||
| @WebMvcTest(DelegatedSecurityConfig.class) | ||||
| @Import({ LoginController.class, CustomAuthenticationEntryPoint.class, DelegatedAuthenticationEntryPoint.class }) | ||||
| public class DelegatedSecurityConfigUnitTest { | ||||
| 
 | ||||
|     @Autowired | ||||
|     private MockMvc mvc; | ||||
| 
 | ||||
|     @Test | ||||
|     @WithMockUser(username = "admin", roles = { "ADMIN" }) | ||||
|     public void whenUserAccessLogin_shouldSucceed() throws Exception { | ||||
|         mvc.perform(formLogin("/login-handler").user("username", "admin") | ||||
|             .password("password", "password") | ||||
|             .acceptMediaType(MediaType.APPLICATION_JSON)) | ||||
|             .andExpect(status().isOk()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void whenUserAccessWithWrongCredentialsWithDelegatedEntryPoint_shouldFail() throws Exception { | ||||
|         RestError re = new RestError(HttpStatus.UNAUTHORIZED.toString(), "Authentication failed at controller advice"); | ||||
|         mvc.perform(formLogin("/login-handler").user("username", "admin") | ||||
|             .password("password", "wrong") | ||||
|             .acceptMediaType(MediaType.APPLICATION_JSON)) | ||||
|             .andExpect(status().isUnauthorized()) | ||||
|             .andExpect(jsonPath("$.errorMessage", is(re.getErrorMessage()))); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,54 @@ | ||||
| package com.baeldung.global.exceptionhandler; | ||||
| 
 | ||||
| import static org.hamcrest.Matchers.is; | ||||
| import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; | ||||
| import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; | ||||
| import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||||
| 
 | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; | ||||
| import org.springframework.context.annotation.Import; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.security.test.context.support.WithMockUser; | ||||
| import org.springframework.test.context.junit4.SpringRunner; | ||||
| import org.springframework.test.web.servlet.MockMvc; | ||||
| 
 | ||||
| import com.baeldung.global.exceptionhandler.controller.LoginController; | ||||
| import com.baeldung.global.exceptionhandler.handler.RestError; | ||||
| import com.baeldung.global.exceptionhandler.security.CustomAuthenticationEntryPoint; | ||||
| import com.baeldung.global.exceptionhandler.security.CustomSecurityConfig; | ||||
| import com.baeldung.global.exceptionhandler.security.DelegatedAuthenticationEntryPoint; | ||||
| 
 | ||||
| @RunWith(SpringRunner.class) | ||||
| @WebMvcTest(CustomSecurityConfig.class) | ||||
| @Import({LoginController.class, CustomAuthenticationEntryPoint.class, DelegatedAuthenticationEntryPoint.class}) | ||||
| public class SecurityConfigUnitTest { | ||||
| 
 | ||||
|     @Autowired | ||||
|     private MockMvc mvc; | ||||
| 
 | ||||
|     @Test | ||||
|     @WithMockUser(username = "admin", roles = { "ADMIN" }) | ||||
|     public void whenUserAccessLogin_shouldSucceed() throws Exception { | ||||
|         mvc.perform(formLogin("/login").user("username", "admin") | ||||
|             .password("password", "password") | ||||
|             .acceptMediaType(MediaType.APPLICATION_JSON)) | ||||
|             .andExpect(status().isOk()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void whenUserAccessWithWrongCredentialsWithDelegatedEntryPoint_shouldFail() throws Exception { | ||||
|         RestError re = new RestError(HttpStatus.UNAUTHORIZED.toString(), "Authentication failed"); | ||||
|         mvc.perform(formLogin("/login").user("username", "admin") | ||||
|             .password("password", "wrong") | ||||
|             .acceptMediaType(MediaType.APPLICATION_JSON)) | ||||
|             .andExpect(status().isUnauthorized()) | ||||
|             .andExpect(jsonPath("$.errorMessage", is(re.getErrorMessage()))); | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user