Spring Security (3.2.5) HTTP POST 认证后不转发到原始请求

标签 spring http spring-mvc post spring-security

我有一个示例 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>

这是我的测试 Controller :

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 时,Spring Security 将只保存 GET 请求。原因是 CSRF token 会在用户进行身份验证后立即更改,以防止恶意用户在用户身份验证之前发现 CSRF(即在公共(public)环境中)。如果我们缓存了请求,那么它将使用旧的 CSRF 重放并且无论如何都无法通过 CSRF 验证。

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

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

关于Spring Security (3.2.5) HTTP POST 认证后不转发到原始请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26763958/

相关文章:

java - Spring 启动 : Cannot determine embedded database driver class for database type NONE

spring - 使用@Configurable域对象属性根据数据分配特定行为

java - MySQL 数据库在 8 小时后断开连接。如何预防?

java.lang.NoClassDefFoundError : kotlin/jvm/internal/Intrinsics in Spring Boot App

java - Spring 安全奇怪的行为

java - 无法在 hibernate 状态下保留哈希集

java - jboss中连接mysql出现异常

php - 需要页面等待 php 完成

web-services - HTTP 406 和 415 错误代码

.net - 如何在 .NET 中录制在线广播?