Spring Boot: How to specify the PasswordEncoder?
SpringMavenSpring BootSpring SecuritySpring Problem Overview
Currently I got the main class:
package com.recweb.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
/*@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})*/
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
a members class (id, firstname..), a MemberController class:
package com.recweb.springboot;
import java.util.Arrays;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MemberController {
@GetMapping("/members")
public List<Member> getAllUsers() {
return Arrays.asList(new Member(1, "amit"));
}
}
and a WebSecurityConfig class:
package com.recweb.springboot;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("user").roles("USER").build());
return manager;
}
}
When i run "http://localhost:8080/members" i get a login page, i enter "user" as user & "user" as password and then i get the hardcoded Member. It worked fine, but then i right clicked on my project-Run as-Maven install (because i added a dependency, i don't know if that was necessary, first time with Maven too). Since then, when i enter "user" & "user" on the login page i get this error:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:233) ~[spring-security-core-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:196) ~[spring-security-core-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:86) ~[spring-security-core-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:166) ~[spring-security-core-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) ~[spring-security-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.1.RELEASE.jar:5.0.1.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) ~[tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.23.jar:8.5.23]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_131]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.23.jar:8.5.23]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
and it stays on the login page. I tried to remove the dependency & Maven install again, but no luck. This is my POM:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.recweb</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</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.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<!-- <scope>provided</scope> -->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</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>
What went wrong? Thanks
Spring Solutions
Solution 1 - Spring
In spring-security-core:5.0.0.RC1
, the default PasswordEncoder
is built as a DelegatingPasswordEncoder
. When you store the users in memory, you are providing the passwords in plain text and when trying to retrieve the encoder from the DelegatingPasswordEncoder
to validate the password it can't find one that matches the way in which these passwords were stored.
Use this way to create users instead.
User.withDefaultPasswordEncoder().username("user").password("user").roles("USER").build();
You can also simply prefix {noop}
to your passwords in order for the DelegatingPasswordEncoder
use the NoOpPasswordEncoder
to validate these passwords. Notice that NoOpPasswordEncoder
is deprecated though, as it is not a good practice to store passwords in plain text.
User.withUsername("user").password("{noop}user").roles("USER").build();
For more information, check this post.
https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-encoding
Solution 2 - Spring
Use NoOpPasswordEncoder
for inMemoryAuthentication
auth.inMemoryAuthentication()
.withUser("user")
.password("{noop}password")
.roles("USER")
Solution 3 - Spring
You need to have some sort of a password encoder, but
withDefaultPasswordEncoder()
is deprecated and no more suitable for production. Use this instead:
PasswordEncoder encoder =
PasswordEncoderFactories.createDelegatingPasswordEncoder();
...
UserDetails user = User.withUsername("someusername")
.password(encoder.encode("somepassword"))
.roles("USER").build();
Solution 4 - Spring
I simply added
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
To the configuration class
Solution 5 - Spring
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("test").password("test123").roles("USER").and()
.withUser("test1").password("test123").roles("ADMIN");
}
Solution 6 - Spring
Try this in SecurityConfig extends WebSecurityConfigurerAdapter
:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception{
auth
.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder().encode("admin"))
.roles("USER", "ADMIN");
}
Work for me
Solution 7 - Spring
You can use this, but be advised that User.withDefaultPasswordEncoder()
is deprecated:
@Bean
@Override
public UserDetailsService userDetailsService() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
final User.UserBuilder userBuilder = User.builder().passwordEncoder(encoder::encode);
UserDetails user = userBuilder
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = userBuilder
.username("admin")
.password("password")
.roles("USER","ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
Solution 8 - Spring
Use any of the following and it will work fine:-
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("{noop}admin@123").roles("admin");
}
or
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("{noop}admin").roles("admin");
}
or
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance()).withUser("admin").password("{noop}admin").roles("admin");
}
Thanks!
Solution 9 - Spring
You need to set the password encoder like this:
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
It's required to use the passwordEncoder for the users and also for the clients in Spring Boot 2
Check out this repository https://github.com/dzinot/spring-boot-2-oauth2-authorization-jwt
You can see how the passwordEncoder is set up and how to use it with users and clients in the database.
Solution 10 - Spring
Prefix all existing passwords with {noop} to keep the default encoder of Spring Security 5.
Example:
auth.inMemoryAuthentication()
.withUser("admin").password("{noop}admin!234").roles("ADMIN");
Solution 11 - Spring
According to [spring security 5.0 's new feature.][1] They write [this][2].
> Spring Security’s PasswordEncoder interface is used to perform a one > way transformation of a password to allow the password to be stored > securely. Given PasswordEncoder is a one way transformation, it is not > intended when the password transformation needs to be two way (i.e. > storing credentials used to authenticate to a database). Typically > PasswordEncoder is used for storing a password that needs to be > compared to a user provided password at the time of authentication.
So i tried this [Mutiple HttpSecurity][3]. This s my security configuration. Hope it help you.
@Configuration
@EnableWebSecurity
public class SecurityConfig
{
private final EdminService edminService;
public SecurityConfig ( final EdminService edminService ){
this.edminService=edminService;
}
@Bean
public UserDetailsService userDetailsService() throw Exception {
UserBuilder users= Users.withDefaultPasswordEncoder;
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
List<EdminEntity> edminList=this.edminService.findAll();
for(EdminEntity edmin: edminList){
manager.createUser(users.username(edmin.getEdminname())
.password(edmin.getEdminrpass()).roles("EDMIN_ROLE").build());
}
return manager;
}
@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/home","/vendor/**","/image/**","/home/**").permitAll()
.antMatchers("/admin/**").hasRole("EDMIN_ROLE")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/home")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));}
}
}
Sorry for my english and thanks for read my answer. [1]: https://docs.spring.io/spring-security/site/docs/current/reference/html/new.html [2]: https://docs.spring.io/spring-security/site/docs/current/reference/html/core-services.html#core-services-password-encoding [3]: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#multiple-httpsecurity
Solution 12 - Spring
A few days ago I have to face the same problem on spring security version (5.0.8). See the example version here:
code:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("farid").password("farid").roles("USER")
.and()
.withUser("admin").password("admin").roles("ADMIN");
}
OR
When you are configuring the ClientDetailsServiceConfigurer, you have to also apply the new password storag`enter code here`e format to the client secret.
.secret("{noop}secret")
you can see the link: enter link description here
Solution 13 - Spring
You need to set Password encoder , check the following sample
PasswordEncoder encoder =
PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication().passwordEncoder(encoder).withUser("Ahmad")
.password("1600").roles("USER", "ADMIN").and().withUser("Belal").password("1515").roles("USER");
Solution 14 - Spring
Had IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
exception in my initial app loading startup housekeeper. (Just needed to normalize data in DB before the app start.) With this (manually set an authenticated user) approach you can create UsernamePasswordAuthenticationToken
and setAuthentication. You can get IllegalArgumentException, if you don't specify 3-rd argument AuthorityList
.
UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(
admin.getEmail(),
UserServiceImpl.PASSWORD_ENCODER.encode(admin.getPassword()),
AuthorityUtils.createAuthorityList(admin.getRoles()) // <= had to add this line to resolve the exception
);
SecurityContextHolder.getContext().setAuthentication(authReq);
Maybe some other setup steps could also work, but setting AuthorityList is good enough for me. And you can at least dig in this direction if can't find a solution on this page.
Solution 15 - Spring
If you are doing in Memory Authentication, here's the code on how to go about it. Tested on Spring Boot 2.3.3.RELEASE
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
static PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("testuser").password(encoder.encode("testpassword")).roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/sites.xhtml/**").hasRole("ADMIN").anyRequest().fullyAuthenticated().and()
.httpBasic();
}
@Bean
public static PasswordEncoder passwordEncoder() {
return encoder;
}
}
Solution 16 - Spring
As I have found many different upvoted answers to this question with very little explanation, I decided to provide my own answer which should provide a more in-depth understanding of how Spring Security password encoding works. I spent some time debugging the Spring code to figure out all the details.
Internally, Spring Security uses a DelegatingPasswordEncoder
when authenticating users. Assuming the credentials user:password
are provided during login, the DelegatingPasswordEncoder
loads the stored password for this particular user and tries to determine its encoding. The DelegatingPasswordEncoder
expects passwords to be stored in the form {type}hash
:
{bcrypt}hash
{scrypt}hash
{MD5}hash
{noop}hash
...
If the default DelegatingPasswordEncoder
now encounters a stored password without the {type}
, it cannot infer which encoder to use and instead uses an UnmappedIdPasswordEncoder
which throws an exception. This means that the exception occurs because the password is stored without any encoding information and Spring Security uses a DelegatingPasswordEncoder
which is not able to handle this.
One way to solve this issue is to also use the DelegatingPasswordEncoder
to encode and store the password. The encoder retrieved with PasswordEncoderFactories.createDelegatingPasswordEncoder()
will use bcrypt
as default encoding so that the password will be stored as {bcrypt}hash
:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth.inMemoryAuthentication()
.withUser("user")
.password(encoder.encode("password"))
.roles("USER");
}
The DelegatingPasswordEncoder
is useful when you want to handle different encodings in your project. For example, assume old passwords were stored in plain text and new passwords are now encoded with bcrypt
. You will just have to prefix all your old stored passwords with {noop}
and you are good to go. It would work the same if you had stored passwords encoded with MD5, just prefix them with {MD5}
.
In a new project it would also be possible and is perfectly fine to define a specific encode like the BCryptPasswordEncoder
instead of using the delegating one. When the encoder is registered as a bean, Spring Security will also use this encoder for authentication instead of the DelegatingPasswordEncoder
. Passwords are now stored as bcrypt hash
without the {type}
.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("password"))
.roles("USER");
}
So you are able to chose one of the described variants. I recently went with PasswordEncoderFactories.createDelegatingPasswordEncoder()
as this only requires one line of code and no bean creation is necessary.
Solution 17 - Spring
This is how I solved it
@Bean
public PasswordEncoder delegatingPasswordEncoder() {
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("bcrypt", encoders);
passwordEncoder.setDefaultPasswordEncoderForMatches(new BCryptPasswordEncoder());
return passwordEncoder;
}