CSC/ECE 517 Fall 2009/wiki2 3 pp

From Expertiza_Wiki
Jump to navigation Jump to search

Synchronizer Token Pattern - Introduction

Double Submit Problem

What is the requirement of having this pattern. Some examples such as Add to cart being pressed twice can result in 2 items in the cart etc. The example should provide or demonstrate how devastating this can be. Some financial example such as clicking on Pay button twice could charge credit card twice etc.

Solutions for Double Submit Problem

Can be server side or client side. Server Vs Client which one is better?

Client Side Solution - Javascript Control Disabling

Briefly describe the approach. Discuss pros and cons.

POST-REDIRECT-GET

Briefly describe the approach. Discuss pros and cons.

Synchronizer Token Pattern

How the patten works? Diagrammatic Representation? Theoretical View? Reply on session.


Using POST-REDIRECT-GET avoids accidental double submits of a single request but does not help prevent a user from completing the same business process twice. Such a business process is typically composed of multiple pages spanning several requests. Synchronizer token pattern adds additional safety on top of the POST-REDIRECT-GET idiom by preventing a possibly intentional resubmit of a page. Both the techniques should typically be combined to deliver a complete solution.

Implementations of the pattern provided by various frameworks

The basic idea of Synchronizer Token is to set a token in a session variable before returning a transactional page to the client. This page carries the token inside a hidden field. Upon submission, request processing first tests for the presence of a valid token in the request parameter by comparing it with the one registered in the session. If the token is valid, processing can continue normally, otherwise an alternate course of action is taken. After testing, the token resets to null to prevent subsequent submissions until a new token is saved in the session, which must be done at the appropriate time based on the desired application flow of control. Many web based frameworks provide built-in support for this. However some of the frameworks require serious developer attention whereas some frameworks do provide configurable automatic support. This section will describe how the built in support for synchronizer token is provided by various frameworks.

Struts

Apache Struts provides built in mechanism for handling tokens in org.apache.struts.action.Action class using the saveToken() and isTokenValid() methods. saveToken() method creates a token (a unique string) and saves that in the user's current session, while isTokenValid() checks if the token stored in the user's current session is the same as that was passed as the request parameter.

To do this the JSP has to be loaded through an Action. Before loading the JSP call saveToken() to save the token in the user session. When the form is submitted, check the token against that in the session by calling isTokenValid(), as shown in the following code snippet:

class PurchaseOrderAction extends DispatchAction { 
      public ActionForward load(ActionMapping mapping,
                                ActionForm form,
                                HttpServletRequest request,
                                HttpServletResponse response) throws Exception {
         try {
             //save the token
             saveToken(request)
             // rest of the code for loading the form
         } catch(Exception ex){
             //exception
         }
      }

      public ActionForward submitOrder(ActionMapping mapping,
                                       ActionForm form,
                                       HttpServletRequest request,
                                       HttpServletResponse response) throws Exception {
          try {
              // check the token. Proceed only if token is valid
              if(isTokenValid(request,true)) {
                  //implement order submit functionality here
              } else {
                  return mapping.findForward("failure");
              }
          } catch(Exception ex){
              //exception
          }
     }
}

This actually what is happening behind the scene in the Action class. saveToken() has logic as below:

HttpSession session = request.getSession();
String token = generateToken(request);
if (token != null) {
   session.setAttribute(Globals.TRANSACTION_TOKEN_KEY, token);
}

The method generates a random token using session id, current time and a MessageDigest and stores it in the session using a key name org.apache.struts.action.TOKEN (This is the value of the static variable TRANSACTION_TOKEN_KEY in org.apache.struts.Globals class).

The Action class that renders the form (PurchaseOrderAction.load) invokes the saveToken() method to create a session attribute with the above name. In the JSP, the token needs to be as a hidden form field as follows:

<input type="hidden" name="<%=org.apache.struts.taglib.html.Constants.TOKEN_KEY%>" 
value="<bean:write name="<%=Globals.TRANSACTION_TOKEN_KEY%>"/>">

The embedded <bean:write> tag shown above, looks for a bean named org.apache.struts.action.TOKEN (which is the value of Globals.TRANSACTION_TOKEN_KEY) in session scope and renders its value as the value attribute of the hidden input variable. The name of the hidden input variable is org.apache.struts.taglib.html.TOKEN (This is nothing but the value of the static variable TOKEN_KEY in the class org.apache.struts.taglib.html.Constants).

When the client submits the form, the hidden field is also submitted. In the Action that handles the form submission i.e. PurchaseOrderAction.submitOrder (which most likely is different from the Action that rendered the form), the token in the form submission is compared with the token in the session by using the isTokenValid() method. The method compares the two tokens and returns a true if both are same. Be sure to pass reset=”true” in the isTokenValid() method to clear the token from session after comparison. If the two tokens are equal, the form was submitted for the first time. However, if the two tokens do not match or if there is no token in the session, then it is a duplicate submission and handle it in the manner acceptable to your users.

If the form is spanned across the multiple pages, then every time the form is submitted before going from one page to another. You definitely want to validate token on every page submission. However you also want to allow the user to traverse back and forth using the browser back button until the point of final submission. If the token is reset on every page submission, the possibility of back and forth traversal using the browser button is ruled out. The solution is not disabling back button (using JavaScript hacks) but to handle the token intelligently. This is where the reset parameter is useful. The token is initially set before showing the first page of the form. The reset parameter is false for all the boolean) isTokenValid() invocations except in the Action for the last page. The last page uses a true value for the reset argument and hence the token is reset in the isTokenValid() method. From this point onwards you cannot use back button to traverse to the earlier form pages and successfully submit the form.

