Spring 安全 /j_spring_security_check 无 JSP

Spring Security /j_spring_security_check without JSP

我需要干净的 URL 而不是附加的扩展名。我在 UI 中使用 Backbone.js 框架。我构建了一个视图,该视图收集用户名和密码并向 /j_spring_security_check 执行 restful 请求。

由于某种原因,/j_spring_security_check 端点看不到用户名和密码。

这是我在 backbone 中执行的请求:

loginModel.save( loginModel.toJSON(), {
    success: _.bind(function(response) {
        Backbone.history.navigate('', {trigger: true});
    }, this),
    error: function() {
        console.log('test');
    }
});

我尝试使用 j_username 和 j_password 作为模型的属性发出请求,并将 url 设置为 /j_spring_security_check

我也试过直接在 url 中传递参数:

/jspring_security_check?j_username=X&j_password=Z

两种方式都进入我的身份验证提供程序身份验证方法,但 CredentialsPrincipal 都是空白字符串,因此身份验证失败。

当我发出请求时,我可以在浏览器的开发人员工具中看到请求负载被设置下来:

{j_username: "a", j_password: "a"} j_password: "a" j_username: "a"

在不使用 JSP 表单的情况下向 /j_spring_security_check 发出请求的正确方法是什么?

让我们看看 Spring 的 class 执行身份验证 /org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.java

private boolean postOnly = true;

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    if (postOnly && !request.getMethod().equals("POST")) {
        throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
    }

您正在发送带有 /jspring_security_check?j_username=X&j_password=Z 的 GET 请求,而它需要 POST 请求。

请求必须是具有内容类型 'application/x-www-form-urlencoded' 和两个参数的 POST 请求。请求的主体不应是 JSON 对象。

我建议重新设计您的登录名,不要使用 Ajax。

j_spring_security_check 并非旨在通过 Ajax 从登录页面调用。它旨在接受来自 HTML 表单的 POST 请求,然后在登录表单提交的 HTTP 响应中呈现应用程序主页。

j_spring_security_check 所做的是对用户进行身份验证,如果身份验证成功,它会创建一个会话并将用户重定向到主屏幕,否则会将用户重定向到 "login failed" 屏幕。 j_spring_security_check returns 302 HTTP 代码是重定向。

RESTful 登录是可能的,但您必须配置 j_spring_security_check 以不使用成功(或失败)登录视图来响应,而是使用 JSON,因此您将拥有在 Java 端编写自定义登录处理程序。我这样做过一次,这让代码(Java 和 ExtJS)变得如此复杂,以至于我又回到了基于表单的登录。毕竟,Facebook 和 Google 使用表单提交登录,这很正常。在我的例子中,切换到 Ajax REST 登录是不值得的。

表单提交的另一个优点是 Web 浏览器可以记住登录名和密码,这是基于 Ajax 的登录不可能的,我认为这就是为什么 Google 和Facebook 使用表单提交。

但是如果您决定仍然需要基于 Ajax 的登录(我强烈反对这样做,因为实施起来很复杂,而且浏览器的密码管理器无法正常工作不会影响用户体验),那么您将不得不创建两个 Spring MVC 控制器,一个用于成功登录 (loginSucessController),另一个用于登录失败 (loginFailureController),两个控制器都应该 return 具有登录状态的统一 JSON 响应。然后配置Spring安全使用上面的两个控制器

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:sec="http://www.springframework.org/schema/security"
       xsi:schemaLocation=
               "http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/security  http://www.springframework.org/schema/security/spring-security.xsd
                http://www.springframework.org/schema/context   http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<sec:http>
            <sec:intercept-url pattern="/**"/>
            <sec:http-basic/>
            <sec:form-login default-target-url="/loginSucessController" authentication-failure-url="/loginFailureController"/>
            <sec:logout/>
    </sec:http>
</beans>

当然,您必须为 loginSucessController 和 loginFailureController 创建映射和实现,我跳过了那部分。

loginSucessController 应该 return

{ "loginStatus": 1 }

并且 loginFailureController 应该 return

{ "loginStatus": 0 }

您还必须创建第三个 MVC 控制器来检测用户是否已经登录,以决定当用户 return 返回应用程序网页时是否必须显示登录表单,使用 org.springframework.security.core.context.SecurityContextHolder#getContext