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.