Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring security - encoded password gives me Bad Credentials

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;
    }

}
like image 391
MarioC Avatar asked Mar 24 '17 19:03

MarioC


People also ask

Does Spring Security support password encoding?

Spring Security's PasswordEncoder interface is used to perform a one way transformation of a password to allow the password to be stored securely.

What is the default password for Spring Security?

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.

Does Spring Security use BCrypt?

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.


2 Answers

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.

like image 83
WildDev Avatar answered Oct 15 '22 02:10

WildDev


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.

like image 20
Dzinot Avatar answered Oct 15 '22 02:10

Dzinot