Spring Security (3.2.5) HTTP POST not forwarding to the original request after authentication

扶醉桌前 提交于 2020-02-03 18:57:11

问题


I have a sample Spring MVC application, secured by Spring security (Spring version 4.0.1.RELEASE, Spring security 3.2.5.RELEASE. When I send an HTTP GET request as an unauthenticated user, I am sent to the login page (as expected) where after I authenticate I am sent along to the page that was requested in the original GET.

When I send an HTTP POST request as an unauthenticated user, I am sent to the login page (as expected), but then after successful authentication I am sent to the page as specified in my "default-target-url" instead of the page as requested in my original POST request.

When I try this same HTTP POST as an authenticated user, it works just fine (as expected). I have already tried setting always-use-default-target="false" as well as omitting that property altogether and the behavior is the same.

Am I missing something? Should Spring pass along the POST request after authentication or does that not happen by design for some reason?

Here is my Spring security config:

    <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>

Here is my jsp to initiate the test (a link to test the GET and a form to test the 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>

Here is the landing page which is a secured resource:

<%@ 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>

and here is my test 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;

    }
}

I can include more of the Spring configuration if it's relevant, but where the app is working fine with a GET but misbehaving (in my opinion) with a POST, I'm thinking it's isolated to the pieces I've shown here.

It seems to me that Spring security should be able to intercept a POST and pass along the POST after authentication, just like a GET.

Any hints or help would be appreciated. Thanks, Rob


回答1:


As pointed out, when CSRF is enabled Spring Security will only save GET requests. The reason is that the CSRF token is changed as soon as a user authenticates to prevent a malicious user from finding out the CSRF before a user authenticates (i.e. in a public setting). If we cached the request, then it would be replayed with the old CSRF and fail CSRF validation anyways.

In general, saving a POST request and automatically processing it seems to be a bit dangerous. Consider a situation where a public computer contains access to a commonly used site. A malicious user performs a POST to the unauthenticated application that transfers funds from the currently authenticated user to their bank account. The application caches the POST and then brings up the log in form. The malicious user walks away and a victim sees that the log in page is already present. Furthermore the URL in the browser is correctly displayed and over HTTPS. The victim logs in and the originally requested POST request is replayed and automatically transfers funds from the victim to the malicious user.

Instead, we should probably be ensuring that an intermediate page is displayed when a POST is going to be replayed.



来源:https://stackoverflow.com/questions/26763958/spring-security-3-2-5-http-post-not-forwarding-to-the-original-request-after-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!