spring boot security not configured properly

Tags: , , , ,



I am new to Spring boot and Spring boot security. However, using the existing sample codes, I had written a code which worked correctly. In my design, I had a separate page for my login, called login.html. Now, I have changed my design and my sign in and sign up are in a single form in the home page and it seems to be problematic. Here I explain the problem in more details:

  1. I open the home page and then click on the sign in button. 2) I enter the username and password and click on the sign in button. Then, I see the dashboard page as expected. So far, so good.

The homepage, looks like the following: enter image description here and here is the code for the signin/signup form in the home.html:

<form th:action="@{/signup}" method="post">
               <input type="hidden" name="action" id="action" value="">
            
                    <div>
                        <input type="email" name="email" id="inputEmail" placeholder="email" required>
                    </div>
                    <div>
                        
                        <input type="password" name="password" id="inputPassword" placeholder="password" required>
                    </div>
                <button type="submit">SIGN IN</button>
            </form>

Here is the signin/signup controller:

 @RequestMapping(value = "/signup", method = RequestMethod.POST)
public ModelAndView createNewUser(@Valid User user, BindingResult bindingResult, @RequestParam("action") String action) {
    
    String act = action;
    
    ModelAndView modelAndView = new ModelAndView();
    User userExists = userService.findUserByEmail(user.getEmail());

    if(action.equalsIgnoreCase("signup")) {
            if (userExists != null) {
        bindingResult
                .rejectValue("email", "error.user",
                 "There is already a user registered with the username");
    }
    if (bindingResult.hasErrors()) {
        modelAndView.setViewName("signup");
    } else {
        userService.saveUser(user);
        modelAndView.addObject("successMessage", "User has been registered successfully");
        modelAndView.addObject("user", new User());
        modelAndView.setViewName("home");
    }
    }else if(action.equalsIgnoreCase("signin")) {
         modelAndView.addObject("currentUser", userExists);
         modelAndView.addObject("firstname", userExists.getFirstname());
         modelAndView.addObject("lastname", userExists.getLastname());

         modelAndView.addObject("adminMessage", "Content Available Only for Users with Admin Role");
        
        modelAndView.setViewName("dashboard");
    }
return modelAndView;

}

So far, everything works and after signing in, I am forwarded to the dashboard page which looks like the following: enter image description here Now, if I click on John which is supposed to forward me to the profile page or click on submit which is supposed to forward me to another page, it returns to the home page in which the user is NOTsigned in. Here is the form in the dashboard which should call a controller method when I click on the submit button:

    <form  action="/addReview" method="POST" name="addReviewForm"onsubmit="return validateForm()">
<input type="hidden" id="category"name="category" />
<input type="text" id="first_input" name="first_point" placeholder="Point 1">
<input type="text" id="seventh_input" name="seventh_point" placeholder="Point 7">
<button type="submit">submit</button></form>

And here is the configuration for the security:

  @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/").permitAll()
           // .antMatchers("/login").permitAll()
            .antMatchers("/signup").permitAll()
            .antMatchers("/search").permitAll()
            .antMatchers("/dashboard/**").hasAuthority("ADMIN").anyRequest()
            .authenticated().and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
            .loginPage("/").failureUrl("/?error=true")
            .usernameParameter("email")
            .passwordParameter("password")
            .and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/").and().exceptionHandling();
}

I think the problem is with .loginPage("/").failureUrl("/?error=true"), but I’m not sure. I don’t have any separate login page. In the meantime, the function validateForm() is properly called. Just the controller method is not called.

I hope somebody can help me.

Thanks in advance,

##Update 1:##

I changed and simplified the code to make it easier to find the problem. Now, I have the following form in my home.html:

    <form th:action="@{/}" method="post">
    <input type="email" name="email" id="inputEmail" placeholder="email" required>
    <input type="password" name="password" id="inputPassword" placeholder="password" required>
<input type="submit" value="SIGN IN" />

And the following configurations:

@Override
protected void configure(HttpSecurity http) throws Exception {
    
  http
  .authorizeRequests()
  .antMatchers("/").permitAll()
  .antMatchers("/search").permitAll()
  .antMatchers("/dashboard/**").hasAuthority("ADMIN").anyRequest()
  .authenticated()
  .and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
  .loginPage("/")
  .failureUrl("/?error=true")
  .usernameParameter("email")
  .passwordParameter("password").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling();
}
    

And the following controllers:

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public ModelAndView createNewUser(@Valid User user, BindingResult bindingResult, @RequestParam("email") String email) {

ModelAndView modelAndView = new ModelAndView();
        User userExists = userService.findUserByEmail(user.getEmail());
             modelAndView.addObject("currentUser", userExists);
             modelAndView.addObject("firstname", userExists.getFirstname());
             modelAndView.addObject("lastname", userExists.getLastname());
          
            modelAndView.setViewName("dashboard");
    return modelAndView;

    }
     @RequestMapping(value = {"/","/home"}, method = RequestMethod.GET)
    public ModelAndView home() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("home");
        return modelAndView;
    }

With this code, I can’t even see the dashboard. Clicking on the sign in button returns me to the http://localhost:8080/?error=true page.

Answer

Starting with the security configuration, I will rewrite it using the lambda DSL and explain what it is doing.

protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests(authorize -> authorize
                    .antMatchers("/").permitAll()
                    .antMatchers("/signup").permitAll()
                    .antMatchers("/search").permitAll()
                    .antMatchers("/dashboard/**").hasAuthority("ADMIN")
                    .anyRequest().authenticated()
            )
            .formLogin(formLogin -> formLogin
                    .successHandler(customizeAuthenticationSuccessHandler)
                    .loginPage("/")
                    .failureUrl("/?error=true")
                    .usernameParameter("email")
                    .passwordParameter("password")
            )
            // ...
}

This is saying that requests to “/”, “/signup”, “/search” are permitted without requiring the user to log in.
Requests to “/dashboard/**” require the user to be logged in and have the role “ADMIN”.
Requests to any other endpoint require the user to be logged in.
This means that a request to “/addReview” requires the user to be logged in.

In the case you described, the user is not logged in, which is why they are not permitted to access “/addReview”.
Spring Security is preventing the request from arriving at the Controller because there is no logged in user.

Spring Security stores the details of who is authenticated in the SecurityContextHolder.
Looking at the provided code, the SecurityContextHolder is never populated, so from a Spring Security standpoint, nobody is logged in.

The “/signup” endpoint above is simply setting some fields on the view (HTML page) and not actually performing a login.

Since you have enabled .formLogin() in the configuration, Spring Security provides most of the login functionality.
You just need to POST the username (in this case email) and password to the generated “/” login endpoint and the framework will validate the user and populate the SecurityContextHolder.

<form th:action="@{/}" method="post">
    <div>
        <input type="email" name="email" id="inputEmail" placeholder="email" required>
    </div>
    <div>
        <input type="password" name="password" id="inputPassword" placeholder="password" required>
    </div>
    <input type="submit" value="SIGN IN" />
</form>

Then you can use the “/signup” endpoint only for registration, rather than combining it with the login logic.

You may also want to look into the Thymeleaf + Spring Security integration. This will allow you to get the username in your template without having to set it in the Controller.

<div>Logged in user: <span sec:authentication="name"></span></div>

I would advise you against disabling CSRF protection. This makes your application vulnerable to CSRF attacks. The CSRF token is automatically included when using Thymeleaf, so there should be no code changes required and your application will be more secure.



Source: stackoverflow