If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:
>> LEARN SPRING SECURITYMocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
1. Overview
In Spring Security 4, it was possible to store passwords in plain text using in-memory authentication.
A major overhaul of the password management process in version 5 has introduced a more secure default mechanism for encoding and decoding passwords. This means that if your Spring application stores passwords in plain text, upgrading to Spring Security 5 may cause problems.
In this short tutorial, weβll describe one of those potential problems and demonstrate a solution.
2. Spring Security 4
Weβll start by showing a standard security configuration that provides simple in-memory authentication (valid for Spring 4):
@Configuration
public class InMemoryAuthWebSecurityConfigurer
extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("spring")
.password("secret")
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/private/**")
.authenticated()
.antMatchers("/public/**")
.permitAll()
.and()
.httpBasic();
}
}
This configuration defines authentication for all /private/ mapped methods and public access for everything under /public/.
If we use the same configuration under Spring Security 5, weβll get the following error:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
The error tells us that the given password couldnβt be decoded since no password encoder was configured for our in-memory authentication.
3. Spring Security 5
We can fix this error by defining a DelegatingPasswordEncoder with the PasswordEncoderFactories class.
We use this encoder to configure our user :
@Configuration
public class InMemoryAuthWebSecurityConfigurer {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
UserDetails user = User.withUsername("spring")
.password(encoder.encode("secret"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
Now, with this configuration, weβre storing our in-memory password using BCrypt in the following format:
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
Although we can define our own set of password encoders, itβs recommended to stick with the default encoders provided in PasswordEncoderFactories.
Since Spring Security version 5.7.0-M2, Spring deprecates the use of WebSecurityConfigureAdapter and suggests creating configurations without it. This article explains it in more detail.
3.2. NoOpPasswordEncoder
If, for any reason, we donβt want to encode the configured password, we can make use of the NoOpPasswordEncoder.
To do so, we simply prefix the passphrase we provide to the password() method with the {noop} identifier:
@Configuration
public class InMemoryNoOpAuthWebSecurityConfigurer {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withUsername("spring")
.password("{noop}secret")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
This way, Spring Security will use the NoOpPasswordEncoder under the hood when it compares the password provided by the user with the one we configured above.
Note, however, that we should never use this approach on the production application! As the official documentation says, the NoOpPasswordEncoder has been deprecated to indicate that itβs a legacy implementation, and using it is considered insecure.
3.3. Migrating Existing Passwords
We can update existing passwords to the recommended Spring Security 5 standards by:
- Updating plain text stored passwords with their value encoded:
String encoded = new BCryptPasswordEncoder().encode(plainTextPassword);
- Prefixing hashed stored passwords with their known encoder identifier:
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
- Requesting users to update their passwords when the encoding-mechanism for stored passwords is unknown
4. Conclusion
In this quick example, we updated a valid Spring 4 in-memory authentication configuration to Spring 5 using the new password storage mechanism.
