Search code examples
javathymeleaf

Interacting with html file from controller using Thymeleaf


I am currently trying to create a controller to interact with an html file using Thymeleaf. After a while I noticed that perhaps my controller (more specifically the @PostMapping) isn't interacting with my html page at all. The reason I thought this is because no matter what input I placed as email/password (be it correct or incorrect) it would always link me to (/login?error). I tested out a simple conditional that would print "HERE" on the html page once the Post request is called to see if that was truly the case. The print never occurs. The point of this post is to understand why this simple post request is being ignored. Since I a making a simple Post request, I would assume that the "Here" would get printed no matter what.

My html code

<!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>

My Controller 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";
    }
}

My Spring Security Configurations

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

Solution

  • come on, first you need a controller for your home page..

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

    then need to configure the security to:

    1 - consider email and password for authentication.

    2 - configure the home page as the default login page.

    3 - configure logout page, the spring default is /login?logout, but I'll show you a way to return a variable as a parameter.

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

    done that, your login should be successful and go to the home page.

    to validate if the email has been verified, you must first have the login validated, so that the user is found correctly, and having the user validated, you can check if the email has been verified. The easiest way, is when you get redirected to /home, you can get the username and validate that the email is verified, if not, you redirect back to /login?unverified-email.

    so then your controller would look something like this:

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

    and on the login page, to show the unverified email message, you can add something like:

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

    Of course, maybe it's not the best way to do it, but it worked for me.