Java Spring Security - User.withDefaultPasswordEncoder() is deprecated?
Asked Answered
T

5

32

I am very new to java spring security, and was following the Spring.io tutorial guide. As part of this, I edited the WebSecurityConfig class as required:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
        .authorizeRequests()
            .antMatchers("/", "/home").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
        .logout()
            .permitAll();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        UserDetails user =
         User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();

    return new InMemoryUserDetailsManager(user);
    }
}

Within the userDetailService() method, it uses withDefaultPasswordEncoder() which is now deprecated as seen in the docs: withDefaultPasswordEncoder()

Unfortunately, I have not been able to find an alternative to this, to complete this tutorial without using the deprecated method. Would somebody be able to provide an alternative for this if possible?

Thanks!

note: I have attached a couple of screen shots of my error, as well as my gradle file

image 1: The error I am receiving

image 2: My gradle file

Teeter answered 15/4, 2018 at 23:32 Comment(6)
Did you read the whole Javadoc? It goes into substantial detail, especially including "is acceptable for demos and getting started".Birdbath
I did, but when implementing this (without just pulling the repo), when i wrote the method, I kept getting the error message saying that withDefaultPasswordEncoder() could not be resolved, and wouldn't let me run the programTeeter
There's a difference between deprecated (you'll get a warning) and unresolved, which is an error. This documentation is still in the 5.0 docs, so it shouldn't be gone; what version of the Spring Security dependency are you using?Birdbath
Using gradle which I assume is 5.0.4.RELEASE..... -> compile("org.springframework.boot:spring-boot-starter-security")Teeter
Spring Boot 1.5 or 2.0?Birdbath
spring boot 1.5Teeter
C
46

EDIT: deleted old answer, misunderstood the question. Here's the new one:

User.withDefaultPasswordEncoder() can still be used for demos, you don't have to worry if that's what you're doing - even if it's deprecated - but in production, you shouldn't have a plain text password in your source code.

What you should be doing instead of using your current userDetailsService() method is the following:

private static final String ENCODED_PASSWORD = "$2a$10$AIUufK8g6EFhBcumRRV2L.AQNz3Bjp7oDQVFiO5JJMBFZQ6x2/R/2";


@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .passwordEncoder(passwordEncoder())
        .withUser("user").password(ENCODED_PASSWORD).roles("USER");
}


@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

Where ENCODED_PASSWORD is secret123 encoded with BCrypt. You can also encode it programmatically like so: passwordEncoder().encode("secret123").

That way, even if you push your code to a public repository, people won't know the password because ENCODED_PASSWORD only shows the encoded (and hashed) version of the password and not the plain text version, but because you know that $2a$10$AIUufK8g6EFhBcumRRV2L.AQNz3Bjp7oDQVFiO5JJMBFZQ6x2/R/2 is actually the encoded password of the string secret123 whereas others don't, your in-memory user with the credentials user:secret123 won't be compromised.

Note that I'm using leaving it in a static variable for the sake of the example.

Capparidaceous answered 15/4, 2018 at 23:42 Comment(5)
Hi, please a quick question about the passwordEncoder().encode("secret123") suggestion. You said That way, even if you push your code to a public repository, people won't know the password because ENCODED_PASSWORD only shows the encoded version of the password and not the plain text version, I'm confused, if I use passwordEncoder().encode("secret123") and upload it to a repository, wouldn't people see my password? whereas if I stick to your original code using ENCODED_PASSWORD variable, that way people won't see my raw password. I'm confused about what you meant by That way,... thanxFor
@For It's never a good idea to push your password on a public repository no matter if it's encoded/hashed or not, but the point is, if you, instead of writing passwordEncoder().encode("secret123") in your code, you pushed $2a$10$AIUufK8g6EFhBcumRRV2L.AQNz3Bjp7oDQVFiO5JJMBFZQ6x2/R/2, people wouldn't automatically know that your password is secret123 because you just pushed the hash -- not the actual password. The point I was trying to make was that you could run System.out.println(passwordEncoder().encode("secret123")) locally, copy the result and use that result as passwordCapparidaceous
... in your code. But once again, keep in mind that all I'm saying is that pushing $2a$10$AIUufK8g6EFhBcumRRV2L.AQNz3Bjp7oDQVFiO5JJMBFZQ6x2/R/2 is much more secure than pushing passwordEncoder().encode("secret123") if you want to keep your password hidden, but in the future, when there are better and faster computers, it's very likely for your password to be compromised. It's better to avoid pushing any password/confidential information on a public repository. Instead, you should use environment variables.Capparidaceous
@Twin How can I handle successful authentication? For example, I need to generate a GWT token and return and add it to HTTP Request as a new Header. Is it possible?Foretopsail
@Capparidaceous I followed your approach. However, I keep getting "Bad credentials" message on supplying username as "user" and password as "secret123". Can you please help on why this error is seen?Exegetic
S
11

In spring security 5.7.3 WebSecurityConfigurerAdapter will be deprecated and so we have to provide bean of UserDetailsService interface.

Sample code

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurity {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.formLogin(form -> {
            form.loginPage("/login")
                    .permitAll();
        }).authorizeRequests();
        return http.build();
    }


    @Bean
    @Description("In memory Userdetails service registered since DB doesn't have user table ")
    public UserDetailsService users() {
        // The builder will ensure the passwords are encoded before saving in memory
        UserDetails user = User.builder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();
        UserDetails admin = User.builder()
                .username("admin")
                .password("password")
                .roles("USER", "ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user, admin);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
Schumann answered 20/8, 2022 at 20:8 Comment(1)
.password(passwordEncoder().encode("password"))Yost
Z
3

Using the passwordEncoder.encode() would be like this

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
    .passwordEncoder(passwordEncoder())
    .withUser("user")
    .password(passwordEncoder().encode("miClave"))
    .roles("USER");
   }

   @Bean
   public PasswordEncoder passwordEncoder() {
       return new BCryptPasswordEncoder();
   } 

}
Zahavi answered 6/9, 2019 at 0:2 Comment(1)
This is exactly what they want to avoid by making the function deprecated. You must not include the password in the code. Instead add the encrypted value.Olimpiaolin
B
2

The withDefaultPasswordEncoder() method is now deprecated in favor of passwordEncoder() which accepts a PasswordEncoder implementation. It's recommended to use a stronger password encoding mechanism such as bcrypt or scrypt. To use bcrypt, you can do the following:

package com.example.securingweb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((requests) -> requests
            .requestMatchers("/", "/home").permitAll()
            .anyRequest().authenticated()
        )
        .formLogin((form) -> form
            .loginPage("/login")
            .permitAll()
        )
        .logout((logout) -> logout.permitAll());

    return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
    UserDetails user =
         User.builder()
            .username("user")
            .password(passwordEncoder().encode("password"))
            .roles("USER")
            .build();

    return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }
} 
     

The below code is the new implementation of WebSecurityConfig.java file https://spring.io/guides/gs/securing-web/

Bakemeier answered 4/2, 2023 at 14:1 Comment(0)
R
1

With Spring Security 6x, WebSecurityConfigurerAdapter has been removed completely, you will be have to use a Bean for the InMemory User Details and also use another bean to encode your password using BcrypPasswordEncoder. Your code should look like this;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User.builder().username("user")
            .password(passwordEncoder().encode("password"))
            .roles("USER")
            .build();
    return new InMemoryUserDetailsManager(user);
}

@Bean
private PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
}
Reimport answered 30/3, 2023 at 20:31 Comment(1)
Thank you. But private or protected keyword in @Bean method will make your application crash. Remove accessors.Ransome

© 2022 - 2024 — McMap. All rights reserved.