Merge pull request #12315 from tirumani03/dev/tirumani03/springSecurityException
BAEL-5533: Spring security exceptions
This commit is contained in:
commit
997307e55f
|
@ -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…
Reference in New Issue