Spring Security AJAX / JSON登录问题

时间:2015-07-31 21:49:59

标签: jquery ajax spring spring-security

我正在尝试使用通过Ajax提交的登录表单来实现Spring Security。我创建了自己的自定义authenticationFailureHandler和我自己的自定义authenticationSuccessHandler。我不确定我的问题是我的Spring / Java代码还是我的客户端代码。我也看到跨站脚本保护可能导致问题。当我登录它确实很好,但我实际上得到一个新的HTML页面,其中包含我的JSON,但内容类型是html / text。

enter image description here

当登录失败时,我会得到同样的结果。

enter image description here

请求和响应标头对我来说不合适。首先没有响应头,其次请求头没有X-Requested-With。

需要注意的一些事项

  1. 我使用的是Spring Boot 1.2.5
  2. 我在Spring Security中启用了CSRF,这是客户端的要求
  3. 不能有登录页面,登录必须位于顶部导航/标题的右上角,因此为什么我通过JS / Ajax提交了
  4. 这是登录表单的样子:

    enter image description here

    以下是我的自定义处理程序

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

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

    登录表格(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>
    

    最后但并非最不重要的是我的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");
            }
        });
    }
    

1 个答案:

答案 0 :(得分:1)

您没有调用在javascript中定义但实际在登录表单中发布到 login 操作的 login()方法。

删除帖子操作调用,并在Login按钮的 onclick 事件中调用 login()方法。