I have an API using Spring boot that previously it would use two auth methods, API KEY and JWT, it was working but suddenly it stopped. I didn't change anything but I can't fix it.
import br.rutiquewiski.HealthBridge.infra.security.configuration.api_key.KeySecurityFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Configuration
@EnableWebSecurity
@Order(1)
public static class ApiKeySecurityConfiguration {
//Api key auth, any endpoint with key in the url uses authentication with api key instead of the jwt token
@Autowired
private KeySecurityFilter keySecurityFilter;
@Bean
public SecurityFilterChain apiKeySecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**").permitAll();
auth.requestMatchers( AntPathRequestMatcher.antMatcher("/api/key/**") ).authenticated();
}
)
.addFilterBefore(keySecurityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
@EnableWebSecurity
@Configuration
@Order(2) //This annotation sets this configuration to be the second one in the auth order
public static class StandardSecurityConfiguration {
//Regular token auth
@Autowired
private TokenSecurityFilter tokenSecurityFilter;
@Bean
public SecurityFilterChain tokenSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**").permitAll();
auth.anyRequest().authenticated();
})
.addFilterBefore(tokenSecurityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationManager authenticationManager2(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
}
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>br.rutiquewiski</groupId>
<artifactId>HealthBridge</artifactId>
<version>1.0.0</version>
<name>HealthBridge</name>
<description>API project por health care system</description>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
I have tried every simple solution. Currently, if the api key auth config is not commented, the other one doesn't work, if I comment the api key config then the jwt works. I have tried changing the order already
I fixed by changing the way the two methods are implemented, putting both of them in the same class and using SecuirtyMatcher().
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Autowired
private KeySecurityFilter keySecurityFilter;
@Autowired
private TokenSecurityFilter tokenSecurityFilter;
@Bean
@Order(1)
public SecurityFilterChain apiKeySecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.securityMatcher("/api/key/**")
.authorizeHttpRequests(auth -> {auth.anyRequest().authenticated();})
.addFilterBefore(keySecurityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
@Order(2)
public SecurityFilterChain tokenSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.securityMatcher("/")
.authorizeHttpRequests(auth -> {auth.anyRequest().authenticated();})
.addFilterBefore(tokenSecurityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}