简体   繁体   English

Spring Security AJAX / JSON登录问题

[英]Spring Security AJAX / JSON Login Issues

I am trying to implement Spring Security with the login form being submitted via Ajax. 我正在尝试通过Ajax提交的登录表单来实现Spring Security。 I have created my own custom authenticationFailureHandler and my own custom authenticationSuccessHandler. 我创建了自己的自定义authenticationFailureHandler和我的自定义authenticationSuccessHandler。 I'm not sure if my issue lies in my Spring / Java code or on my client side code. 我不确定我的问题是在我的Spring / Java代码中还是在我的客户端代码中。 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. 当我登录时,它确实可以进行身份​​验证,但是实际上我得到了带有JSON的新HTML页面,但是内容类型为html / text。

在此处输入图片说明

When the login is unsuccessful I get the same thing. 如果登录失败,我会得到相同的结果。

在此处输入图片说明

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. 首先,没有响应头,其次,请求头没有X-Requested-With。

Some items to note 注意事项

  1. I am using Spring Boot 1.2.5 我正在使用Spring Boot 1.2.5
  2. I have CSRF enabled in Spring Security, it is a requirement from client 我在Spring Security中启用了CSRF,这是客户端的要求
  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 没有登录页面,登录必须位于顶部导航/标题的右上角,因此为什么我通过JS / Ajax提交了

This is what the login form will look like: 登录表单如下所示:

在此处输入图片说明

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 我的Spring Security配置

@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 登录表单(Thymeleaf)-嵌入在导航菜单中

<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 最后但并非最不重要的是我的JavaScript代码

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

You are not calling login() method that you have defined in javascript but actually posting to login action in the login form. 您不是在调用您在javascript中定义的login()方法,而是实际上以登录表单发布到登录操作。

Remove the post action call and call your login() method on onclick event of Login button. 取出行动呼吁和打电话给你的login()方法在登录按钮的onclick事件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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