I defined class for global api error handler in spring boot 3.2.0. I added security with bearer token, I expect to return response but it says 14:41:32.831 [http-nio-0.0.0.0-11150-exec-2] ERROR o.a.c.c.C.[Tomcat].[localhost] - Exception Processing ErrorPage[errorCode=0, location=/error]
I write global api error handler class
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ApiGlobalErrorHandler {
private static final Logger LOG = LoggerFactory.getLogger(ApiGlobalErrorHandler.class.getName());
@ExceptionHandler(UnauthorizedAccessException.class)
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
@ResponseBody
protected ApiResponseWrapper handleUnauthorizedAccessException(UnauthorizedAccessException ex) {
LOG.error("UnauthorizedAccessException occurred:{},code-{}", ex.getErrorType(), ex.getErrorType().getCode());
return new ApiResponseWrapper(new ApiErrorResponse(HttpStatus.UNAUTHORIZED.value() + "." + API_ID_CODE + "." + ex.getErrorType().getCode(), ex.getErrorType().getMessagge()));
}
I expected the error response but I got 14:41:32.831 [http-nio-0.0.0.0-11150-exec-2] ERROR o.a.c.c.C.[Tomcat].[localhost] - Exception Processing ErrorPage[errorCode=0, location=/error]. how to disable processing error page? Trying without bearer token enter image description here stack trace is:
14:41:32.831 [http-nio-0.0.0.0-11150-exec-2] ERROR o.a.c.c.C.[Tomcat].[localhost] - Exception Processing ErrorPage[errorCode=0, location=/error]
rs.yettel.roamingbarring.exception.UnauthorizedAccessException: Missing Authorization header
at rs.yettel.roamingbarring.security.ApiKeyAuthFilter.getPreAuthenticatedPrincipal(ApiKeyAuthFilter.java:19)
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doAuthenticate(AbstractPreAuthenticatedProcessingFilter.java:189)
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:142)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82)
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:642)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:410)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:340)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:277)
at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:362)
at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:222)
at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:308)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:149)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:833)
Starting from Spring Security version 6.0, the framework began filtering all dispatcher types, such as
request,async,error,forward, andinclude.If you want to exclude the
errordispatcher type in your scenario, you can configure the filter chain as shown below:For additional information on this topic, refer to the Spring Security documentation at this link: https://docs.spring.io/spring-security/reference/5.8/migration/servlet/authorization.html#switch-filter-all-dispatcher-types.
It appears that your
ApiKeyAuthFilteris raising anUnauthorizedAccessException. It's important to note that exceptions thrown within Filters cannot be handled using@ControllerAdvice. To gain insights into customizing exceptions thrown within Filters, you may refer to this question for guidance.EDIT
After reviewing your code, I have several points to highlight:
As mentioned earlier, exceptions raised in the filters are not handled by your
@ControllerAdvice. To send a custom error message, it is recommended to catch the error within the filter and send the error code usingHttpServletResponse. I have provided a sample repository demonstrating how to achieve this, which you can find here: GitHub Repository.I noticed that you are using
AbstractPreAuthenticatedProcessingFilterin your case. However, if you are only overriding the abstract methods and those methods do not have access toHttpServletResponse, you won't be able to send a custom error code and/or message using this approach. In the sample repository I created, you can observe the use ofCustomSecurityFilter, which extendsOncePerRequestFilterand is added before runningBasicAuthenticationFilter. This approach is considered a cleaner way to achieve the desired functionality.For further customization, I recommend exploring the
BasicErrorControllerandDefaultErrorAttributesclasses in the Spring framework. Additionally, you can refer to the Spring Boot official documentation for more information: Spring Boot Documentation.