Skip to content
Permalink
Browse files

Upgrade to Spring Boot 2.1.7 and rely on

PasswordEncoder#upgradeEncoding.
  • Loading branch information...
michael-simons committed Aug 15, 2019
1 parent 640d15f commit 0d8ff58a0f1777417e2407366f3884b2051d1335
Showing with 12 additions and 140 deletions.
  1. +3 −39 pom.xml
  2. +9 −101 src/main/java/ac/simons/spring/passwordmigration/PasswordmigrationApplication.java
42 pom.xml
@@ -10,14 +10,14 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.7.RELEASE</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.9</java.version>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
@@ -34,7 +34,7 @@
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
</dependencies>
<build>
@@ -48,40 +48,4 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
@@ -15,23 +15,18 @@
*/
package ac.simons.spring.passwordmigration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.util.stream.Collectors.joining;
import static org.springframework.security.core.userdetails.User.withUsername;

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -41,12 +36,6 @@
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.HashMap;
import java.util.Map;

import static java.util.stream.Collectors.joining;
import static org.springframework.security.core.userdetails.User.withUsername;

/**
* @author Michael J. Simons
*/
@@ -82,9 +71,6 @@ public boolean matches(CharSequence rawPassword, String encodedPassword) {
@Configuration
class SecurityConfiguration {

private static final Logger LOG = LoggerFactory
.getLogger(SecurityConfiguration.class);


/**
* This configures the new delegating password encoder system. More hints inside the
@@ -117,7 +103,7 @@ public PasswordEncoder passwordEncoder() {
final DelegatingPasswordEncoder rv =
new DelegatingPasswordEncoder(idForEncode, encoders);
// !!!!
// That is the crucial part to support "legacy" passworts without a hash
// That is the crucial part to support "legacy" passwords without a hash
// Usually (and hopefully) that would be something like a sha1, maybe md5, but
// not the one I configured here ;)
rv.setDefaultPasswordEncoderForMatches(new BSPasswordEncoder());
@@ -129,15 +115,15 @@ public PasswordEncoder passwordEncoder() {
* purpose of this demo, I'm using an in-memory based solution.
* <br>
* Notice that I'm not using {@link InMemoryUserDetailsManagerConfigurer} from within
* {@link #webSecurityConfigurer(AuthenticationEventPublisher, UserDetailsService)}.
* a {@link AuthenticationManagerBuilder#inMemoryAuthentication}.
* That configurer would have gave me the ability to set the password encoder.
* Which would defeat the purpose of this demo, so I use the super secret, incredible
* hard to guess password hash <pre>fvzbaf</pre>.
*
* @return Your custom user details service
*/
@Bean
public UserDetailsService userDetailsService() {
public InMemoryUserDetailsManager userDetailsService() {
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager
.createUser(withUsername("michael")
@@ -147,84 +133,6 @@ public UserDetailsService userDetailsService() {
);
return manager;
}

/**
* This bean is needed to publish security events like successfull and failed logins.
* Spring Boot 2M7 still creates that for you, but that might change in the future.
*
* @param publisher
* @return
*/
@Bean
public AuthenticationEventPublisher authenticationEventPublisher(
final ApplicationEventPublisher publisher
) {
return new DefaultAuthenticationEventPublisher(publisher);
}

/**
* This {@link WebSecurityConfigurerAdapter} takes care of all things security.
* Take note that as of Spring Boot 2 having one of those in the context, all Spring Boots
* magic goes away. All defaults are courtesy of Spring Security itself.
*
* @param authenticationEventPublisher
* @param userDetailsService
* @return
*/
@Bean
public WebSecurityConfigurerAdapter webSecurityConfigurer(
final AuthenticationEventPublisher authenticationEventPublisher,
final UserDetailsService userDetailsService
) {
return new WebSecurityConfigurerAdapter() {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
// That is actually insecure!! But if you want to migrate the password,
// we have to keep the credentials inside the authentication object.
// Luckily for use, the credentials are the clear text password
// in case of username and password authenticatio
.eraseCredentials(false)
// Register the authenticationEventPublisher with security, otherwise no events will be fired
.authenticationEventPublisher(authenticationEventPublisher)
// Register the rest of our infrastructure.
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
// @formatter:on
}
};
}


/**
* This method represents the "guts" of this demo: It listens on
* {@link AuthenticationSuccessEvent authentication success events} and checks if the authentication inside them
* is of a {@link UsernamePasswordAuthenticationToken} with credentials being availabe.
*
* @param passwordEncoder That is the new, delegating password encoder.
* @return
*/
@Bean
public ApplicationListener<AuthenticationSuccessEvent> onSuccessListener(
final PasswordEncoder passwordEncoder)
{
return (AuthenticationSuccessEvent event) -> {
final Authentication authentication = event.getAuthentication();
if (authentication instanceof UsernamePasswordAuthenticationToken && authentication.getCredentials() != null) {
// That is the point where we are able to retrieve the users login name
// and also the clear text password if we didn't let Spring Security erase the credentials
// We now can use the delegating password encoder to rehash the password to a safe hash
final CharSequence plainTextPassword = (CharSequence) authentication.getCredentials();
final String rehashedPassword = passwordEncoder.encode(plainTextPassword);

LOG.info("Now is the time to store new password hash {} for user {}", rehashedPassword, authentication.getName());

// Now clear the credentials
((UsernamePasswordAuthenticationToken)authentication).eraseCredentials();
}
};
}
}

/**

0 comments on commit 0d8ff58

Please sign in to comment.
You can’t perform that action at this time.