使用 Thymeleaf 与来自控制器的 html 文件交互

Interacting with html file from controller using Thymeleaf

我目前正在尝试创建一个控制器来使用 Thymeleaf 与 html 文件进行交互。一段时间后,我注意到我的控制器(更具体地说是 @PostMapping)可能根本没有与我的 html 页面交互。我认为这是因为无论我将什么输入设置为 email/password(正确或不正确),它总是 link 我(/login?error)。我测试了一个简单的条件语句,一旦 Post 请求被调用,它会在 html 页面上打印“HERE”,以查看是否确实如此。打印永远不会发生。 post 的目的是理解为什么忽略这个简单的 post 请求。由于我提出了一个简单的 Post 请求,因此我假设无论如何都会打印“此处”。

我的html代码

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="ISO-8859-1">
    <title>Login Page</title>
</head>
<body>
    <form th:action="@{/login}" th:object="${user}" method="post">
        
        <div th:if="${here}">
            <p>HERE</p>
        </div>
        
        <div>
            <label>Email</label>
            <input type="text" th:field="*{email}">
        </div>
    
        <div>
            <label>Password</label>
            <input type="text" th:field="*{password}" placeholder="Password">
        </div>
        
        <input type="submit" value="submit"/>
    </form>
</body>
</html>

我的控制器class

@Controller
public class UserController {
    
    UserRepository userRepo;
    UserService userService;
    
    @GetMapping("/login")
    public String login(Model model) {
        model.addAttribute("user", new User());
        return "login";
    }
    
    @PostMapping("/login")
    public String loginUser(@ModelAttribute("user") User user, Model model) {
        model.addAttribute("here", true);

        return "login";
    }
}

我的 Spring 安全配置

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    private final UserService userService;
    private final BCryptPasswordEncoder passwordEncoder;
    
    public WebSecurityConfig(UserService userService, BCryptPasswordEncoder passwordEncoder) {
        this.userService = userService;
        this.passwordEncoder = passwordEncoder;
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests()
                .antMatchers("/registration/**", "/login")
                .permitAll()
                .anyRequest()
                .authenticated().and()
                .formLogin().loginPage("/login").permitAll();;
        
        return http.build();
    }

    
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        
    }
    
    @Bean
    public DaoAuthenticationProvider daoAutenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setPasswordEncoder(passwordEncoder);
        provider.setUserDetailsService(userService);
        return provider;
    }
}

来吧,首先你需要一个用于你的 home 页面的控制器..

@RequestMapping("/home")
public String home() {
    return "home";
}

然后需要将security配置为:

1 - 考虑 emailpassword 进行身份验证。

2 - 将 home 页面配置为默认登录页面。

3 - 配置注销页面,spring默认是/login?logout,但我会告诉你一个方法return一个变量作为参数。

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeHttpRequests()
            .antMatchers("/registration/**", "/login")
            .permitAll()
            .anyRequest().authenticated()
            .and()
                .formLogin()
                    .loginPage("/login")
                    .usernameParameter("email")
                    .passwordParameter("password")
                    .defaultSuccessUrl("/home", true)
                .permitAll()
            .and()
                .logout()
                .logoutSuccessUrl("/login?logout")
        .logoutSuccessUrl("/login?unverified-email"); //unverified email variable.
    
    return http.build();
}

完成后,您的登录应该会成功并转到主页。

验证邮箱是否通过验证,首先要验证登录,这样才能正确找到用户,通过用户验证后,就可以查看邮箱是否通过验证了。最简单的方法是,当您被重定向到 /home 时,您可以获取用户名并验证电子邮件是否已通过验证,如果没有,您将重定向回 /login?unverified-email.

那么你的控制器看起来像这样:

UserService userService;

@RequestMapping("/home")
public String home() {
User user = userService.getUser();
if(!user.verified)
    return "redirect:/login?unverified-email";

    return "home";
}

UserService

public User getUser() {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String user;
        
    if (principal instanceof UserDetails) {
        user = ((UserDetails)principal).getUsername();    
    } else {
    user = principal.toString();
    }
    return repository.findByEmail(user);
}

并且在登录页面上,要显示未验证的电子邮件消息,您可以添加如下内容:

<div th:if="${param.unverified-email}">
    User not confirmed, please confirm your Email.
</div>

当然,也许这不是最好的方法,但它对我有用。

因此,为了处理不正确的登录,您有多种选择。要指定处理登录错误的不同页面(与登录页面不同),您可以在 Spring security.

中调用 failureUrl("/loginFailure")

但是如果您仍想使用相同的登录名 page/request(例如 /login )来处理登录错误,您可以通过为 GET /login 提供一个端点来做到这一点(注意 POST /login 由 Spring 安全性处理,正如评论中提到的那样),您可以在其中检查 url 是否包含名为“error”的参数。

@RequestMapping("/login")
public ModelAndView loginUser(ModelAndView mv, 
        HttpServletRequest request) {
 //for failed logins, the query string is equal to error
 String queryStr = request.getQueryString();
 if(queryStr != null && "error".equals(queryStr)){
    mv.addObject("errorMsg", "wrong credentials");
    mv.setViewName("errorPage");
 }else {
   //process other scenarios
 }


 return mv;
}

现在,在 Thymeleaf 中,您可以简单地检查 errorMsg 的值并向用户显示适当的消息。