Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security Webflux/Reactive Exception Handling

I'm building app on spring webflux, and i'm stuck because spring security webflux (v.M5) did not behave like Spring 4 in term of exception handling.

I saw following post about how to customise spring security webflux: Spring webflux custom authentication for API

If we throw exception let say in ServerSecurityContextRepository.load, Spring will update http header to 500 and nothing i can do to manipulate this exception.

However, any error thrown in controller can be handled using regular @ControllerAdvice, it just spring webflux security.

Is there anyway to handle exception in spring webflux security?

like image 337
Mistletoe Avatar asked Dec 24 '17 06:12

Mistletoe


1 Answers

The solution I found is creating a component implementing ErrorWebExceptionHandler. The instances of ErrorWebExceptionHandler bean run before Spring Security filters. Here's a sample that I use:

@Slf4j
@Component
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {

  @Autowired
  private DataBufferWriter bufferWriter;

  @Override
  public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
    AppError appError = ErrorCode.GENERIC.toAppError();

    if (ex instanceof AppException) {
        AppException ae = (AppException) ex;
        status = ae.getStatusCode();
        appError = new AppError(ae.getCode(), ae.getText());

        log.debug(appError.toString());
    } else {
        log.error(ex.getMessage(), ex);
    }

    if (exchange.getResponse().isCommitted()) {
        return Mono.error(ex);
    }

    exchange.getResponse().setStatusCode(status);
    return bufferWriter.write(exchange.getResponse(), appError);
  }
}

If you're injecting the HttpHandler instead, then it's a bit different but the idea is the same.

UPDATE: For completeness, here's my DataBufferWriter object, which is a @Component:

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class DataBufferWriter {
    private final ObjectMapper objectMapper;

    public <T> Mono<Void> write(ServerHttpResponse httpResponse, T object) {
        return httpResponse
            .writeWith(Mono.fromSupplier(() -> {
                DataBufferFactory bufferFactory = httpResponse.bufferFactory();
                try {
                    return bufferFactory.wrap(objectMapper.writeValueAsBytes(object));
                } catch (Exception ex) {
                    log.warn("Error writing response", ex);
                    return bufferFactory.wrap(new byte[0]);
                }
            }));
    }
}
like image 179
MuratOzkan Avatar answered Oct 23 '22 02:10

MuratOzkan