3

Here is a controller method for user registration:

@PostMapping("register_user")
public void registerUser(@RequestParam String email, @RequestParam String password, @RequestParam String name,
                         @RequestParam String info, HttpServletResponse response) throws EmailExistsException, IOException {
    userRepository.save(new User(email, new BCryptPasswordEncoder().encode(password), name, info));
    try {
        UserDetails userDetails = customUserDetailsService.loadUserByUsername(email);
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
        authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        if (usernamePasswordAuthenticationToken.isAuthenticated()) {
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            log.debug(String.format("Auto login %s successfully!", email));
        }
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
    response.sendRedirect("/");
}

Here is a configure method from SecurityConfig:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}

During user registration there is a "Bad credentials" error:

org.springframework.security.authentication.BadCredentialsException: Bad credentials
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:98) ~[spring-security-core-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:166) ~[spring-security-core-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at s7.controller.ActionController.registerUser(ActionController.java:45) ~[main/:na]

After registration user can login without any errors. What am I doing wrong?

P.S. I also tried auto login like this topic: Auto login after successful registration But I have the same BadCredentialsException.

If I comment authenticationManager.authenticate(usernamePasswordAuthenticationToken);, user will auto login without any BadCredentialsException with correct authentication.getPrincipal().

Community
  • 1
  • 1
Serge Nikitin
  • 1,628
  • 5
  • 19
  • 27

2 Answers2

1

Add 'HttpServletRequest request' parameter to registerUser method.

Right after this line: SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

Add this line of code: request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());

Otherwise the user won't be in session which is required by Spring Security.

Hope this help!

Gustavo Soler
  • 1,469
  • 2
  • 9
  • 6
1

I was able to solve this problem with the following code (SpringBoot 2.0.5):

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

@Service
public class SecurityService {

    @Autowired
    @Lazy
    AuthenticationManager authManager;

    private Logger logger = LoggerFactory.getLogger(SecurityService.class);

    public void doLogin(String username, String password) {

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);

        Authentication authentication = authManager.authenticate(authRequest);

        if (authentication.isAuthenticated()) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
            logger.debug(String.format("Auto login successfully!", username));
        }
    }

}

To make it work the hardest bit for me to figure out was the AuthenticationManager. You have to Autowire the same AuthenticationManager you set in your spring security configuration as shown bellow, dont forget to autowire lazy on the service.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UsuarioService usuarioService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests()
                .antMatchers("/registration*").permitAll()
                .anyRequest().hasAnyAuthority("MASTER")
                .and()
                .formLogin().loginPage("/login").permitAll()
                .and()
                .csrf().disable()
                .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login");

    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**", "/css/**", "/img/**", "/files/**", "/forgot-password"");
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(usuarioService);
    }

    @Bean
    public AuthenticationManager authManager() throws Exception {
        return this.authenticationManager();
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        return bCryptPasswordEncoder;
    }
}

And the last problem that took me a while to figure out. If you add your registration endpoint with the "web.ignoring()" security context will not be present and you will not be able to log in the user. So be sure to add it as .antMatchers("/registration*").permitAll()

Eduardo Mayer
  • 359
  • 1
  • 3
  • 10