How to Manage Exceptions Thrown in Filters in Spring

How to manage exceptions thrown in filters in Spring?

So this is what I did:

I read the basics about filters here and I figured out that I need to create a custom filter that will be first in the filter chain and will have a try catch to catch all runtime exceptions that might occur there. Then i need to create the json manually and put it in the response.

So here is my custom filter:

public class ExceptionHandlerFilter extends OncePerRequestFilter {

@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (RuntimeException e) {

// custom error response class used across my project
ErrorResponse errorResponse = new ErrorResponse(e);

response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getWriter().write(convertObjectToJson(errorResponse));
}
}

public String convertObjectToJson(Object object) throws JsonProcessingException {
if (object == null) {
return null;
}
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(object);
}
}

And then i added it in the web.xml before the CorsFilter. And it works!

<filter> 
<filter-name>exceptionHandlerFilter</filter-name>
<filter-class>xx.xxxxxx.xxxxx.api.controllers.filters.ExceptionHandlerFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>exceptionHandlerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

exception handling for filter in spring

Filters happens before controllers are even resolved so exceptions thrown from filters can't be caught by a Controller Advice.

Filters are a part of the servlet and not really the MVC stack.

How to handle custom exceptions thrown by a filter in Spring Security

Spring security has a filter which is called the ExceptionTranslationFilter which translates AccessDeniedException and AuthenticationException into responses. This filter catches these thrown exceptions in the spring security filter chain.

So if you want to return a custom exception, you could instead inherit from one of these classes instead of RuntimeException and add a custom message.

I just want to emphasis and it can never be said too many times:

Providing friendly error messages in production applications when it comes to authentication/authorization is in general bad practice from a security standpoint. These types of messages can benefit malicious actors, when trying out things so that they realize what they have done wrong and guide them in their hacking attempts.

Providing friendly messages in test environments may be okey, but make sure that they are disabled in production. In production all failed authentication attempts a recommendation is to return a 401 with no additional information. And in graphical clients, generalized error messages should be displayed for instance "failed to authenticate" with no given specifics.

Also:

Writing custom security as you have done is also in general bad practice. Spring security is battle tested with 100000 of applications running it in production environments. Writing a custom filter to handle token and passwords, is in general not needed. Spring security already has implemented filters to handle security and authentication using standards like BASIC authentication and TOKEN/JWT. If you implement a non standard login, one bug might expose your application to a huge risk.

  • Username and password authentication in spring
  • Oauth2 authentication in spring

How to globally handle errors thrown from WebFilter in Spring WebFlux?

Three steps are required to get full control over all exceptions thrown from application endpoints handling code:

  1. Implement org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler
  2. Annotate with @ControllerAdvice (or just @Component)
  3. Set @Priority less than 1 to let the custom handler run before the default one (WebFluxResponseStatusExceptionHandler)

The tricky part is where we get an instance implementing
ServerResponse.Context for passing to
ServerResponse.writeTo(exchange, context). I did not find the final
answer, and comments are welcome. In the internal Spring code they always create a new instance of context for each writeTo invocation,
although in all cases (I've manged to find) the context instance is immutable.
That is why I ended up using the same ResponseContextInstance for all responses.
At the moment no problems detected with this approach.



@ControllerAdvice
@Priority(0) /* should go before WebFluxResponseStatusExceptionHandler */
class CustomWebExceptionHandler : ErrorWebExceptionHandler {

private val log = logger(CustomWebExceptionHandler::class)

override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> {
log.error("handled ${ex.javaClass.simpleName}", ex)

val sr = when (ex) {
is FirstException -> handleFirst(ex)
is SecondException -> handleSecond(ex)
else -> defaultException(ex)
}

return sr.flatMap { it.writeTo(exchange, ResponseContextInstance) }.then()
}

private fun handleFirst(ex: FirstException): Mono<ServerResponse> {
return ServerResponse
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.bodyValue("first")
}

private fun handleSecond(ex: SecondException): Mono<ServerResponse> {
return ServerResponse.status(HttpStatus.BAD_REQUEST).bodyValue("second")
}

private object ResponseContextInstance : ServerResponse.Context {

val strategies: HandlerStrategies = HandlerStrategies.withDefaults()

override fun messageWriters(): List<HttpMessageWriter<*>> {
return strategies.messageWriters()
}

override fun viewResolvers(): List<ViewResolver> {
return strategies.viewResolvers()
}
}
}


Related Topics



Leave a reply



Submit