Our custom WebFilter, named JWTAuthWebFilter, is entrusted with converting the JWT token received into appropriate classes that the Spring Security understands. It makes use of a converter named JWTAuthConverter, which does a number of things, as follows:
- Gets the authorization payload
- Extracts the token by discarding the Bearer string
- Verifies the token
- Creates a UsernamePasswordAuthenticationToken class understood by Spring Security
The code snippet below shows the JWTAuthWebFilter class and its important method which does operations listed above.
public class JWTAuthConverter implements Function<ServerWebExchange,
Mono<Authentication>> {
@Override
public Mono<Authentication> apply(ServerWebExchange serverWebExchange) {
return Mono.justOrEmpty(serverWebExchange)
.map(JWTUtil::getAuthorizationPayload)
.filter(Objects::nonNull)
.filter(JWTUtil.matchBearerLength())
.map(JWTUtil.getBearerValue())
.filter(token -> !token.isEmpty())
.map(JWTUtil::verifySignedJWT)
.map(JWTUtil::getUsernamePasswordAuthenticationToken)
.filter(Objects::nonNull);
}
}
After this conversion, it does the actual authentication using Spring Security, which sets SecurityContext in the application, as shown in the following code snippet:
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return this.getAuthMatcher().matches(exchange)
.filter(matchResult -> matchResult.isMatch())
.flatMap(matchResult -> this.jwtAuthConverter.apply(exchange))
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
.flatMap(token -> authenticate(exchange, chain, token));
}
//..more methods
private Mono<Void> authenticate(ServerWebExchange exchange,
WebFilterChain chain, Authentication token) {
WebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain);
return this.reactiveAuthManager.authenticate(token)
.flatMap(authentication -> onAuthSuccess(authentication,
webFilterExchange));
}
private Mono<Void> onAuthSuccess(Authentication authentication, WebFilterExchange
webFilterExchange) {
ServerWebExchange exchange = webFilterExchange.getExchange();
SecurityContextImpl securityContext = new SecurityContextImpl();
securityContext.setAuthentication(authentication);
return this.securityContextRepository.save(exchange, securityContext)
.then(this.authSuccessHandler
.onAuthenticationSuccess(webFilterExchange, authentication))
.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(
Mono.just(securityContext)));
}
The JWTAuthWebFilter class filter method does the necessary conversions and then the authenticate method does the actual authentication, and finally calls the onAuthSuccess method.