我有一个示例Spring MVC应用程序,由Spring安全保护(Spring版本4.0.1.RELEASE,Spring安全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中正常工作但在我看来行为不当(在我看来),我认为它与我在这里显示的部分是分开的
在我看来,Spring安全应该能够拦截POST并在身份验证后传递POST,就像GET一样。
任何提示或帮助将不胜感激。 谢谢, 罗布
答案 0 :(得分:2)
正如所指出的,当启用CSRF时,Spring Security将仅保存GET请求。原因是一旦用户进行认证就改变CSRF令牌以防止恶意用户在用户认证之前(即在公共设置中)找到CSRF。如果我们缓存了请求,那么它将使用旧的CSRF重放,并且无论如何都会失败CSRF验证。
通常,保存POST请求并自动处理它似乎有点危险。考虑一种公共计算机包含对常用站点的访问的情况。恶意用户对未经身份验证的应用程序执行POST,该应用程序将资金从当前经过身份验证的用户转移到其银行帐户。应用程序缓存POST,然后调出登录表单。恶意用户走开,受害者看到登录页面已经存在。此外,浏览器中的URL通过HTTPS正确显示。受害者登录并重播最初请求的POST请求,并自动将资金从受害者转移给恶意用户。
相反,我们应该确保在重播POST时显示中间页面。