Skip to content

Spring Security 6 JWT Configuration ​

Problem Statement ​

When upgrading to Spring Security 6, developers face two significant deprecations:

  1. JWT Configuration: The direct OAuth2ResourceServerConfigurer::jwt method reference is deprecated and no longer functional
  2. Endpoint Matchers: Traditional security matchers like antMatchers(), mvcMatchers(), and regexMatchers() have been fully removed

This migration requires adopting Spring Security's new lambda-based DSL for configuring both resource server JWT authentication and endpoint security rules. Attempting to use the deprecated approaches will result in compilation errors and runtime failures.

Solution Overview ​

Migration Approach ​

To resolve these issues:

  • Replace method references with explicit lambda DSL configuration
  • Use requestMatchers() for endpoint security
  • Configure JWT through the new Customizer interface

Key principles driving these changes:

  1. Type-Safe Configuration: Prevent common misconfiguration errors
  2. Consistent DSL: Unified approach for all security configurations
  3. Modern Java Practices: Leverage functional interfaces and lambdas

Endpoint Security Configuration ​

Replacing antMatchers ​

Traditional matchers are replaced by the versatile requestMatchers() method:

java
http.authorizeHttpRequests(auth -> auth
    .requestMatchers("/public/**").permitAll()   // New replacement
    .requestMatchers("/admin/**").hasRole("ADMIN")
    .anyRequest().authenticated()
);

JWT Resource Server Configuration ​

The optimal approach configures both custom JWT decoding and resource server security:

java
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .oauth2ResourceServer(oauth2 -> oauth2
            .jwt(jwt -> jwt.decoder(jwtDecoder())  // Custom decoder
        ));
        
    // Additional configurations
    .csrf(csrf -> csrf.disable())
    .headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin()));
    
    return http.build();
}

@Bean
JwtDecoder jwtDecoder() {
    return NimbusJwtDecoder.withJwkSetUri("https://auth.example.com/.well-known/jwks.json").build();
}

Alternative Configuration Styles ​

Both of these are functionally equivalent:

java
http.oauth2ResourceServer(oauth2 -> oauth2
    .jwt(Customizer.withDefaults())
);

// With bean resolver
@Bean
JwtDecoder jwtDecoder() { /* ... */ }
java
@Value("${security.jwks-uri}")
private String jwksUri;

http.oauth2ResourceServer(oauth2 -> oauth2
    .jwt(jwt -> jwt.jwkSetUri(jwksUri))
);

Kotlin DSL Configuration ​

For Kotlin users, the DSL provides concise configuration:

kotlin
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
    http {
        authorizeHttpRequests {
            authorize("/admin/**", hasRole("ADMIN"))
            authorize(anyRequest, authenticated)
        }
        oauth2ResourceServer {
            jwt { }
        }
        headers {
            frameOptions { sameOrigin }
        }
        csrf { disable() }
        sessionManagement {
            sessionCreationPolicy = SessionCreationPolicy.STATELESS
        }
    }
    return http.build()
}

@Bean
fun jwtDecoder(): JwtDecoder = 
    NimbusJwtDecoder.withJwkSetUri("https://auth.example.com/jwks.json").build()

Complete Java Example ​

Combining all configurations in a production-ready setup:

java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    private static final String JWKS_URI = "https://your-auth-domain/.well-known/jwks.json";

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.decoder(jwtDecoder()))
            )
            .csrf(csrf -> csrf.disable())
            .headers(headers -> headers
                .frameOptions(frameOptions -> frameOptions.sameOrigin())
            );
        
        return http.build();
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withJwkSetUri(JWKS_URI).build();
    }
}

Key Migration Notes ​

Best Practice

Test JWKS Connectivity: Always validate your JWKS URI is accessible during startup to prevent runtime authentication failures. Consider adding a health check for this endpoint.

API Changes

  1. and() is deprecated - Configure everything in a single fluent chain
  2. Method chaining rules changed - Each component must be configured within its own lambda
  3. Customizer.withDefaults() activates Spring's default configuration for that component

Removed Features

The following are completely removed and have no direct replacements:

  • antMatchers()
  • mvcMatchers()
  • regexMatchers()
  • OAuth2ResourceServerConfigurer::jwt method reference

This lambda DSL configuration approach provides more flexibility while reducing common configuration errors. As you migrate older Spring Security setups, consistently apply the configure -> lambda -> component pattern throughout your security configuration.