I have a java application build on spring boot 3 and I have a simple security configuration like this:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
private static final String[] permitMethods = new String[]{
"/api/games/**",
"/swagger-ui/**",
"/v3/api-docs/**",
"/actuator/**"
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeRequests(authorizeRequests ->
authorizeRequests
.requestMatchers(permitMethods).permitAll()
.anyRequest().authenticated()
)
.userDetailsService(userDetailsService())
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin")
.build();
return new CachingUserDetailsService(new InMemoryUserDetailsManager(user));
}
}
The problem is that current application uses too much CPU over 70% while executing stress test.
When I am removing
.httpBasic(Customizer.withDefaults());
CPU usage goes down to 10%. I am executing stress test just with 40 parallel threads, the more parallel threads I set the more CPU usage becomes.
I am currently using spring boot 3.1.1 version
but I have tried it on spring boot 3.0.0
version as well, but the result was same.
Do you have any ideas how can I minimize CPU usage with the basic authentication ?
the issue is simple, the cause of high cpu is in your password encoder specifically at line which is UserDetails user = User.withDefaultPasswordEncoder()
now at the stage if u go to the implantation of it, you can see it uses ByCrypt algorithm for encoding passwords, and ByCrypt is expensive for CPU, since it does hashing in many iterations, you can check how bycrypt works to understand more about how it works.
you can verify that the issue is bycrpt and not httbasic by doing following:
define bean password encoder:
@Bean
public PasswordEncoder bCryptPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
and here try use plain text passwords!
also for registring users use
UserDetails user = User.builder()
.username("admin")
.password("admin")
.build();
now that you know the issue what can you do?
you can alter some bycrypt functionalities which is available when you define BCryptPasswordEncoder
as bean(check constructor). like below:
@Bean
public PasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder(strenght_number_that_suits_for_you);
}
or
you can use faster hashing algorithm for spring security you can check implemented classes of PasswordEncoder
interface
also you can check some other related questions that might help you more like: this or this