[java] How to use new PasswordEncoder from Spring Security

As of Spring Security 3.1.4.RELEASE, the old org.springframework.security.authentication.encoding.PasswordEncoder has been deprecated in favour of org.springframework.security.crypto.password.PasswordEncoder. As my application has not been released to the public yet, I decided to move to the new, not deprecated API.

Until now, I had a ReflectionSaltSource that automatically used the user's registration date as per-user salt for password.

String encodedPassword = passwordEncoder.encodePassword(rawPassword, saltSource.getSalt(user));

During login process, Spring also used my beans to appropriate verify if the user can or can not sign in. I can't achieve this in the new password encoder, because the default implementation of SHA-1 - StandardPasswordEncoder has only ability to add a global secret salt during the encoder creation.

Is there any reasonable method of how to set it up with the non-deprecated API?

This question is related to java spring spring-security

The answer is


Having just gone round the internet to read up on this and the options in Spring I'd second Luke's answer, use BCrypt (it's mentioned in the source code at Spring).

The best resource I found to explain why to hash/salt and why use BCrypt is a good choice is here: Salted Password Hashing - Doing it Right.


I had a similar issue. I needed to keep the legacy encrypted passwords (Base64/SHA-1/Random salt Encoded) as users will not want to change their passwords or re-register. However I wanted to use the BCrypt encoder moving forward too.

My solution was to write a bespoke decoder that checks to see which encryption method was used first before matching (BCrypted ones start with $).

To get around the salt issue, I pass into the decoder a concatenated String of salt + encrypted password via my modified user object.

Decoder

@Component
public class LegacyEncoder implements PasswordEncoder {

    private static final String BCRYP_TYPE = "$";
    private static final PasswordEncoder BCRYPT = new BCryptPasswordEncoder();

    @Override
    public String encode(CharSequence rawPassword) {

    return BCRYPT.encode(rawPassword);
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {

    if (encodedPassword.startsWith(BCRYP_TYPE)) {
        return BCRYPT.matches(rawPassword, encodedPassword);
    }

    return sha1SaltMatch(rawPassword, encodedPassword);
    }

    @SneakyThrows
    private boolean sha1SaltMatch(CharSequence rawPassword, String encodedPassword) {

    String[] saltHash = encodedPassword.split(User.SPLIT_CHAR);

    // Legacy code from old system   
    byte[] b64salt = Base64.getDecoder().decode(saltHash[0].getBytes());
    byte[] validHash = Base64.getDecoder().decode(saltHash[1]);
    byte[] checkHash = Utility.getHash(5, rawPassword.toString(), b64salt);

    return Arrays.equals(checkHash, validHash);
    }

}

User Object

public class User implements UserDetails {

    public static final String SPLIT_CHAR = ":";

    @Id
    @Column(name = "user_id", nullable = false)
    private Integer userId;

    @Column(nullable = false, length = 60)
    private String password;

    @Column(nullable = true, length = 32)
    private String salt;

.
.

    @PostLoad
    private void init() {

    username = emailAddress; //To comply with UserDetails
    password = salt == null ? password : salt + SPLIT_CHAR + password;
    }        

You can also add a hook to re-encode the password in the new BCrypt format and replace it. Thus phasing out the old method.


Here is the implementation of BCrypt which is working for me.

in spring-security.xml

<authentication-manager >
    <authentication-provider ref="authProvider"></authentication-provider>  
    </authentication-manager>
<beans:bean id="authProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
  <beans:property name="userDetailsService" ref="userDetailsServiceImpl" />
  <beans:property name="passwordEncoder" ref="encoder" />
</beans:bean>
<!-- For hashing and salting user passwords -->
    <beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

In java class

PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(yourpassword);

For more detailed example of spring security Click Here

Hope this will help.

Thanks


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to spring

Are all Spring Framework Java Configuration injection examples buggy? Two Page Login with Spring Security 3.2.x Access blocked by CORS policy: Response to preflight request doesn't pass access control check Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified Spring Data JPA findOne() change to Optional how to use this? After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName The type WebMvcConfigurerAdapter is deprecated No converter found capable of converting from type to type

Examples related to spring-security

Two Page Login with Spring Security 3.2.x Spring 5.0.3 RequestRejectedException: The request was rejected because the URL was not normalized Unsupported Media Type in postman How Spring Security Filter Chain works Spring security CORS Filter How to configure CORS in a Spring Boot + Spring Security application? Failed to load ApplicationContext (with annotation) disabling spring security in spring boot app When to use Spring Security`s antMatcher()? How to manage exceptions thrown in filters in Spring?