0

我正在尝试在使用 spring security 和 boot 成功注册后对用户进行身份验证。登录和注册本身工作正常,但是当我注册时,我总是被重定向到登录页面,尽管我已经关注了这个线程(成功注册后自动登录)。

大多数在线解决方案似乎以几乎相同的方式处理该问题,但没有一个对我有用。

以下是可能影响我的问题的相关类:

注册控制器:

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

网络安全配置

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

WebMvc 配置:

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

用户服务:

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

注册.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>

如果您认为我遗漏了什么,请随时要求我提供。

祝你有美好的一天。

4

2 回答 2

0

我想到了。验证并保存新用户后,我添加了 request.login(username, password) 。

@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);
            String password = user.getPassword();
            userService.save(user);
            request.login(user.getEmail(), password);
            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");
        } catch (ServletException e) {
            e.printStackTrace();
            modelAndView.setViewName(VIEW_PATH + "signup");
        }
    }
    return modelAndView;
}

我从我的控制器中删除了 authenticateUser 方法。

于 2019-08-19T11:09:36.690 回答
-1

spring UsernamePasswordAuthenticationToken API 状态:

主体凭据应设置为通过其 Object.toString() 方法提供相应属性的对象。最简单的此类对象是字符串。

因此,我认为

UsernamePasswordAuthenticationToken token = 
               new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()); 

应该

String username = user.getEmail();
String password = user.getPassword();
UsernamePasswordAuthenticationToken token =
                new UsernamePasswordAuthenticationToken(username, password);
于 2019-07-22T16:04:59.830 回答