vendredi 31 juillet 2015

Spring Security AJAX / JSON Login Issues

I am trying to implement Spring Security with the login form being submitted via Ajax. I have created my own custom authenticationFailureHandler and my own custom authenticationSuccessHandler. I'm not sure if my issue lies in my Spring / Java code or on my client side code. I've also seen the cross site scripting protection could be causing the issue. When I login it does authenticate fine, but i actually get a new HTML page with my JSON on it but the content type is html/text.

enter image description here

When the login is unsuccessful I get the same thing.

enter image description here

The Request and Response Headers do not look right to me. First there are no response headers, and secondly the request headers does not have X-Requested-With.

enter image description here

Some items to note

  1. I am using Spring Boot 1.2.5
  2. I have CSRF enabled in Spring Security, it is a requirement from client
  3. There cannot be a login page, the login must be in the top right of the top navigation / header, hence why I am submitted the from via JS/Ajax

This is what the login form will look like:

enter image description here

Here are my custom handlers

@Component
public class AuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Autowired
    private UserMapper userMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {

        userMapper.incrementFailedLogin(request.getParameter("sec-user"));

        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().print("{\"success\": false}");
        response.getWriter().flush();
    }
}

-

@Component
public class AuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Autowired
    private UserMapper userMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws ServletException, IOException {

        userMapper.updateUserOnAuthSuccess(request.getParameter("sec-user"));

        response.setStatus(HttpServletResponse.SC_OK);
        response.getWriter().print("{\"success\": true}");
        response.getWriter().flush();
    }
}

My Spring Security config

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    AuthFailureHandler authFailureHandler;

    @Autowired
    AuthSuccessHandler authSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .antMatchers("/", "/about","/public/**").permitAll()
                    .anyRequest().fullyAuthenticated()
                    .and()
                .formLogin()
                    .usernameParameter("sec-user")
                    .passwordParameter("sec-password")
                    .failureHandler(authFailureHandler)
                    .successHandler(authSuccessHandler)
                    .permitAll()
                    .and()
                .logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    .logoutSuccessUrl("/")
                    .deleteCookies("remember-me", "JSESSIONID")
                    .permitAll()
                    .and()
                .rememberMe();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }
}

Login Form (Thymeleaf)- embedded in navigation menu

<div th:fragment="login">
        <form th:action="@{/login}" method="post" accept-charset="UTF-8" class="login-pane" id="login-form">
            <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
            <div class="form-group">
                <label for="sec-user">Email</label>
                <input type="email" class="form-control" id="sec-user" name="sec-user" placeholder="Email" />
            </div>
            <div class="form-group">
                <label for="sec-password">Password</label>
                <input type="password" class="form-control" id="sec-password" name="sec-password" placeholder="Password" />
            </div>
            <button type="login-btn" class="btn btn-danger">Login</button>
        </form>
    </div>

And last but not least my javascript code

$(document).ready(function() {

    $("#login-btn").click(login)

});

function login() {

    console.info("Attempting to authenticate");

    $.ajax({
        type: 'POST',
        url: '/login',
        data: $('#login-form').serialize(),
        cache: false,
        dataType: "json",
        crossDomain: false,
        success: function (data) {
            var response = jQuery.parseJSON(data);
            if (response.success == true) {
                console.info("Authentication Success!");
                window.location.href("/");
            }
            else {
                console.error("Unable to login");
            }
        },
        error: function (data) {
            console.error("Login failure");
        }
    });
}

Aucun commentaire:

Enregistrer un commentaire