【问题标题】:Spring Security (3.2.5) HTTP POST not forwarding to the original request after authenticationSpring Security(3.2.5)HTTP POST认证后不转发到原始请求
【发布时间】:2015-01-02 01:03:14
【问题描述】:

我有一个示例 Spring MVC 应用程序,由 Spring security(Spring 版本 4.0.1.RELEASE,Spring security 3.2.5.RELEASE)保护。当我作为未经身份验证的用户发送 HTTP GET 请求时,我被发送到登录页面(如预期),在我进行身份验证后,我被发送到原始 GET 中请求的页面。

当我以未经身份验证的用户身份发送 HTTP POST 请求时,我会被发送到登录页面(如预期的那样),但在成功验证后,我会被发送到我的“default-target-url”中指定的页面在我的原始 POST 请求中请求的页面。

当我以经过身份验证的用户身份尝试相同的 HTTP POST 时,它工作得很好(如预期的那样)。我已经尝试设置 always-use-default-target="false" 以及完全省略该属性,并且行为是相同的。

我错过了什么吗? Spring 是否应该在身份验证后传递 POST 请求,还是出于某种原因而设计不这样做?

这是我的 Spring 安全配置:

    <beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-3.2.xsd">

    <http auto-config="true">
        <intercept-url pattern="/admin/**" access="ROLE_USER" />
        <form-login 
            login-page="/login.htm" 
            default-target-url="/hello.htm" 
            always-use-default-target="false"
            authentication-failure-url="/login.htm?error" 
            username-parameter="username"
            password-parameter="password" />
        <logout logout-success-url="/login.htm?logout" />
        <!-- enable csrf protection -->
        <csrf/>
    </http>

    <authentication-manager>
      <authentication-provider>
        <user-service>
        <user name="admin" password="password" authorities="ROLE_USER" />
        <user name="super" password="man" authorities="ROLE_SUPER_USER" />
        </user-service>
      </authentication-provider>
    </authentication-manager>

</beans:beans>

这是我启动测试的 jsp(测试 GET 的链接和测试 POST 的表单):

<%@ include file="/WEB-INF/jsp/include.jsp" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>
  <head><title>TEST SECURITY</title></head>
  <body>
        <p><a href="admin/security_landing.htm">GET</a></p>
        <form:form  method="POST" action="admin/security_landing.htm"><input type="submit" value="POST"></form:form>

  </body>
</html>

这是一个安全资源的登录页面:

<%@ include file="/WEB-INF/jsp/include.jsp" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>
  <head><title>TEST SECURITY LANDING PAGE</title></head>
  <body>
        <p>YOU MADE IT!!!!</p>
  </body>
</html>

这是我的测试控制器:

package springapp.web;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


@Controller
public class TestController {

    /** Logger for this class and subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    @RequestMapping(value="test", method= RequestMethod.GET)
    public ModelAndView methodGet()
            {

        logger.info("Found it's way to the GET method");

        ModelAndView model = new ModelAndView();

        model.setViewName("security_test");

        return model;

    }

    @RequestMapping(value="/admin/security_landing", method=RequestMethod.POST)
    public ModelAndView sendToLandingPOST()
            {

        logger.info("Found it's way to the GET method");

        ModelAndView model = new ModelAndView();

        model.setViewName("/admin/security_landing");

        return model;

    }

    @RequestMapping(value="/admin/security_landing", method= RequestMethod.GET)
    public ModelAndView sendToLandingGET()
            {

        logger.info("Found it's way to the GET method");

        ModelAndView model = new ModelAndView();

        model.setViewName("/admin/security_landing");

        return model;

    }
}

如果相关,我可以包含更多 Spring 配置,但是如果应用程序在 GET 上运行良好但在 POST 上表现不佳(在我看来),我认为它与我在这里展示的部分隔离.

在我看来,Spring 安全性应该能够拦截 POST 并在身份验证后传递 POST,就像 GET 一样。

任何提示或帮助将不胜感激。 谢谢, 抢

【问题讨论】:

  • 通过实验,我已将问题与启用的 CSRF 保护隔离开来。当安全配置中存在 标记时,会出现此问题。当它被删除时,应用程序按预期工作。不知道为什么,但至少我已经找出了原因。
  • 如果您使用 CSRF 检查,那么您需要在表单提交中提交 CSRF 令牌。 the manual中有一个例子。
  • 谢谢 Luke,我实际上是在使用带有 form:form 标签的 Spring 表单库,它应该自动处理 CSRF 标签,但我也尝试过明确地包含令牌。没有运气。我在文档中发现了这一点,这让我相信这种行为可能是设计使然。
  • 2.1.12。 此元素将向应用程序添加跨站点请求伪造 (CSRF) 保护。它还将默认的 RequestCache 更新为仅在成功验证后重放“GET”请求。更多信息可以在参考的跨站请求伪造 (CSRF) 部分找到。

标签: spring http spring-mvc post spring-security


【解决方案1】:

正如所指出的,当 CSRF 被启用时,Spring Security 只会保存 GET 请求。原因是 CSRF 令牌在用户验证后立即更改,以防止恶意用户在用户验证之前发现 CSRF(即在公共设置中)。如果我们缓存了请求,那么它将使用旧的 CSRF 重放,并且无论如何都无法通过 CSRF 验证。

一般来说,保存一个 POST 请求并自动处理它似乎有点危险。考虑公共计算机包含对常用站点的访问权限的情况。恶意用户对未经身份验证的应用程序执行 POST,将资金从当前经过身份验证的用户转移到他们的银行账户。应用程序缓存 POST,然后显示登录表单。恶意用户走开,受害者看到登录页面已经存在。此外,浏览器中的 URL 可以通过 HTTPS 正确显示。受害者登录并重放最初请求的 POST 请求,并自动将资金从受害者转移给恶意用户。

相反,我们可能应该确保在重放 POST 时显示中间页面。

【讨论】:

    猜你喜欢
    • 2011-11-30
    • 2012-03-13
    • 2015-05-01
    • 2015-09-14
    • 2018-03-11
    • 1970-01-01
    • 2016-02-17
    • 2016-01-08
    相关资源
    最近更新 更多