In Spring Boot 2.2.1 I have done it using class derived from AuthenticationEntryPoint:
import java.io.IOException;
import javassist.NotFoundException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class AppAuthenticationEntryPoint implements AuthenticationEntryPoint{
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException auth) throws IOException, ServletException {
// 401
setResponseError(response, HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
}
@ExceptionHandler (value = {AccessDeniedException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
// 403
setResponseError(response, HttpServletResponse.SC_FORBIDDEN, String.format("Access Denies: %s", accessDeniedException.getMessage()));
}
@ExceptionHandler (value = {NotFoundException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, NotFoundException notFoundException) throws IOException {
// 404
setResponseError(response, HttpServletResponse.SC_NOT_FOUND, String.format("Not found: %s", notFoundException.getMessage()));
}
@ExceptionHandler (value = {Exception.class})
public void commence(HttpServletRequest request, HttpServletResponse response, Exception exception) throws IOException {
// 500
setResponseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, String.format("Internal Server Error: %s", exception.getMessage()));
}
private void setResponseError(HttpServletResponse response, int errorCode, String errorMessage) throws IOException{
response.setStatus(errorCode);
response.getWriter().write(errorMessage);
response.getWriter().flush();
response.getWriter().close();
}
}
And in You security config (I have ResourceServer that is accessible by OAuth2.0 token)
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().sameOrigin(); // it is to fix issue with h2-console access
http.csrf().disable()
.authorizeRequests().antMatchers("/", "/callback", "/login**", "/webjars/**", "/error**").permitAll()
.and()
.authorizeRequests().antMatchers("/api/**").authenticated()
.and()
.authorizeRequests().antMatchers("/h2-console/**").permitAll()
.and()
.authorizeRequests().antMatchers("/swagger-ui.html").permitAll()
.and()
.authorizeRequests().antMatchers("/swagger-ui/**").permitAll()
.and()
.exceptionHandling().authenticationEntryPoint(new AppAuthenticationEntryPoint())
.and()
.logout().permitAll().logoutSuccessUrl("/");
}
@Bean
public PrincipalExtractor getPrincipalExtractor(){
return new KeyCloakUserInfoExtractorService();
}
@Autowired
private ResourceServerTokenServices resourceServerTokenServices;
}