Announcement Announcement Module
Collapse
No announcement yet.
POST called in controller instead of GET! Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • POST called in controller instead of GET!

    I've just noticed a bug in a fairly simple system I built using Spring security/Spring MVC. Here's a bit of background about what the system does:

    There are 2 pages, the logon page, and the main page. The main page has a form which the user fills in, and once submitted, the user is shown a "result" on the same page. Once the user has submitted the form 5 times or more, they are shown an error on the main page, and they won't be shown the "result" any more. They would have to log out then log in again.

    I have a session timeout set up in web.xml (1 minute for testing):

    <session-config>,
    <session-timeout>1</session-timeout>,
    </session-config>

    So far the system is ALMOST working fine. If I log on, wait for over a minute for their session to timeout, then submit the form, spring security sends the user back to the entry point (the logon page). This is as expected. However, when I log on again, the POST method of my controller is being called, not the GET method. It's as if Spring remembers that the GET has already been called. This causes a NullPointerException to be thrown in the POST method, at the following line:

    int transformationAttemptNo=((Integer)session.getAttri bute("transformationAttemptNo")).intValue() + 1;

    This is because the session attribute "transformationAttemptNo" no longer exists because of the timeout.

    Is this a common problem: Spring thinking that it should invoke the POST method on a controller instead of GET, when the session has timed out?

    The controller for the main page uses annotations:

    @Controller
    @RequestMapping("/secure/transform.htm")
    public class TransformController {
    .
    .
    .
    @RequestMapping(method = RequestMethod.GET)
    public String setupForm(HttpServletRequest request,ModelMap model) {
    //get the session object
    HttpSession session = request.getSession();

    if ((session.getAttribute("transformationAttemptNo") == null)) {
    //set up the number of transformation attempts as a session attribute
    session.setAttribute("transformationAttemptNo", new Integer(0));
    }
    .
    .
    .
    //create command object and store it into a model attribute
    model.addAttribute("transformForm",new TransformForm());

    return "transform";
    }

    @RequestMapping(method = RequestMethod.POST)
    public String submitTransformForm(HttpServletRequest request, @ModelAttribute("transformForm")TransformForm form, Errors errors) {
    //get the session object
    HttpSession session = request.getSession();
    .
    .
    .
    //add 1 to the number of times they have attempted a transformation
    int transformationAttemptNo=((Integer)session.getAttri bute("transformationAttemptNo")).intValue() + 1;
    session.setAttribute("transformationAttemptNo",tra nsformationAttemptNo);
    //if this is their 6th attempt, cancel the transformation process, and send back an error
    if(transformationAttemptNo>5){
    return "transform";
    }
    .
    .
    .
    //put the result in request scope
    request.setAttribute("result", result);

    return "transform";
    }

    }

  • #2
    I added the following line to both the GET and POST method of the controller:

    System.out.println(request.getMethod());

    Obviously, in the GET method of the controller, "GET" is printed, and in the POST method, "POST" is printed.

    In my speds-security-context.xml, I have a custom AuthenticationProcessingFilter:

    <bean id="myAuthenticationProcessingFilter"
    class="com.johnlewis.pde.speds.pres.logon.LogonAut henticationProcessingFilter">
    <property name="defaultTargetUrl" value="/secure/transform.htm" />
    <property name="alwaysUseDefaultTargetUrl" value="true"/>
    <property name="authenticationFailureUrl" value="/login.htm?authentication_error=true" />
    <property name="authenticationManager" ref="authenticationManagerAlias" />
    <property name="auditService" ref="auditService" />
    <property name="messageSource" ref="messageSource" />
    <property name="userAuthenticationService"
    ref="userAuthenticationService" />
    <security:custom-filter
    position="AUTHENTICATION_PROCESSING_FILTER" />
    </bean>

    If you logon after manually logging off, defaultTargetUrl seems to use GET. But, after a session timeout, if you logon again, defaultTargetUrl uses POST.

    If anyone finds an answer to this I'll pay your mortgage for the next year.

    Comment


    • #3
      try to use a redirect

      Hello,

      you could try to use a redirect for your defaultTargetUrl:
      "redirect:/secure/transform.htm"

      Comment


      • #4
        Thanks for the reply. Unfortunately, it doesn't look like you can do this, as I get the following error when the beans load up:

        Property 'defaultTargetUrl' threw exception; nested exception is java.lang.IllegalArgumentException: defaultTarget must start with '/' or with 'http(s)'

        Comment


        • #5
          i noticed you use a custom AuthenticationProcessingFilter

          Probably internally a forward is used.
          You could extend that class with your own implementation, and add a flag wether a forward or a redirect should be used

          Comment


          • #6
            The problem looks to be that the URL you are posting to is the same as the one that is your defaultTargetUrl. Spring Security caches the original request before redirecting to the login page. Unfortunately the new incoming request matches the cached one and a request wrapper reproducing the original request is substituted in the chain, with the original HTTP method.

            We hope to provide more flexible support for "saved request" handling strategies in version 3.0, but for the moment the best way to get round this would be to map a different URL path to your defaultTargetUrl.

            Comment


            • #7
              Thanks for your reply Luke.

              I made the following change to speds-security-context.xml:

              <property name="defaultTargetUrl" value="/secure/enter_transform.htm" />

              and the following change to TransformController:

              @RequestMapping(value={"/secure/transform.htm","/secure/enter_transform.htm"})

              I'm still getting the same problem, only now when I post the form it just POSTs to /secure/enter_transform.htm instead.

              The requirement of the system is to have a logon page, and then go straight to the page with the form on it (so no intermediate page between the two).

              Is this going to cause a problem?

              Thanks

              Comment


              • #8
                I tried the following, which made no difference:

                1)

                <property name="defaultTargetUrl" value="/secure/enter_transform.htm" />

                @Controller
                public class ForwardingController {
                @RequestMapping("/secure/enter_transform.htm")
                public String forwardingMethod(){
                return "enter_transform";
                }
                }

                And I created "enter_transform.jsp":

                <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

                <a href="<c:url value="/secure/transform.htm"/>">Click here</a>

                And the following set in TransformController:

                @Controller
                @RequestMapping("/secure/transform.htm")
                public class TransformController {.....}

                2) I also tried the following which, yet again, made no difference:

                <property name="defaultTargetUrl" value="/secure/enter_transform.htm" />

                @Controller
                public class ForwardingController {
                @RequestMapping("/secure/enter_transform.htm")
                public String forwardingMethod(){
                return "redirect:transform.htm";
                }
                }

                I don't really have a clue what to do now to be honest.

                Comment

                Working...
                X