I'm newbie to spring webflux and reactor I want to have a fallback mechanism when some specific exception occurs and based on my research onErrorResume method does that but it won't get called and I get 500 internal server error instead of my fallback triggered and preventing this error.
Note: I use spring webflux which means it made some changes in the normal behavior of the reactor project
public Mono<Date> getExpirationDateFromToken(String token) {
return getAllClaimsFromToken(token)
.onErrorResume(ExpiredJwtException.class, e -> Mono.just(e.getClaims()))
.map(Claims::getExpiration);
}
public Mono<Claims> getAllClaimsFromToken(String token) {
return Mono.just(Jwts.parserBuilder()
.setSigningKey(Base64.getEncoder().encodeToString(secret.getBytes()))
.build()
.parseClaimsJws(token)
.getBody());
}
and this is the stack trace
io.jsonwebtoken.ExpiredJwtException: JWT expired at 2020-03-22T08:17:15Z. Current time: 2020-03-22T08:50:56Z, a difference of 2021835 milliseconds. Allowed clock skew: 0 milliseconds.
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:439) ~[jjwt-impl-0.11.0.jar:0.11.0]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Handler ir.siavash.customerservice.security.controller.AuthController#refreshToken(Mono) [DispatcherHandler]
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP POST "/auth/refreshToken" [ExceptionHandlingWebHandler]
Stack trace:
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:439) ~[jjwt-impl-0.11.0.jar:0.11.0]
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:541) ~[jjwt-impl-0.11.0.jar:0.11.0]
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:601) ~[jjwt-impl-0.11.0.jar:0.11.0]
at io.jsonwebtoken.impl.ImmutableJwtParser.parseClaimsJws(ImmutableJwtParser.java:173) ~[jjwt-impl-0.11.0.jar:0.11.0]
at ir.siavash.customerservice.security.JWTUtil.getAllClaimsFromToken(JWTUtil.java:42) ~[classes/:na]
Ok, On second look, it's clear. That's a common mistake. Reactive components could be difficult to learn, but you're not very far from the solution.
Mono.just takes a pre-computed value as parameter. Therefore, your getAllClaimsFromToken will create a Mono only after all code given to just is executed. That means that your error happens before any reactive component is available for error management (assembly time).
What you could do instead is using Mono#fromCallable to ask Reactor to compute Jwt token on demand, or creating Mono with just initial string, then mapping using your function.
Let's see second solution:
public Mono<Date> getExpirationDateFromToken(String token) {
return Mono.just(token)
.map(this::getAllClaimsFromToken)
.onErrorResume(ExpiredJwtException.class, e -> Mono.just(e.getClaims()))
.map(Claims::getExpiration);
}
public Claims getAllClaimsFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(Base64.getEncoder().encodeToString(secret.getBytes()))
.build()
.parseClaimsJws(token)
.getBody();
}