Validating JWTs on every request
Accept bearer tokens with the built-in resource server instead of writing a custom filter.
Open this lesson in the learning hubKey points
- Do not write a custom JWT filter.
oauth2ResourceServerinstallsBearerTokenAuthenticationFilterfor you. - It checks signature,
exp,nbfand issuer, then puts aJwtAuthenticationTokenin the context. - With an external provider set
issuer-uri. Keys come from the JWKS endpoint and are cached and rotated automatically. - A
scopeclaim becomesSCOPE_readauthorities. Map a custom claim withJwtAuthenticationConverter. - Rejections return 401 with a
WWW-Authenticateheader naming the failed check. Read it before debugging blind.
Example
@Bean
SecurityFilterChain api(HttpSecurity http, JwtAuthenticationConverter converter) throws Exception {
return http
.securityMatcher("/api/**")
.csrf(csrf -> csrf.disable())
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(a -> a
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(o -> o.jwt(jwt -> jwt.jwtAuthenticationConverter(converter)))
.build();
}
@Bean
JwtDecoder jwtDecoder(RSAPublicKey publicKey) {
return NimbusJwtDecoder.withPublicKey(publicKey).build();
}
@Bean
JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter authorities = new JwtGrantedAuthoritiesConverter();
authorities.setAuthoritiesClaimName("roles");
authorities.setAuthorityPrefix(""); // the claim already contains ROLE_ values
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(authorities);
return converter;
}
The resource server already does JWT validation properly — plug it in, do not reinvent it.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.