I'm trying make a user authenticated after a successful sign-up with spring security and boot. The login and the registration by themselves work fine, but when I sign-up, I'm always redirected to the login page despite me having followed this thread (Auto login after successful registration).
Most solutions online seem to deal with the problem almost the same way, but none of them worked for me.
Here are the relevant classes that might affect my problem:
Signup controller:
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.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import training.spring.profilemanger.exception.PasswordsNotMatchingException;
import training.spring.profilemanger.exception.UserEmailExistsException;
import training.spring.profilemanger.model.User;
import training.spring.profilemanger.model.UserLogin;
import training.spring.profilemanger.service.UserService;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
@Controller
public class IdentificationController {
private UserService userService;
protected AuthenticationManager authenticationManager;
public IdentificationController(UserService userService, AuthenticationManager authenticationManager) {
this.userService = userService;
this.authenticationManager = authenticationManager;
}
private static final String VIEW_PATH = "identification/";
@GetMapping({"/login", "/logout"})
public ModelAndView loginForm(ModelAndView modelAndView) {
modelAndView.addObject("userLogin", new UserLogin());
modelAndView.setViewName(VIEW_PATH + "login");
return modelAndView;
}
@GetMapping("/signup")
public ModelAndView signupForm(ModelAndView modelAndView) {
modelAndView.addObject("user", new User());
modelAndView.setViewName(VIEW_PATH + "signup");
return modelAndView;
}
@PostMapping("/signup")
public ModelAndView signupSubmit(ModelAndView modelAndView, @ModelAttribute @Valid User user,
BindingResult bindingResult, @ModelAttribute("passwordConfirmation") String passwordConfirmation,
HttpServletRequest request) {
if (bindingResult.hasErrors()) {
modelAndView.setViewName(VIEW_PATH + "signup");
} else {
try {
userService.validateAllFields(user, passwordConfirmation);
userService.save(user);
authenticateUser(user, request);
modelAndView.addObject("users", userService.findAll());
modelAndView.setViewName("redirect:/users");
} catch (PasswordsNotMatchingException e) {
bindingResult.rejectValue("password", "error.user", "passwords are not matching");
modelAndView.setViewName(VIEW_PATH + "signup");
} catch (UserEmailExistsException e) {
bindingResult.rejectValue("email", "error.user", "email already in use");
modelAndView.setViewName(VIEW_PATH + "signup");
}
}
return modelAndView;
}
private void authenticateUser(User user, HttpServletRequest request) {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword());
// generate session if one doesn't exist
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
Authentication authenticatedUser = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
}
}
WebSecurityConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.password.PasswordEncoder;
import training.spring.profilemanger.service.UserService;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private PasswordEncoder passwordEncoder;
private UserDetailsService userService;
public WebSecurityConfig(PasswordEncoder passwordEncoder, UserService userService) {
this.passwordEncoder = passwordEncoder;
this.userService = userService;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login", "/signup").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth
.authenticationProvider(authProvider());
}
@Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userService);
authProvider.setPasswordEncoder(passwordEncoder);
return authProvider;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
WebMvcConfig :
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class WebMvcConfig {
@Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
}
UserService:
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import training.spring.profilemanger.exception.PasswordsNotMatchingException;
import training.spring.profilemanger.exception.UserEmailExistsException;
import training.spring.profilemanger.model.MyUserDetails;
import training.spring.profilemanger.model.User;
import training.spring.profilemanger.repository.UserRepository;
@Service
public class UserService implements UserDetailsService {
private UserRepository userRepository;
private PasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
public void validateAllFields(User user, String passwordConfirmation) throws PasswordsNotMatchingException, UserEmailExistsException {
user = trimWhiteSpaces(user);
checkPasswordsMatching(user.getPassword(), passwordConfirmation);
if (userRepository.findByEmail(user.getEmail()) != null) {
throw new UserEmailExistsException();
}
}
public User save(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
private User trimWhiteSpaces(User user) {
user.setFirstName(user.getFirstName().trim());
user.setLastName(user.getLastName().trim());
user.setEmail(user.getEmail().trim());
return user;
}
public Iterable<User> findAll() {
return userRepository.findAll();
}
private void checkPasswordsMatching(String password, String passwordConfirmation) throws PasswordsNotMatchingException {
if (passwordConfirmation == null || !passwordConfirmation.equals(password)) {
throw new PasswordsNotMatchingException();
}
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if (user != null) {
return new MyUserDetails(user);
} else {
throw new UsernameNotFoundException(username + " doesn't exist");
}
}
}
signup.html:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>User Form</title>
</head>
<body>
<h1>User Sign-up Form:</h1>
<form action="#" th:action="@{/signup}" th:object="${user}" method="post">
<input type="hidden" th:field="*{id}"/>
<table>
<tr>
<td>First name</td>
<td>:<input type="text" th:field="*{firstName}"/></td>
<td><label th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}" style="color: red"/></td>
</tr>
<tr>
<td>Last name</td>
<td>:<input type="text" th:field="*{lastName}"/></td>
<td><label th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}" style="color: red"/></td>
</tr>
<tr>
<td>Email</td>
<td>:<input type="email" th:field="*{email}"/></td>
<td><label th:if="${#fields.hasErrors('email')}" th:errors="*{email}" style="color: red"/></td>
</tr>
<tr>
<td>Password</td>
<td>:<input type="password" th:field="*{password}"/></td>
<td><label th:if="${#fields.hasErrors('password')}" th:errors="*{password}" style="color: red"/></td>
</tr>
<tr>
<td>Password Confirmation</td>
<td>:<input type="password" name="passwordConfirmation"/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Submit"/><input type="reset" value="Reset"/></td>
</tr>
</table>
</form>
</body>
</html>
If you think I'm missing something, don't hesitate to ask me to provide it.
I wish you a very nice day.