Spring Security | Authentication | BCryptPasswordEncoder: Password Hashing
Why Hash Passwords?
If data is leaked, storing passwords as plain text is extremely dangerous. Passwords should generally be stored in a form that does not reveal the original string. When decryption is unnecessary, use a hash function.
BCrypt
MD5 and SHA are well-known hash functions, but BCrypt is convenient for password hashing.
Applying a hash function only once remains vulnerable to brute-force attacks, dictionary attacks, and rainbow tables.
BCrypt adds a random salt and hashes repeatedly, making the original password harder to guess. Spring Security also recommends BCrypt.
Implementation
Configure a PasswordEncoder on DaoAuthenticationProvider. To use BCrypt, use the BCryptPasswordEncoder implementation. Store precomputed BCrypt hash values.
XML Configuration
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
...
<bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user
name="hoge"
password="$2a$08$CekzJRYhb5bzp5mx/eZmX.grG92fRXo267QVVyRs0IE.V.zeCIw8S"
authorities="ROLE_USER" />
</sec:user-service>
<sec:password-encoder ref="passwordEncoder" />
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
- Define
BCryptPasswordEncoderas a Bean and set it in therefattribute of<password-encoder>. - Configure
<password-encoder>as a child element of<authentication-provider>.
Java Configuration
MySpringSecurityConfig.java
package sample.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
public void configure(AuthenticationManagerBuilder auth, PasswordEncoder passwordEncoder) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("hoge")
.password("$2a$08$CekzJRYhb5bzp5mx/eZmX.grG92fRXo267QVVyRs0IE.V.zeCIw8S")
.roles("USER");
}
}
- Specify the
PasswordEncoderwith thepasswordEncoder()method ofAbstractDaoAuthenticationConfigurer. AbstractDaoAuthenticationConfigureris the parent class ofInMemoryUserDetailsManagerConfigurer, returned byAuthenticationManagerBuilder.inMemoryAuthentication().
Encoding Passwords
To encode a password, obtain PasswordEncoder from the container and use encode().
EncodePasswordServlet.java
package sample.spring.security.servlet;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/encode-password")
public class EncodePasswordServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());
PasswordEncoder passwordEncoder = context.getBean(PasswordEncoder.class);
String password = req.getParameter("password");
String encode = passwordEncoder.encode(password);
System.out.println(encode);
}
}
- The configuration is the same as described above: register
BCryptPasswordEncoderas a Bean. - The example obtains
PasswordEncoderexplicitly for demonstration. In practice, inject it into a container-managed Bean. - Access http://localhost:8080/encode-password?password=fuga.
Server console output
$2a$10$2qVDkAqxp8eDrxR8Be2ZpubYGOCVZ7Qy9uK/XzOIY1ZoxpChtrWDK