Although the above approach is good, it requires application developer to add the token checking method pair – saveToken() and isTokenValid() in methods rendering and submitting the sensitive forms respectively. Since the two tasks are generally performed by two different Actions, the pairs need to be identified and added manually. Next framework provides automatic support for generating and checking token thus developer do not have to worry about forming the pairs.

Spring Web Flow

Spring Web Flow was specifically designed to help developers implement complex conversations in web applications. As a controller component in the MVC triad, Spring Web Flow integrates into hosting web MVC frameworks, serving as an application controller, handling screen navigation, and coordinating the flow of a business process.

Spring Web Flow captures business processes or conversations in modules called flows. A flow is a blueprint for the interaction a user can have with a web application; it reacts to user events to drive the process to completion. You can look at a flow as a simple manifestation of a finite state machine (FSM), consisting of a number of states that define the activities to execute while progressing through the flow. A state can allow a user to participate in the flow, or it can call business services. The flow can move from one state to another using transitions triggered by events. As a common practice business processes are defined using UML state diagrams, and Spring Web Flow flow definitions use a similar model. The following screenshot shows a Spring Web Flow flow definition that mirrors the process definition in Figure:

Spring Web Flow flow definition
Figure 1 Spring Web Flow flow definition.

Given the navigational rules set out in a flow definition, Spring Web Flow automatically takes care of navigational control. Using web continuations, Spring Web Flow can guarantee stable, predictable behavior of a web application even when the user uses the browser’s Back, Forward, or Refresh buttons; revisits bookmarked pages; or opens multiple windows in the same conversation. The POST-REDIRECT-GET idiom will also be automatically applied, without any need for developer intervention.

Typically, a web flow will define a process spanning multiple requests into the web application. While completing the process, the user interacts with the application through several different pages, accumulating data along the way. It is the responsibility of a flow execution repository to maintain all data associated with a flow execution in between separate requests participating in that flow execution.

FlowExecutionRepository
Figure 2 FlowExecutionRepository Interface.

Spring Web Flow provides multiple types of repository implementations where the control flow behavior is predefined. Depending on the requirements developer can configure to use a particular implementation of a repository. All of these implementations implements FlowExecutionRepository interface.


  • Line 5: To adequately manage flow executions, a flow execution repository needs to assign a unique key to each flow execution. Using generateKey(flowExecution), a new FlowExecutionKey will be generated for a freshly launched flow execution. When an existing flow execution, with a key already assigned, needs to be persisted again, getNextKey(flowExecution, previousKey) generates the next key to use. This implies that a new key can be obtained every time a flow execution needs to be stored in a repository, allowing the repository to potentially change the key every time. FlowExecutionKey objects can be marshaled into a string form using their toString() method. This string form will be embedded in HTML pages and later travels back to the server using the _flowExecutionKey request parameter. Using parseFlowExecutionKey(encodedKey), you can unmarshal a string form of a flow execution key back into its object form.


  • Line 12: To make sure all access to a flow execution object occurs in an orderly fashion, a flow execution repository provides a FlowExecutionLock. A flow execution needs to be locked before it is manipulated and unlocked afterward to ensure that all processing done by a flow execution is serialized: the next request is only processed when the previous one completed processing.


  • Line 15: Finally, the FlowExecutionRepository interface provides methods to store FlowExecution objects in the repository (putFlowExecution(key, flowExecution)), obtain them from the repository getFlowExecution(key)), or remove them from the repository (removeFlowExecution(key)). Before using any of these methods, the flow execution needs to be locked in the repository.



Figure 3 represents all the built in repository implementations provided by Spring Web Flows. An appropriate implementation can be chosen by using:

       <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="simple"/>

In above example simple repository will be used. When using classic Spring bean definitions instead of the Spring Web Flow configuration schema, set the repositoryType property to value SIMPLE:

       <bean id="flowExecutor" class="org.springframework.webflow.config.FlowExecutorFactoryBean">
           <property name="definitionLocator" ref="flowRegistry"/>
           <property name="repositoryType" value="SIMPLE"/>
       </bean>
FlowExecutionRepository
Figure 3 FlowExecutionRepository Class Diagram.
  • Simple Repository

The simplest FlowExecutionRepository implementation shipped with Spring Web Flow is, not surprisingly, called SimpleFlowExecutionRepository. It manages a single snapshot of an ongoing flow execution, guarding access to the FlowExecution object using the flow execution key. Figure illustrates this algorithm. This results in a situation very similar to the use of a synchronizer token. The simple repository guards access to the flow execution by changing the flow execution key for every request. In other words, the getNextKey(flowExecution, previousKey) method always returns a new key, different from the previous key. This ensures that a stale request, resulting from the user using the browser navigational aides (for instance, the Back button), can no longer access the flow execution. The end result is that use of the Back button or the browser history is completely disallowed. A user who tries to go back will receive an error stating that doing so is not allowed. The same is true, of course, when the conversation ends (because the flow reached an end state). At that point, Spring Web Flow will clean up all conversational state, preventing a user from resuming the terminated conversation or causing a double submits.
Pros and Cons - The most important benefit of using the simple repository is that it only maintains a single copy of the FlowExecution data. These results in very low memory requirements, making this repository type ideal for environments where memory resources are scarce or load requirements are very high. As a downside, the simple repository does not support use of the browser Back button or navigation history and generates an exception if Back button usage is detected. This kind of strict no-Back-button policy enforcement is typically not suited for Internet facing applications. It can be ideal, however, in intranet settings, where you might be able to deploy a custom browser that can completely disable the browser navigational aids.

  • Single Key Repository
    • Pros
    • Cons

Ruby on the Rails

Grails

References

[1] References go here