简体   繁体   中英

CSRF/Spring Security - 403 Error After Login via AJAX

I'm encountering an issue when using CSRF protection with Spring Security 5.1.4.

I have set up a custom login page for the application. When the user enters his or her credentials, an AJAX request is made. The success handler performs its responsibilities and returns a JSON document in the response. When the AJAX call succeeds, the screen is dynamically updated by removing the username and password fields and replacing them with a dropdown field and a Continue button.

When the user makes a selection from the dropdown and clicks the Continue button, the action on the form is changed and the form is submitted using the POST method. The user is then to be directed to a home screen. Unfortunately, the application responds with a Forbidden (403) error.

When I modify the WebSecurityConfigurerAdapter to disable CSRF protection, my login screen performs as expected.

Is there something I'm missing when I submit the form? Note that I've included the CSRF parameter and token in a hidden field within the form element.

WebSecurityConfigurerAdapter

protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/css/**", "/images/**", "/js/**")
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .successHandler(customAuthenticationHandler)
                .permitAll()
                .and()
                .logout()
                .invalidateHttpSession(true)
                .deleteCookies("JESSIONID")
                .permitAll();
    }

public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
            .withUser("user").password("password").roles("USER");
}

Controller

@RequestMapping(value = "/login", method = RequestMethod.GET)
public String logIn() {
    return "login-custom";
}

@RequestMapping(value = "/home", method = RequestMethod.POST)
public String home() {
    return "home";
}

JSP

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title></title>

    <script type="text/javascript" src="/workdaybsa/js/jquery-1.10.2.min.js"></script>
    <script language="javascript">

        function login() {
            $.ajax({
                url: $('#loginForm').attr('action'),
                type: 'POST',
                data: $('#loginForm').serialize(),
                dataType: "json",
                success: function(response){
                    $('#loginSection').attr('style', 'display:none');
                    $('#selectionSection').removeAttr('style');
                },
                error: function (xhr, status, error) {
                    alert(status);
                    alert(error);
                }
           });
        }

        function proceed() {
            $('form').attr('action', 'home');
            $('form').submit();
        }
    </script>

</head>

<body>

    <form id="loginForm" action="login" method="post">

        <div id="loginSection">
            <p>
                <label for="username">Username</label>
                <input type="text" id="username" name="username">
            </p>
            <p>
                <label for="password">Password</label>
                <input type="password" id="password" name="password">
            </p>
            <input type="button" value="Sign In" onclick="login()">
        </div>
        <div id="selectionSection" style="display:none">
            <select id="selection">
                <option value="">-- Select The Option --</option>
            </select>
            <input type="button" value="Continue" onclick="proceed()">
        </div>
        <input type="hidden" id="csrf" name="${_csrf.parameterName}" value="${_csrf.token}"/>

    </form>

</body>

</html>

CSRF token is an anti forgery token that you'll need to pass back to your Spring Security on every request using Csrf-Token header attribute which you didn't include in your ajax call even though it's in a hidden input field. That's why the CSRF validation fails and throw a 403.

The solution is provided here in this SO question

To shamelessly copy paste the answer, this is what you need:

$.ajax({
  url: route.url,
  ...
  headers: {
    'Csrf-Token': $('#csrf").val()
  }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM