I'm using Spring security and use an encoder to encode passwords.
So, in my Spring Security Config I have auto wired the
PasswordEncoder passwordEncoder()
and added it to the DaoAuthenticationProvider
, this is my Spring Security config
package it.besmart.easyparking.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
import org.springframework.web.servlet.support.RequestDataValueProcessor;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
@Autowired
CustomSuccessHandler customSuccessHandler;
@Autowired
CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
@Autowired
DataSource dataSource;
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/home", "/user/**").permitAll()
.antMatchers("/spots/**", "/parks/**", "/floors/**", "/lights/**", "/sensors/**", "/illumination/**",
"/occupation/**", "/movement/**", "/map/**", "/include/**")
.access("hasRole('USER') or hasRole('ADMIN') or hasRole('PARK')").antMatchers("/admin/**")
.access("hasRole('ADMIN') and hasRole('PARK')").antMatchers("/updatePassword")
.hasAuthority("CHANGE_PASSWORD_PRIVILEGE").
and().formLogin().loginPage("/login").successHandler(customSuccessHandler)
.failureHandler(customAuthenticationFailureHandler).usernameParameter("email")
.passwordParameter("password").and().rememberMe().rememberMeParameter("remember-me")
.tokenRepository(persistentTokenRepository()).tokenValiditySeconds(86400).and().csrf().and()
.exceptionHandling().accessDeniedPage("/Access_Denied");
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
tokenRepositoryImpl.setDataSource(dataSource);
return tokenRepositoryImpl;
}
@Bean
public RequestDataValueProcessor requestDataValueProcessor() {
return new CsrfRequestDataValueProcessor();
}
}
When i pass data from my DTO to the Model I simply do the following
user.setPassword(passwordEncoder.encode(accountDTO.getPassword()));
And in my DB i see the encoded password, for instance something like $2a$10$vVCWjKltOiYO0nPYT1qYI.z4TSk2QJqViDOqRfmoB6BAgldF4vAmm
But when i try to login i'm getting
org.springframework.security.authentication.BadCredentialsException: Bad credentials
When i see logs, i find this
o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
My password field in the DB is varchar(100) so i think there is enough space to store it... If i change the encoded password in the DB with a decoded one, i can login...
this is my CustoUserDetailsService
@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
UserRepository repository;
private final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
try {
User user = repository.findByEmail(email);
if (user == null) {
throw new UsernameNotFoundException("No user found with username: " + email);
}
logger.debug("user: " + user.toString());
return new org.springframework.security.core.userdetails.User(user.getEmail(),
user.getPassword(), user.isEnabled(), accountNonExpired, credentialsNonExpired, accountNonLocked,
getAuthorities(user));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private List<GrantedAuthority> getAuthorities(User user) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getUserProfile().getType()));
// System.out.print("authorities :"+authorities);
return authorities;
}
}
Spring Security's PasswordEncoder interface is used to perform a one way transformation of a password to allow the password to be stored securely.
The default username is: user and the default password will be printed in the console at the time when your Spring Boot project is starting.
There are a few encoding mechanisms supported by Spring Security, and for this tutorial, we'll use BCrypt, as it's usually the best solution available. Most of the other mechanisms, such as the MD5PasswordEncoder and ShaPasswordEncoder, use weaker algorithms and are now deprecated.
The exception has been thrown by UserDetailsService.loadUserByUsername()
method at the return
position because of the wrong instantiated User
object that hasn't received the encoded password in the corresponding field.
The correct code would look like this:
...
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
UserEntity user = userRepository.findByUsername(username);
if (user == null)
throw new UsernameNotFoundException("Bad credentials");
return new User(
user.getUsername(),
user.getPassword(), // shall to be the already BCrypt-encrypted password
getAuthorities());
}
The org.springframework.security.authentication.BadCredentialsException: Bad credentials
will be thrown once the user.getPassword()
isn't well formed BCrypt hashsum.
The password encoder may be registered like this:
@Autowired
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
That's how it works.
The PasswordEncoder should be set like this:
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Also you need to use the passwordEncoder for the users and clients.
You can check this repository https://github.com/dzinot/spring-boot-2-oauth2-authorization-jwt
It uses Spring Boot 2, JWT tokens and the Users and Clients are stored in the database.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With