Describe the bug Migrating spring-boot project to 3.0.6, we use spring security to authenticate urls by user and password
spring: security: user: name: user #${SPRING_SECURITY_USER_NAME} password: passcode ${SPRING_SECURITY_USER_PASSWORD}
This works with old spring boot version and new spring boot version always reporting following error,
Caused by: java.lang.IllegalArgumentException: username cannot be null
at org.springframework.util.Assert.notNull(Assert.java:204) ~[spring-core-6.0.8.jar:6.0.8]
at org.springframework.security.core.userdetails.User$UserBuilder.username(User.java:357) ~[spring-security-core-6.0.3.jar:6.0.3]
at org.springframework.security.core.userdetails.User.withUsername(User.java:216) ~[spring-security-core-6.0.3.jar:6.0.3]
My security class
package org.selflearn.spring.security;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.Customizer; 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.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.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.RegexRequestMatcher;
@Configuration @EnableWebSecurity public class SecurityConfiguration {
String name;
String password;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeHttpRequests( authorizationManagerRequestMatcherRegistry -> {
try {
authorizationManagerRequestMatcherRegistry
.requestMatchers(new RegexRequestMatcher("/", HttpMethod.GET.name(), false),
new RegexRequestMatcher("/actuator/health", HttpMethod.GET.name(), false),
new RegexRequestMatcher("/actuator/health/readiness", HttpMethod.GET.name(), false),
new RegexRequestMatcher("/actuator/health/liveness", HttpMethod.GET.name(), false),
new RegexRequestMatcher("/health*", HttpMethod.GET.name(), false),
new RegexRequestMatcher("/api-docs.*", HttpMethod.GET.name(), false),
new RegexRequestMatcher("/swagger-resources/.*", HttpMethod.GET.name(), false)
).permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf(AbstractHttpConfigurer::disable);
} catch (Exception e) {
throw new RuntimeException(e);
}
}).httpBasic(Customizer.withDefaults());
return httpSecurity.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withUsername(name)
.password(passwordEncoder().encode(password))
.roles("user", "ACTUATOR", "ADMIN")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Comment From: nabeeltariqbhatti
Hi @voodemsanthosh, Just a simple question from my side. How are we initializing variables username and password?
Comment From: voodemsanthosh
@nabeeltariqbhatti
mvn clean compile spring-boot:run -Dspring.security.user.name=user -Dspring.security.user.password=passcode
Comment From: voodemsanthosh
Could anyone help me to solve this issue?
Comment From: marcusdacoregio
Hi @voodemsanthosh, thanks for the report.
Those properties work only for the auto-configuration from Spring Boot. If you provide an UserDetailsService bean the UserDetailsServiceAutoConfiguration will back-off and not initialize a default user for you.
Can you provide a reproducible sample where it worked on an older version and did not work on 3.0.6?
Comment From: voodemsanthosh
Thnank you @marcusdacoregio. Here is my old version sample code.
package org.selflearn.spring.security;
import io.netty.handler.codec.http.HttpMethod; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import java.util.HashMap; import java.util.Map;
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
String username;
String password;
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
Map encoders = new HashMap<>();
encoders.put("scrypt", new SCryptPasswordEncoder());
PasswordEncoder passwordEncoder =
new DelegatingPasswordEncoder("scrypt", encoders);
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser(username).password(password).roles("ACTUATOR");
}
/*@Override
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher allOtherEndpoints = new RegexRequestMatcher(".*", null);
http
.requestMatcher(allOtherEndpoints)
.authorizeRequests()
.anyRequest()
.hasRole("ACTUATOR")
.and()
.httpBasic()
.and().csrf(AbstractHttpConfigurer::disable);
}*/
@Override
public void configure(WebSecurity webSecurity) {
webSecurity
.ignoring()
// All of Spring Security will ignore the requests
.antMatchers(String.valueOf(HttpMethod.POST), "/encrypt")
.regexMatchers("/manage/hystrix.stream")
.regexMatchers("/actuator/health")
.regexMatchers("/actuator/health/readiness")
.regexMatchers("/actuator/health/liveness")
.regexMatchers("/health")
.regexMatchers("/config-server")
.regexMatchers("/")
.regexMatchers("/api-docs.*")
.regexMatchers("/swagger-resources/.*");
}
}
Comment From: marcusdacoregio
It seems that your username and password fields should have a @Value annotation telling Spring that the value come from a property source. Have you tried that?
If you are using the Spring Boot properties and you only want to have one user, you do not need to create a UserDetailsService bean by yourself, you can just set the properties and let Spring Boot create the user and the UserDetailsService for you.
It feels like this is a question that would be better suited to Stack Overflow. We prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add more detail if you feel this is a genuine bug.