如何使用Ext.Ajax登录Spring Security?

4

我正在使用Extjs-6和Spring 4开发一个应用程序。我的应用程序是Restful的。
我按照以下方式启用了CORS Origin:

public class CorsFilter extends OncePerRequestFilter {

    private static final String ORIGIN = "Origin";


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

        response.addHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.addHeader("Access-Control-Max-Age", "10");

        String reqHead = request.getHeader("Access-Control-Request-Headers");

        if (!StringUtils.isEmpty(reqHead)) {
            response.addHeader("Access-Control-Allow-Headers", reqHead);
        }

        if (request.getMethod().equals("OPTIONS")) {
            try {
                response.getWriter().print("OK");
                response.getWriter().flush();
            } catch (IOException e) {
            e.printStackTrace();
            }
        } else{
            filterChain.doFilter(request, response);
        }
    }
 }

过滤器配置:
<security:http use-expressions="true">
    ...
    <sec:custom-filter ref="CorsFilter" before="HEADERS_FILTER"/>
</security:http>

和豆子:

<bean id="CorsFilter" class="..." />

我希望用户能够使用AJAX请求进行登录。我使用高级REST客户端HTTP请求器测试AJAX请求。扩展程序的结果如下:

enter image description here

enter image description here

Ext Ajax请求的代码如下:

Ext.Ajax.request({
    url: "http://localhost/Calk/j_spring_security_check",
//      params: {
//          "j_username": "ali",
//          "j_password": "123456"
//      },
    params: "j_username=ali&j_password=123456",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    method: "POST",
    success: function(){...},
    failure: function(){...}
});

当我发送请求时,它会收到“200 OK”响应,并在客户端初始化应用程序,发送一些请求以获取数据。但是,对于所有这些请求,服务器都会收到“401 Unauthorized”响应。
问题出在哪里?
重要更新:
登录请求如下:

enter image description here

服务器在响应中设置cookie,获取授权数据的请求如下::

enter image description here

为什么


你尝试过使用 params: form.getValues() 吗? - qmateub
是的,结果与上面一样。 - Morteza Malvandi
请阅读链接的论坛帖子直到结束。你不可能在大约2分钟内完成它。 - Alexander
会话 cookie 受限于 /Calk/ 路径,你的带有 ajax 的页面是否位于此路径下?检查你的 ajax 调用,查看第一次响应头中设置了哪个 cookie,以及它是否在第二个请求头中被发送回来。 - serg
我的页面和我的应用程序位于不同的路径上。页面:localhost/Calk,应用程序:localhost:8084/Calk - Morteza Malvandi
显示剩余2条评论
3个回答

2

您出现错误的原因是您正在进行跨域请求。我认为您的javascript应用程序在不同的端口运行,而您的应用程序服务器在8084上运行。

要使其正常工作,您需要为您的应用程序启用CORS支持。以下是如何实现的方法。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }
}

点击这个链接查看详细的解释。大部分情况下,这个方法应该能够解决问题。如果spring-security仍然报错,你可能需要为spring security添加过滤器。你可以参照这个答案进行操作。

public class CorsFilter extends OncePerRequestFilter {

    private static final String ORIGIN = "Origin";


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

        if (request.getHeader(ORIGIN) == null || request.getHeader(ORIGIN).equals("null")) {
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.addHeader("Access-Control-Max-Age", "10");

            String reqHead = request.getHeader("Access-Control-Request-Headers");

            if (!StringUtils.isEmpty(reqHead)) {
                response.addHeader("Access-Control-Allow-Headers", reqHead);
            }
        }
        if (request.getMethod().equals("OPTIONS")) {
            try {
                response.getWriter().print("OK");
                response.getWriter().flush();
            } catch (IOException e) {
            e.printStackTrace();
            }
        } else{
            filterChain.doFilter(request, response);
        }
    }
 }

为安全性配置过滤器

<security:http use-expressions="true" .... >
    ...
    //your other configs
    <sec:custom-filter ref="corsFilter" before="HEADERS_FILTER"/>
</security:http>

将过滤器暴露为Bean
<bean id="corsFilter" class="<<location of the CORS filter class>>" />

我有一些商店,它们都从同一个Web服务器获取数据。我设置了CORS配置,并且可以正确地获取所有数据。但是只有登录请求无法正常工作。 - Morteza Malvandi
然后尝试使用安全过滤器,更新答案。 - JChap
我根据您的回复更新了我的代码。当我发送登录Ajax请求时,服务器会收到“200 OK”(当我发送不正确的用户名/密码时,它会收到“401未授权”)。当用户成功登录后,页面使用ajax获取一些数据,但在下一个请求中,服务器会收到“401未授权”。 - Morteza Malvandi
嗯,可能是因为它正在创建一个新的会话。你能确认一下吗?登录请求和发送数据请求时的会话是否相同? - JChap
我不知道。但是当我使用HTTP请求应用程序时,它可以正常工作。 - Morteza Malvandi

1
Spring Security 的工作方式如下:
如果来自客户端的任何请求没有 JSessionID cookie,Spring 将会重置该客户端的会话并为其设置一个无效的会话。然后下一个请求将使用该无效的会话。
因此,您需要在所有的 Ext ajax 请求中设置 cookie。我猜一些并发的 ajax 请求不发送 cookie。为了在所有的 ajax 请求中设置 cookie,您可以添加此事件:
Ext.Ajax.on("beforerequest",function(con){
  con.setUseDefaultXhrHeader(false);
  con.setWithCredentials(true);
}

这个函数在发送所有ajax请求之前运行。


0

我认为问题在于会话cookie被设置为/Calk/路径,而ajax调用是从/Workspace/Calk/进行的。因此,您需要找到Spring中配置会话cookie路径的位置,并将其更改为/

我没有使用过Spring安全性,但从快速搜索thisthis的问题来看,您可以在web.xml中更改路径:

<web-app>
    <session-config>
        <cookie-config>
            <path>/</path>
        </cookie-config>
    </session-config>
</web-app>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接