I am working on upgrading my SpringBoot 2.7.5 application to SpringBoot 3.0.0.
I have the current configuration in my application.yml which works.
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://myissuer.okta.com/oauth2/someotherstuffhere
In my SecurityConfig I do the following
package com.fedex.amp.stormgateway.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
import java.util.Collections;
@Configuration
public class SecurityConfig{
private static Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
private static final String ROLE_ACTUATOR = "ACTUATOR";
@Value("${com.amp.storm.gateway.actuator.username}")
private String actuatorUsername;
@Value("${com.amp.storm.gateway.actuator.password}")
private String actuatorPassword;
@Bean
@Order(1)
@Profile("!test")
public SecurityFilterChain filterChainBasicAuth(HttpSecurity http) throws Exception {
// @formatter:off
http.antMatcher("/actuator/**")
.httpBasic()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// if no credentials supplied in request,
// then immediately fail the request rather than prompting for a login
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.authorizeRequests()
.anyRequest().hasRole(ROLE_ACTUATOR);
// @formatter:on
logger.info("Loading Actuator Security Config");
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers(EndpointRequest.to("info", "health"));
}
@Bean
public WebSecurityCustomizer webSecurityCustomizerSwagger() {
return (web) -> web.ignoring().antMatchers("/swagger-ui/**", "/v3/api-docs/**");
}
@Bean
public UserDetailsService sampleUserDetails() {
//@formatter:off
UserDetails user = User
.withUsername(actuatorUsername)
.password("{bcrypt}" + new BCryptPasswordEncoder().encode(actuatorPassword))
.roles(ROLE_ACTUATOR).build();
return new InMemoryUserDetailsManager(Collections.singletonList(user));
//@formatter:on
}
@Bean
@Order(2)
@Profile("!test")
public SecurityFilterChain filterChainOkta(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and()
.headers().frameOptions().sameOrigin()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.oauth2ResourceServer().jwt();
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
The above worked exactly as I expected. Since moving to SpringBoot 3 I have no changed my application.yml at all but I did update my Security config like so below.
@Configuration
public class SecurityConfig{
private static Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
private static final String ROLE_ACTUATOR = "ACTUATOR";
@Value("${com.amp.storm.gateway.actuator.username}")
private String actuatorUsername;
@Value("${com.amp.storm.gateway.actuator.password}")
private String actuatorPassword;
@Bean
@Order(1)
@Profile("!test")
public SecurityFilterChain filterChainBasicAuth(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeHttpRequests(auth -> auth.requestMatchers("/actuators/**"))
.httpBasic()
.and()
.csrf(csrf -> csrf.disable())
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.authorizeHttpRequests()
.anyRequest()
.hasRole((ROLE_ACTUATOR));
// @formatter:on
logger.info("Loading Actuator Security Config");
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers(EndpointRequest.to("info", "health"));
}
@Bean
public WebSecurityCustomizer webSecurityCustomizerSwagger() {
return (web) -> web.ignoring().requestMatchers("/swagger-ui/**", "/v3/api-docs/**");
}
@Bean
public UserDetailsService sampleUserDetails() {
//@formatter:off
UserDetails user = User
.withUsername(actuatorUsername)
.password("{bcrypt}" + new BCryptPasswordEncoder().encode(actuatorPassword))
.roles(ROLE_ACTUATOR).build();
return new InMemoryUserDetailsManager(Collections.singletonList(user));
//@formatter:on
}
@Bean
@Order(2)
@Profile("!test")
public SecurityFilterChain filterChainOkta(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.requestMatchers("/**"))
.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
.and()
.headers().frameOptions().sameOrigin()
.and()
.authorizeHttpRequests().anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf(csrf -> csrf.disable())
.oauth2ResourceServer().jwt();
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
However now I get the following error at run time.
Method filterChainOkta in com.fedex.amp.stormgateway.security.SecurityConfig required a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' that could not be found.
Does anyone know if there have been updates to variable pathing or some other change that would impact okta configuration and spring boot 3.0.0?
I looked at the Okta Spring Boot GithUb but I didn’t see any pull requests directly relating to this and examples seem to rely on the deprecated WebSecurityConfigurationAdapter.