5.12. Double Submit Protection¶
Table of Contents
- Overview
- How to use
- Preventing double clicking of button using JavaScript
- Using PRG (Post-Redirect-Get) pattern
- Using transaction token check
- Transaction token check provided by common library
- Attributes of
@TransactionTokenCheck
annotation - Format of transaction token
- Lifecycle of transaction token
- Settings for using a transaction token check
- Settings for handling transaction token errors
- How to use transaction token check in Controller
- How to use transaction token check in View (JSP)
- When multiple usecases are to be implemented in one Controller
- Typical example of using transaction token check
- Exclusion control of parallel processing while using a session
- How to extend
- Appendix
5.12.1. Overview¶
5.12.1.1. Problems¶
If any of the operations given below is performed in a Web application with screens, it leads to same process being executed multiple times.
Sr. No. Operation Operation Overview (1) Double clicking of ‘Update’ button Repeatedly clicking the button to perform update process. (2) Reloading of screen after completing the update process Using the ‘Refresh’ button of the browser to reload the screen after completion of update process. (3) Invalid screen transition using ‘Back’ button of the browser Using ‘Back’ button of the browser to go back to the previous page from update process completion screen and clicking the button again to perform update process.
Each problem is described in detail below.
5.12.1.1.1. Double clicking of ‘Update’ button¶
Sr. No. Description (1) Buyer clicks ‘Order’ button on Product Purchase screen. (2) Buyer accidentally clicks ‘Order’ button again before receiving the response of (1). (3) Server updates DB based on purchase of the product received through request (1). (4) Server updates DB based on purchase of the product received through request (2). (5) Server sends response with a “Purchase Complete” screen for the product received through request (2).Warning
In the above case, since buyer accidentally clicks ‘Order’ button again, it results in duplicate purchase of same product. Although the problem can be attributed to erroneous operation by the buyer, it is desirable to have the application design such that the above problems do not occur.
5.12.1.1.2. Reloading of screen after completion of update process¶
Sr. No. Description (1) Buyer clicks ‘Order’ button on “Product Purchase” screen. (2) Server updates DB based on purchase of the product received through request (1). (3) Server sends response with a “Purchase complete” screen for the product received through request (1). (4) Buyer accidently executes Reload functionality of the browser. (5) Server updates DB based on the purchase of the product received through request (4). (6) Server sends response with a “Purchase Complete” screen for the product received through request (4).Warning
In the above case, since buyer accidentally executes Reload functionality of the browser, it results in duplicate purchase of same product. Although the problem can be attributed to erroneous operation by the buyer, it is desirable to have the application design such that the above problems do not occur.
5.12.1.1.3. Invalid screen transition using ‘Back’ button of the browser¶
Sr. No. Description (1) Buyer clicks ‘Order’ button on “Product Purchase” screen. (2) Server updates DB based on the purchase of the product received through request (1). (3) Server sends response with a “Purchase complete” screen of the product received through request (1). (4) Buyer uses ‘Back’ button of the browser to go back to “Product Purchase” screen. (5) Buyer again clicks ‘Order’ button on “Product Purchase” screen which is re-displayed by clicking ‘Back’ button of the browser. (6) Server updates DB based on the purchase of the product received through request (5). (7) Server sends response with a “Purchase Complete” screen for the product received through request (5).Note
In the above case, since buyer does not perform any erroneous operation, the problem is not attributed to the buyer.
However, if update process gets executed even after performing invalid screen operations, the following problems occur.
Warning
As described above, update process getting executed even after performing invalid screen operations increases the risk of direct updates by a malicious attacker bypassing a valid route.
Sr. No. Description (1) Attacker executes request for processing a direct product purchase without going through a valid screen transition. (2) Server cannot detect that the request is getting executed through an invalid route; hence it updates DB based on the purchase of the product received through that request.Execution of purchase process through an invalid request increases the load on each server resulting in inability to purchase products through a valid route. As a result, the problem causes a ripple effect for the users who purchase the products through valid routes. Hence, it is desirable to have the application design such that the above problems do not occur.
5.12.1.2. Solutions¶
Sr. No. Solution Overview (1) Preventing double clicking of a button using JavaScript When a button to perform update process is clicked, the button control using JavaScript prevents the submission of a request if the button is clicked again. (2) Applying PRG (Post-Redirect-Get) pattern A redirect command is returned as a response to the request for performing update process (request by POST method) and then a screen for transition is returned as a response of GET method which is automatically requested from a browser.When a PRG pattern is used, the request generated while reloading the page after the screen is displayed is a GET method; hence re-execution of update process can be prevented. (3) Applying transaction token check Issue a token value for each screen transition and compare the token value sent from browser with the token value stored on the Server to make sure that invalid screen operations do not occur in the transaction.Implementation of transaction token check can prevent re-execution of update process after the page is reloaded using ‘Back’ button of the browser.Deleting the token value stored on the Server after performing the token check can prevent double submission as a Server side process.Note
When only transaction token check is performed, even a simple operational mistake can lead to transaction token error which in turn results in a low-usability application for the user.
To ensure usability as well as to prevent the problems that occur due to double submission, measures such as “Preventing double clicking of a button using JavaScript” and “Applying PRG (Post-Redirect-Get) pattern” are necessary.
** Although this guideline recommends that you implement all the measures, the decision should be taken depending on application requirements.**
Warning
In Ajax and Web services, since it is difficult to transfer transaction tokens which change for each request, transaction token check need not be used. In Ajax, double submit protection should be performed using only one of the above measures i.e. “Preventing double clicking of a button using JavaScript”.
Todo
TBD
There is further scope for reviewing the check methods in Ajax and Web services.
5.12.1.2.1. Preventing double clicking of a button using JavaScript¶
- By disabling the button or link so that it cannot be clicked
- By maintaining a flag for tracking process status and displaying notification “Process in progress” when the button or link is clicked in the middle of the process.
The image when a button is disabled will be as follows:
5.12.1.2.2. About PRG (Post-Redirect-Get) pattern¶
Sr. No. Description (1) Buyer clicks ‘Order’ button on “Product Purchase” screen.The request is submitted using POST method. (2) Server updates DB based on the purchase of the product received through request (1). (3) Server sends a redirect response for the URL to display the “Purchase Complete” screen for the product. (4) Browser submits request for the URL to display the “Purchase Complete” screen for the product.The request is submitted using GET method. (5) Server sends response with a “Purchase Complete” screen for the product. (6) Buyer accidentally executes Reload functionality of the browser.The request called by Reload functionality displays “Purchase Complete” screen of the product; hence update process is not re-executed. (7) Server sends response with a “Purchase Complete” screen.Note
It is recommended to use PRG pattern for the processes associated with update process and implement a control so that a request of GET method is sent when ‘Refresh’ button of the browser is clicked.
Warning
In the PRG pattern, the re-execution of update process cannot be prevented by clicking ‘Back’ button of the browser on Completion screen. A transaction token check must be performed to prevent re-execution of update process after an invalid screen transition using ‘Back’ button of the browser.
5.12.1.2.3. Transaction Token Check¶
Transaction token check consists of the following 3 processes.
- When a request is received from Client, the Server stores a value (hereafter referred to as transaction token) for uniquely identifying a transaction on the Server.
- Server passes the transaction token to the Client. In case of a Web application with screens, it passes the transaction token to the Client using hidden tag of form.
- When submitting the next request, Client sends the transaction token that was passed from the Server. Server compares the transaction token received from the Client and the transaction token stored on the Server.
When the transaction token sent in the request does not match with the transaction token stored on the Server, it is treated as invalid request and an error is returned.
Warning
Misuse of transaction token check leads to poor usability of the application; hence the scope of its usage should be defined by considering the following points.
It is not necessary to include reference-type requests that do not involve data update and requests that perform only screen transitions in the scope of transaction token check.If the scope of transactions is extended unnecessarily, transaction token errors are more likely to occur which in turn reduces the usability of the application. Transaction token check is not mandatory for the processes wherein there is no problem even if the data gets updated multiple times from business perspective (user information update etc.). Transaction token check is mandatory for the processes such as deposit process or product purchase process etc. wherein there is a risk of duplicate execution.
The process flow when expected operations are performed and process flow when unexpected operations are performed using transaction token check are shown below.
Sr. No. Description (1) Client sends the request. (2) Server creates the transaction token (token001) and stores it on the Server. (3) Server passes the created transaction token (token001) to the Client. (4) Client sends the request along with the transaction token (token001). (5) Server checks whether the transaction token (token001) stored on the Server and the transaction token (token001) submitted by the Client are same.Since the values are same, the request is considered as valid. (6) Server generates transaction token (token002) to be used in the next request and updates the value stored on the Server.At this point, the transaction token (token001) is discarded. (7) Server passes the updated transaction token (token002) to the Client.
Sr. No. Description (8) ‘Back’ button of the browser on Client side is clicked. (9) Request is sent from Client side along with the transaction token (token001) of the screen which is displayed after clicking ‘Back’ button. (10) Server checks whether the transaction token (token002) stored on the Server and the transaction token (token001) submitted by the Client are same.Since the values are different, the request is considered as invalid and a transaction token error is thrown. (11) Server sends response with an error screen to notify that a transaction token error has occurred.
The 3 events described below can be prevented by transaction token check.
- Invalid screen transition in case of a business process that requires fixed screen transition
- Data update due to invalid requests that do not involve valid screen transitions
- Duplicate execution of update process due to double submission
Invalid screen transition in case of a business process that requires fixed screen transition, can be prevented by the flow shown below.
Sr. No. Description (1) Buyer clicks ‘Order’ button on “Product Purchase” screen.Since the transaction token stored on the Server and the transaction token submitted by the Client match, the process to purchase the product is executed.At this time, the value of the transaction token stored on the Server is discarded and updated to a new token value. (2) Server updates DB based on the purchase of the product received through request (1). (3) Server sends response with a “Purchase Complete” screen for the product received through request (1). (4) Buyer uses ‘Back’ button of the browser to go back to “Product Purchase” screen. (5) Buyer again clicks ‘Order’ button on “Product Purchase” screen which is displayed using ‘Back’ button of the browser.Since the transaction token sent by the Client is a value which has already been discarded, a transaction token error occurs. (6) Server sends response with an error screen to notify that a transaction token error has occurred.
Data updated by an invalid request which does not involve a valid screen transition can be prevented by the flow shown below.
Sr. No. Description (1) Attacker sends a request to purchase the product directly without performing a valid screen transition.Since the request for generating a transaction token is not executed, a transaction token error occurs. (2) Server sends response with an error screen to notify that a transaction token error has occurred.
Duplicate execution of update process at the time of double submission can be prevented by the flow shown below.
Sr. No. Description (1) Buyer clicks ‘Order’ button on “Product Purchase” screen.Since the transaction token stored on the server and the transaction token submitted by the Client match, the process to purchase the product is executed.At this time, the value of the transaction token that is stored on the server is discarded and updated to a new token value. (2) Buyer accidentally clicks ‘Order’ button again before the response of (1) is returned.Since the transaction token sent by the Client is a value which has already been discarded, a transaction token error occurs when process of (1) is executed. (3) Server sends response with an error screen to notify that a transaction token error has occurred for the request (2). (4) Server updates DB based on the purchase of the product received through request (1). (5) Server attempts to respond with a “Purchase Complete” screen for the product received through request (1); however since the stream for responding to the request of (1) is closed due to the transmission of the request of (2), it fails to send response with a “Purchase Complete” screen.Warning
This can prevent duplicate execution of update process at the time of double submission; however this does not resolve the problem of server not being able to respond with a screen to notify the completion of process. Therefore, it is also recommended to deal with this problem by preventing double clicking of a button using JavaScript.
5.12.1.3. About NameSpace of transaction token¶
In the transaction token check functionality provided by the common library, it is possible to set a NameSpace in the container for storing transaction token. This enables parallel execution of update process using a tab browser or multiple windows.
5.12.1.3.1. Problems that occur when there is no NameSpace¶
Sr. No. Description (1) The request is sent from Window 1, then the transaction token (token001) received as a response is stored in the browser.The transaction token stored on the server is considered token001. (2) The request is sent from Window 2, then the transaction token (token002) received as a response is stored in the browser.The transaction token stored on the server is considered token002. The transaction token (token001) generated by the process (1) is discarded at this point. (3) The request is sent from Window 1 along with the transaction token (token001) stored in the browser.Since the transaction token stored on the server (token002) and the transaction token sent by the request (token001) do not match, the request is considered as invalid resulting in a transaction token error.Warning
The update process cannot be executed concurrently in absence of NameSpace; hence the application becomes a low-usability application.
5.12.1.3.2. Behavior when NameSpace is specified¶
5.12.2. How to use¶
5.12.2.1. Preventing double clicking of button using JavaScript¶
Todo
TBD
The check method in JavaScript will be described in detail in subsequent versions.
5.12.2.2. Using PRG (Post-Redirect-Get) pattern¶
- Controller
@Controller @RequestMapping("prgExample") public class PostRedirectGetExampleController { @Inject UserService userService; @ModelAttribute public PostRedirectGetForm setUpForm() { PostRedirectGetForm form = new PostRedirectGetForm(); return form; } @RequestMapping(value = "create", method = RequestMethod.GET, params = "form") // (1) public String createForm( PostRedirectGetForm postRedirectGetForm, BindingResult bindingResult) { return "prg/createForm"; // (2) } @RequestMapping(value = "create", method = RequestMethod.POST, params = "confirm") // (3) public String createConfirm( @Validated PostRedirectGetForm postRedirectGetForm, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "prg/createForm"; } return "prg/createConfirm"; // (4) } @RequestMapping(value = "create", method = RequestMethod.POST) // (5) public String create( @Validated PostRedirectGetForm postRedirectGetForm, BindingResult bindingResult, RedirectAttributes redirectAttributes) { if (bindingResult.hasErrors()) { return "prg/createForm"; } // omitted String output = "result register..."; // (6) redirectAttributes.addFlashAttribute("output", output); // (6) return "redirect:/prgExample/create?complete"; // (6) } @RequestMapping(value = "create", method = RequestMethod.GET, params = "complete") // (7) public String createComplete() { return "prg/createComplete"; // (8) } }
Sr. No. Description (5) A processing method to perform a process when ‘Register’ button (Create User button) on Confirmation screen is clicked.The request is received by POST method. (6) It is redirected to URL for displaying Completion screen.In the above example, a request is sent to URL"prgExample/create?complete"
byGET
method.When data is to be delivered to redirect destination, addFlashAttribute method ofRedirectAttributes
is called and the data to be delivered is added.addAttribute method ofModel
cannot deliver data to the redirect destination. (7) A processing method to display Completion screen.A request is received by GET method. (8) View (JSP) is called to display the Completion screen and responds with Completion screen.Since the extension of JSP is assigned byViewResolver
defined inspring-mvc.xml
, it is omitted from the return value of the processing method.Note
- At the time of redirecting, assign “redirect:” as the prefix of transition information to be returned by the processing method as the return value.
- When the data is to be delivered to the process of redirect destination, call addFlashAttribute method of
RedirectAttributes
and add the data to be delivered.
createForm.jsp
<h1>Create User</h1> <div id="prgForm"> <form:form action="${pageContext.request.contextPath}/rpgExample/create" method="post" modelAttribute="postRedirectGetForm"> <form:label path="firstName">FirstName</form:label> <form:input path="firstName" /><br> <form:label path="lastName">LastName:</form:label> <form:input path="lastName" /><br> <form:button name="confirm">Confirm Create User</form:button> </form:form> </div>
createConfirm.jsp
<h1>Confirm Create User</h1> <div id="prgForm"> <form:form action="${pageContext.request.contextPath}/rpgExample/create" method="post" modelAttribute="postRedirectGetForm"> FirstName:${f:h(postRedirectGetForm.firstName)}<br> <form:hidden path="firstName" /> LastName:${f:h(postRedirectGetForm.lastName)}<br> <form:hidden path="lastName" /> <form:button>Create User</form:button> <%-- (6) --%> </form:form> </div>
Sr. No. Description (6) When the button to perform update process is clicked, a request is sent by POST method.
createComplete.jsp
<h1>Successful Create User Completion</h1> <div id="prgForm"> <form:form action="${pageContext.request.contextPath}/rpgExample/create" method="get" modelAttribute="postRedirectGetForm"> output:${f:h(output)}<br> <%-- (7) --%> <form:button name="backToTop">Top</form:button> </form:form> </div>
Sr. No. Description (7) When the data delivered from update process is to be referred at the redirect destination, specify the attribute name of the data added by the addFlashAttribute method ofRedirectAttributes
.In the above example,"output"
is the attribute name to refer to the delivered data.
5.12.2.3. Using transaction token check¶
5.12.2.3.1. Transaction token check provided by common library¶
Transaction token check functionality of common library provides @org.terasoluna.gfw.web.token.transaction.TransactionTokenCheck
annotation to perform the following tasks:
- Creation of NameSpace for transaction token
- Starting the transaction
- Token value check in the transaction
- Ending the transaction
The transaction token check can be performed declaratively by assigning @TransactionTokenCheck
annotation for the
Controller class and the processing methods of the Controller class.
5.12.2.3.2. Attributes of @TransactionTokenCheck
annotation¶
The attributes that can be specified in @TransactionTokenCheck
annotation are explained below.
¶ Sr. No. Attribute Name Contents default Example
value Any character string. Used as NameSpace.None value = “create”If there is only 1 argument, the “value =” part can be omitted.
type BEGINA transaction token is created and a new transaction is started.INTransaction token is validated.When the requested token value and the token value stored on the server match, the token value of transaction token is updated.IN type = TransactionTokenType.BEGINtype = TransactionTokenType.INNote
It is recommended that the value to be set in “value” attribute should be same as the config value of “value” attribute for
@RequestMapping
annotation.Note
In “type” attribute, NONE and END can be specified; however, the description is omitted as normally they are not used.
5.12.2.3.3. Format of transaction token¶
Format for the transaction token used in the transaction token check of common library is as follows:
Sr. No. Components Description (1)NameSpace
- NameSpace is an element for assigning a logical name to identify a series of screen transitions.
- By setting a NameSpace, it is possible to prevent the requests belonging to different Namespaces from interfering with each other and it is also possible to increase the number of screen transitions that can operate in parallel.
- The value specified in the “value” attribute of
@TransactionTokenCheck
annotation is used as the value to be used for NameSpace.- When both “value” attribute of the “class” annotation and “value” attribute of the “method” annotation are specified, the value which concatenates both the values with
"/"
is used as NameSpace. When the same value is specified in multiple methods, the methods belong to same NameSpace.- When the “value” attribute is specified only in “class” annotation, all the NameSpaces of the transaction tokens generated in that class will be the value specified in “class” annotation.
- When the “value” attribute is specified only in “method” annotation, the NameSpace of the generated transaction tokens will be the value specified in “method” annotation. When the same value is specified in multiple methods, the methods belong to same NameSpace.
- When both “value” attribute of “class” annotation and “value” attribute of “method” annotation are omitted, the method belong to the global token. For global token, refer to Global Tokens.
(2)TokenKey
TokenKey is an element for identifying the transactions stored in the Namespace.
TokenKey is generated upon execution of a method wherein
TransactionTokenType.BEGIN
is declared in the “type” attribute of@TransactionTokenCheck
annotation. A maximum limit exists for the number of multiple TokenKeys which can be concurrently stored and the default number is 10. The count of stored TokenKeys is managed for each NameSpace. When the number of TokenKeys stored for each NameSpace reaches the maximum value at the time ofTransactionTokenType.BEGIN
, a new transaction will be stored as a valid transaction by discarding the TokenKey with the oldest date and time of execution (Least Recently Used (LRU)). When the access is made by using the discarded transaction token, a transaction token error is thrown. (3)TokenValue
- TokenValue is an element for storing the token value of the transaction.
- TokenValue is generated upon execution of the method wherein
TransactionTokenType.BEGIN
orTransactionTokenType.IN
is declared in the “type” attribute of@TransactionTokenCheck
annotation.Warning
When the “value” attribute is specified only in “method” annotation and if the same value is specified in another Controller, it should be noted that it will be handled as a request for carrying out a series of screen transitions. “value” attribute should be specified by this method only when the screen transitions across Controllers are to be treated as the same transaction.
Basically, it is recommended not to use the method wherein “value” attribute is specified only in “method” annotation.
Note
The method for specifying NameSpace is classified according to creation granularity of the Controller,
- when “value” attributes of both “class” annotation and “method” annotation are specified
- when “value” attribute is specified only in “class” annotation
When a processing method which corresponds to multiple usecases is to be implemented in Controller, “value” attributes of both “class” annotation and “method” annotation are specified.For example, this pattern is used when registration, change, deletion of users is to be implemented in a single Controller. When a processing method which corresponds to a single usecase is to be implemented in Controller, “value” attribute is specified only in “class” annotation.For example, this pattern is used when a Controller is implemented for every registration, modification, deletion of users.
5.12.2.3.4. Lifecycle of transaction token¶
The lifecycle (Generate, Update, Discard) control of transaction token is performed in the following scenarios.
Sr. No. Lifecycle Control Description (1) Token Generation A new token is generated and transaction is started when the processing of the method whereinTransactionTokenType.BEGIN
is specified in “type” attribute of@TransactionTokenCheck
annotation, is terminated. (2) Token Update The token (TokenValue) is updated and transaction is continued when the processing of the method whereinTransactionTokenType.IN
is specified in “type” attribute of@TransactionTokenCheck
annotation, is terminated. (3) Token Discard The tokens are discarded in any of the following scenarios and the transaction is terminated.[1]When the method wherein ``TransactionTokenType.BEGIN`` is specified in “type” attribute of@TransactionTokenCheck
annotation, is called, the transaction token specified in the request parameter is discarded and unnecessary transaction is terminated.[2]If a new transaction starts when number of transaction tokens (TokenKey) that can be stored in NameSpace has reached the maximum limit, the transaction token with the oldest date and time of execution is discarded and the transaction is forcibly terminated.[3]When exceptions such as system error occur, the transaction token specified in the request parameter is discarded and the transaction is terminated.Note
A maximum limit is provided for the number of transaction tokens (TokenKey) which can be stored in a NameSpace. When the maximum limit is reached while generating a new transaction token, a new transaction is managed as a valid transaction, by discarding the transaction token which has the TokenKey with the oldest date and time of execution (Least Recently Used (LRU)).
The maximum limit of transaction tokens that can be stored for each NameSpace is 10 by default. To change the maximum limit, refer to How to change the maximum limit of transaction tokens .
- A default value (10) is specified as the maximum limit for the number of transaction tokens that can be stored in the NameSpace.
@TransactionTokenCheck("name")
is specified as the class annotation of Controller.- Transaction tokens of the same NameSpace have reached the maximum limit.
Sr. No. Description (1) A request to start a new transaction is received when the transaction tokens of the same NameSpace have reached the maximum limit. (2) A new transaction token is generated. (3) The generated transaction token is added to the location where tokens are stored.** At this point, the transaction tokens that exceed the maximum limit are present in the NameSpace.** (4) The transaction tokens exceeding the maximum limit are deleted from the NameSpace.The transaction tokens are deleted in a sequence starting with the transaction token with the oldest date and time of execution.
5.12.2.3.5. Settings for using a transaction token check¶
The settings for using the transaction token check provided by the common library are shown below.
spring-mvc.xml
<mvc:interceptors> <mvc:interceptor> <!-- (1) --> <mvc:mapping path="/**" /> <!-- (2) --> <mvc:exclude-mapping path="/resources/**" /> <!-- (2) --> <mvc:exclude-mapping path="/**/*.html" /> <!-- (2) --> <!-- (3) --> <bean class="org.terasoluna.gfw.web.token.transaction.TransactionTokenInterceptor" /> </mvc:interceptor> </mvc:interceptors> <bean id="requestDataValueProcessor" class="org.terasoluna.gfw.web.mvc.support.CompositeRequestDataValueProcessor"> <constructor-arg> <util:list> <!-- (4) --> <bean class="org.terasoluna.gfw.web.token.transaction.TransactionTokenRequestDataValueProcessor" /> <!-- omitted --> </util:list> </constructor-arg> </bean>
Sr. No. Description (1) SetHandlerInterceptor
to generate and check transaction tokens. (2) Specify a request path whereinHandlerInterceptor
is to be applied.In the above example, it is applicable to all the requests except the requests under /resources and the requests to HTML. (3) Specify a class (TransactionTokenInterceptor
) to generate and check transaction tokens using@TransactionTokenCheck
annotation. (4) Set a class (TransactionTokenRequestDataValueProcessor
) for automatic embedding of the transaction token to the Hidden area using<fomr:form>
tag of Spring MVC.
5.12.2.3.6. Settings for handling transaction token errors¶
InvalidTransactionTokenException
to the following settings.ExceptionCodeResolver
defined inapplicationContext.xml
SystemExceptionResolver
defined inspring-mvc.xml
For adding the settings, refer to the following:
5.12.2.3.7. How to use transaction token check in Controller¶
- Controller
@Controller @RequestMapping("transactionTokenCheckExample") @TransactionTokenCheck("transactionTokenCheckExample") // (1) public class TransactionTokenCheckExampleController { @RequestMapping(params = "first", method = RequestMethod.GET) public String first() { return "transactionTokenCheckExample/firstView"; } @RequestMapping(params = "second", method = RequestMethod.POST) @TransactionTokenCheck(type = TransactionTokenType.BEGIN) // (2) public String second() { return "transactionTokenCheckExample/secondView"; } @RequestMapping(params = "third", method = RequestMethod.POST) @TransactionTokenCheck // (3) public String third() { return "transactionTokenCheckExample/thirdView"; } @RequestMapping(params = "fourth", method = RequestMethod.POST) @TransactionTokenCheck // (3) public String fourth() { return "transactionTokenCheckExample/fourthView"; } @RequestMapping(params = "fifth", method = RequestMethod.POST) @TransactionTokenCheck // (3) public String fifth() { return "redirect:/transactionTokenCheckExample?complete"; } @RequestMapping(params = "complete", method = RequestMethod.GET) public String complete() { // (4) return "transactionTokenCheckExample/fifthView"; } }
Sr. No. Description (1) NameSpace is specified in “value” attribute of “class” annotation.In the above example, the value same as the “value” attribute of@RequestMapping
which is the recommended pattern of this guideline is specified. (2) The transaction is started and a new transaction token is issued.Here, since the transaction tokens are managed at Controller level, “value” attribute of “method” annotation is not specified. (3) The transaction token is checked and transaction token value is updated.If the type attribute is omitted, the behavior remains the same as when@TransactionTokenCheck(type = TransactionTokenType.IN)
is specified. (4) Since it is not necessary to perform transaction token check in the request for displaying a screen which notifies the completion of the usecase,@TransactionTokenCheck
annotation is not specified.Note
- When BEGIN is specified in the “type” attribute of
@TransactionTokenCheck
annotation, transaction token is not checked since a new TokenKey is generated.- When IN is specified in the “type” attribute of
@TransactionTokenCheck
annotation, it is checked whether the token value specified in the request and the token value stored on the server are the same.
5.12.2.3.8. How to use transaction token check in View (JSP)¶
<form:form>
tag is recommended as a method to submit it as a request parameter after carrying out Settings for using a transaction token check.firstView.jsp
<h1>First</h1> <form:form method="post" action="transactionTokenCheckExample"> <input type="submit" name="second" value="second" /> </form:form>
secondView.jsp
<h1>Second</h1> <form:form method="post" action="transactionTokenCheckExample"><!-- (1) --> <input type="submit" name="third" value="third" /> </form:form>
thirdView.jsp
<h1>Third</h1> <form:form method="post" action="transactionTokenCheckExample"><!-- (1) --> <input type="submit" name="fourth" value="fourth" /> </form:form>
fourthView.jsp
When
<form:form>
tag is used<h1>Fourth</h1> <form:form method="post" action="transactionTokenCheckExample"><!-- (1) --> <input type="submit" name="fifth" value="fifth" /> </form:form>
When ``<form>`` tag of HTML is used
<h1>Fourth</h1> <form method="post" action="transactionTokenCheckExample"> <t:transaction /><!-- (2) --> <!-- (3) --> <input type="hidden" name="${f:h(_csrf.parameterName)}" value="${f:h(_csrf.token)}"/> <input type="submit" name="fifth" value="fifth" /> </form>
fifthView.jsp
<h1>Fifth</h1> <form:form method="get" action="transactionTokenCheckExample"> <input type="submit" name="first" value="first" /> </form:form>
Sr. No. Description (1) When<form:form>
tag is used in JSP and if BEGIN or IN is specified in “type” attribute of@TransactionTokenCheck
annotation, the Value ofname="_TRANSACTION_TOKEN"
is automatically embedded as a hidden tag. (2) When<form>
tag of HTML is used, a hidden tag same as (1) is embedded using<t:transaction />
. (3) When<form>
tag of HTML is used, csrf token necessary for CSRF token check provided by Spring Security needs to be embedded as a hidden item.For csrf token necessary for CSRF token check, refer to How to explicitly insert CSRF token.Note
If
<form:form>
tag is used, the parameters necessary for CSRF token check are also automatically embedded. Refer to How to insert CSRF token automatically for the parameters necessary for CSRF token check.Note
<t:transaction />
is a JSP tag library provided by the common library. For the “t:” used in (2), refer to Creating common JSP for include.
- Example of Output HTML
Following observations can be made upon verifying the output HTML.
- For NameSpace, the value specified in “value” attribute of the class annotation is set.In the above example,
"transactionTokenCheckExample"
(underlined in orange) is the NameSpace. - For TokenKey, the value that was issued at the start of the transaction is circulated and set.In the above example,
"c0123252d531d7baf730cd49fe0422ef"
(underlined in blue) is the TokenKey. - Value to be set for TokenValue varies depending on request.In the above example,
"3f610684e1cb546a13b79b9df30a7523"
,"da770ed81dbca9a694b232e84247a13b"
,"bd5a2d88ec446b27c06f6d4f486d4428"
(underlined in green) are TokenValues.
5.12.2.3.9. When multiple usecases are to be implemented in one Controller¶
- Controller
@Controller @RequestMapping("transactionTokenChecFlowkExample") @TransactionTokenCheck("transactionTokenChecFlowkExample") // (1) public class TransactionTokenCheckFlowExampleController { @RequestMapping(value = "flowOne", params = "first", method = RequestMethod.GET) public String flowOneFirst() { return "transactionTokenChecFlowkExample/flowOneFirstView"; } @RequestMapping(value = "flowOne", params = "second", method = RequestMethod.POST) @TransactionTokenCheck(value = "flowOne", type = TransactionTokenType.BEGIN) // (2) public String flowOneSecond() { return "transactionTokenChecFlowkExample/flowOneSecondView"; } @RequestMapping(value = "flowOne", params = "third", method = RequestMethod.POST) @TransactionTokenCheck(value = "flowOne", type = TransactionTokenType.IN) // (2) public String flowOneThird() { return "transactionTokenChecFlowkExample/flowOneThirdView"; } @RequestMapping(value = "flowTwo", params = "first", method = RequestMethod.GET) public String flowTwoFirst() { return "transactionTokenChecFlowkExample/flowTwoFirstView"; } @RequestMapping(value = "flowTwo", params = "second", method = RequestMethod.POST) @TransactionTokenCheck(value = "flowTwo", type = TransactionTokenType.BEGIN) // (3) public String flowTwoSecond() { return "transactionTokenChecFlowkExample/flowTwoSecondView"; } @RequestMapping(value = "flowTwo", params = "third", method = RequestMethod.POST) @TransactionTokenCheck(value = "flowTwo", type = TransactionTokenType.IN) // (3) public String flowTwoThird() { return "transactionTokenChecFlowkExample/flowTwoThirdView"; } @RequestMapping(value = "flowThree", params = "first", method = RequestMethod.GET) public String flowThreeFirst() { return "transactionTokenChecFlowkExample/flowThreeFirstView"; } @RequestMapping(value = "flowThree", params = "second", method = RequestMethod.POST) @TransactionTokenCheck(value = "flowThree", type = TransactionTokenType.BEGIN) // (4) public String flowThreeSecond() { return "transactionTokenChecFlowkExample/flowThreeSecondView"; } @RequestMapping(value = "flowThree", params = "third", method = RequestMethod.POST) @TransactionTokenCheck(value = "flowThree", type = TransactionTokenType.IN) // (4) public String flowThreeThird() { return "transactionTokenChecFlowkExample/flowThreeThirdView"; } }
Sr. No. Description (1) NameSpace is specified in “value” attribute of “class” annotation.In the above example, the value same as the “value” attribute of@RequestMapping
which is a recommended pattern of this guideline, is specified. (2) The transaction token is checked for processing of usecase with name"flowOne"
.In the above example, the value same as the “value” attribute of@RequestMapping
which is a recommended pattern of this guideline, is specified. (3) The transaction token is checked for processing of usecase with name"flowTwo"
.In the above example, the value same as the “value” attribute of@RequestMapping
which is a recommended pattern of this guideline, is specified. (4) The transaction token is checked for processing of usecase with name"flowThree"
.In the above example, the value same as the “value” attribute of@RequestMapping
which is a recommended pattern of this guideline, is specified.Note
Allocating a NameSpace for each usecase enables transaction token check for each usecase.
5.12.2.3.10. Typical example of using transaction token check¶
See the example below wherein transaction token check is applied for the usecase which carries out a simple screen transition such as “Input Screen-> Confirmation Screen-> Completion Screen”.
- Controller
@Controller @RequestMapping("user") @TransactionTokenCheck("user") // (1) public class UserController { // omitted @RequestMapping(value = "create", params = "form") public String createForm(UserCreateForm form) { // (2) return "user/createForm"; } @RequestMapping(value = "create", params = "confirm", method = RequestMethod.POST) @TransactionTokenCheck(value = "create", type = TransactionTokenType.BEGIN) // (3) public String createConfirm(@Validated UserCreateForm form, BindingResult result) { // omitted return "user/createConfirm"; } @RequestMapping(value = "create", method = RequestMethod.POST) @TransactionTokenCheck(value = "create") // (4) public String create(@Validated UserCreateForm form, BindingResult result) { // omitted return "redirect:/user/create?complete"; } @RequestMapping(value = "create", params = "complete") public String createComplete() { // (5) return "user/createComplete"; } // omitted }
Sr. No. Description (1) A NameSpace called"user"
is set as “class” annotation.In the above example, the value same as “value” attribute of@RequestMapping
annotation which is a recommended pattern, is specified. (2) A processing method to display input screen.It is a screen to start a usecase; however since the process only displays data and does not involve data update, it is not necessary to start a transaction.Therefore,@TransactionTokenCheck
annotation is not specified in the example given above. (3) A processing method to perform input validation and display the Confirmation screen.A transaction is started at this point since a button to perform update process is placed on the Confirmation screen.A View (JSP) is specified for the transition destination. (4) A processing method to perform update.Since this method performs update, a transaction token check is performed. (4) A processing method to display the Completion screen.Since the method only displays a Completion screen, the transaction token check is not required.Therefore,@TransactionTokenCheck
annotation is not specified in the example given above.Warning
It is necessary to specify the View (JSP) for the transition destination of processing method wherein
@TransactionTokenCheck
annotation is defined. If other than View (JSP) of the redirect destination is specified as transition destination, the value of TransactionToken changes in the next process always resulting in the TransactionToken error.
5.12.2.3.11. Exclusion control of parallel processing while using a session¶
When a form object is stored in a session using @SessionAttribute
annotation,
and if multiple screen transitions of the same processing are performed in parallel, screen operations may interfere with each other and the values displayed on the screen and the values stored in the session may no longer match.
Transaction token check function can be used to prevent requests from non-conforming screens as the invalid requests.
The maximum limit of transaction tokens that can be stored for each NameSpace is set to 1.
spring-mvc.xml
<mvc:interceptor> <mvc:mapping path="/**" /> <!-- omitted --> <bean class="org.terasoluna.gfw.web.token.transaction.TransactionTokenInterceptor"> <constructor-arg value="1"/> <!-- (1) --> </bean> </mvc:interceptor>
Sr. No. Description (1) The number of transaction tokens stored for each NameSpace is set to “1”.Note
When form objects etc. are stored in session using
@SessionAttribute
annotation, the requests from the screen displaying old data can be prevented as invalid requests by setting number of transaction token stored for each NameSpace to “1”.
5.12.3. How to extend¶
5.12.3.1. How to manage the lifecycle of transaction tokens using a program¶
It is possible to receive org.terasoluna.gfw.web.token.transaction.TransactionTokenContext
as an argument for processing method of Controller and manage the lifecycle of transaction tokens programmatically by adding the settings give below.
spring-mvc.xml
<mvc:annotation-driven> <mvc:argument-resolvers> <!-- (1) --> <bean class="org.terasoluna.gfw.web.token.transaction.TransactionTokenContextHandlerMethodArgumentResolver" /> </mvc:argument-resolvers> </mvc:annotation-driven>
Sr. No. Description (1) In<mvc:argument-resolvers>
element, set the class (TransactionTokenContextHandlerMethodArgumentResolver
) which passes the object (TransactionTokenContext
) managing the lifecycle of transaction tokens programmatically, as an argument for methods of Controller.When it is not necessary to manage the lifecycle of transaction tokens using a program, this setting is not required.Note
This setting is not required as the transaction tokens that can no longer be used are automatically discarded when the tokens that can be stored in a NameSpace exceeds the maximum limit.
5.12.3.2. How to change the maximum limit of transaction tokens¶
The maximum limit of transaction tokens that can be stored on 1 NameSpace can be changed by performing settings given below.
spring-mvc.xml
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <mvc:exclude-mapping path="/resources/**" /> <mvc:exclude-mapping path="/**/*.html" /> <bean class="org.terasoluna.gfw.web.token.transaction.TransactionTokenInterceptor" /> <constructor-arg value="5"/> <!-- (1) --> </mvc:interceptor> </mvc:interceptors>
Sr. No. Description (1) The maximum limit of transaction tokens that can be stored in a NameSpace is specified as a value of constructor ofTransactionTokenInterceptor
.The default value (value that is set when the default constructor is used) is 10.In the above example, the default value (10) is changed to 5.
5.12.4. Appendix¶
5.12.4.1. Transaction Token Check in case that the cache of browser is disabled¶
If the cache of web browser is disabled due to Cache-Control
in HTTP response header,
the message will display which tells the cache has been expired before transaction token error
in case of the illegal operation flow in “Transaction Token Check”
Concretely, the following screen will display when the browser back button is clicked (8). This is an example of Internet Explorer 11.
There is no problem because the double submit itself is prevented.
In blank projects after 5.0.0.RELEASE, it is configured so that the cache is disabled by Spring Security.
If showing the transaction error screen is preferred instead of the screen above,
excluding <sec:cache-control />
is required.
However, <sec:cache-control />
should be configured from the point of view of security.
5.12.4.2. Global Tokens¶
@TransactionTokenCheck
annotation is not specified, it is handled as global transaction token."globalToken"
(fixed value) is used for the NameSpace of global transaction tokens.Note
When only a single screen transition is to be allowed as an overall application, it can be implemented by setting the maximum limit of transaction tokens that can be stored for each NameSpace to 1 and using the global token.
The settings and implementation example when only a single screen transition is to be allowed as an overall application are shown below.
5.12.4.2.1. Changing the maximum limit of transaction tokens that can be stored for each NameSpace¶
The maximum limit of transaction tokens that can be stored for each NameSpace is set to 1.
spring-mvc.xml
<mvc:interceptor> <mvc:mapping path="/**" /> <!-- omitted --> <bean class="org.terasoluna.gfw.web.token.transaction.TransactionTokenInterceptor"> <constructor-arg value="1"/> <!-- (1) --> </bean> </mvc:interceptor>
Sr. No. Description (1) The number of tokens stored for each NameSpace is set to “1”.
5.12.4.2.2. Implementation of Controller¶
The value is not specified in “value” attribute of @TransactionTokenCheck
annotation, in order to make it the NameSpace for global tokens.
- Controller
@Controller @RequestMapping("globalTokenCheckExample") public class GlobalTokenCheckExampleController { // (1) @RequestMapping(params = "first", method = RequestMethod.GET) public String first() { return "globalTokenCheckExample/firstView"; } @RequestMapping(params = "second", method = RequestMethod.POST) @TransactionTokenCheck(type = TransactionTokenType.BEGIN) // (2) public String second() { return "globalTokenCheckExample/secondView"; } @RequestMapping(params = "third", method = RequestMethod.POST) @TransactionTokenCheck // (2) public String third() { return "globalTokenCheckExample/thirdView"; } @RequestMapping(params = "fourth", method = RequestMethod.POST) @TransactionTokenCheck // (2) public String fourth() { return "globalTokenCheckExample/fourthView"; } @RequestMapping(params = "fifth", method = RequestMethod.POST) public String fifth() { return "globalTokenCheckExample/fifthView"; } }
Sr. No. Description (1)@TransactionTokenCheck
annotation is not specified as the class annotation. (2) The “value” attribute of@TransactionTokenCheck
annotation to be specified as “method” annotation is not specified.
- Example of Output HTML
JSP used is same as the one created in How to use transaction token check in View (JSP) .Other details are same except change in action from"transactionTokenCheckExample"
to"globalTokenCheckExample"
.
Following observations can be made upon verifying the output HTML.
- For NameSpace, a fixed value called
"globalToken"
is set. - For TokenKey, the value that was issued while starting the transaction is circulated and set.In the above example,
"9d937be4adc2f5dd2032292d153f1133"
(underlined in blue) is the TokenKey. - Value to be set for TokenValue varies depending on request.In the above example,
"9204d7705ce7a17f16ca6cec24cfd88b"
,"69c809fefcad541dbd00bd1983af2148"
,"6b83f33b365f1270ee1c1b263f046719"
(underlined in green) are TokenValues.
The behavior when the maximum limit of transaction tokens for each NameSpace is set to 1 and global token is used, is explained below.
Sr. No. Description (1) In window 1 process, TransactionTokenType.BEGIN is performed and global token is generated. (2) In window 2 process, the token is updated by TransactionTokenType.BEGIN.Although internally the data is reshuffled rather than getting updated, it gives the impression that the token has been updated since one transaction token can be stored on the server. (3) Token value is checked in TransactionTokenType.IN of window 1 process.Transaction token generated in process 1 is submitted as request parameter; however since the specified token does not exist on the server, a transaction token error occurs. (4) Token value is checked in TransactionTokenType.IN of window 2 process.The transaction token generated in process 2 is submitted as request parameter and it is checked whether the value matches with the token value stored on the server.If the value matches, the process is continued. (5) Similar to (4) . (6) Similar to (4) . (7) When a screen is to be displayed using redirect, hidden tag for transaction token does not exist.Note
The transaction token existing on the server is automatically deleted when a new global token is generated.
5.12.4.3. Quick Reference¶
Number Number of tokens stored for each NameSpace NameSpace value specified in class NameSpace value specified in method Example of generated token Business limitations (1) 10 (Default) account Not specified account~key~value Number of concurrent executions of all Account usecases (create/update/delete) is restricted to 10. (2) 10 (Default) account create account/create~key~value Number of concurrent executions of “create” operation of Account usecase is restricted to 10. (3) 10 (Default) account update account/update~key~value Number of concurrent executions of “update” operation of Account usecase is restricted to 10. (4) 10 (Default) account delete account/delete~key~value Number of concurrent executions of “delete” operation of Account usecase is restricted to 10. (By specifying (2), (3) and (4), the number of concurrent executions of all Account usecases should be 30. Since this setting value is too high for most applications, a value smaller than default value 10 can also be specified.) (5) 10 (Default) Unspecified create create~key~value The same Namespace called “create” is created in the application and the number of concurrent executions is restricted to 10. When the Account and Customer business processes are independent, and among them when “create” is specified in the NameSpace of TransactionToken using “create” method, the total number of concurrent executions is restricted to 10. (6) 10 (Default) Not specified update update~key~value Same as (5) (7) 10 (Default) Not specified delete delete~key~value Same as (5) (8) 10 (Default) Not specified Not specified globalToken~key~value The total number of concurrent executions of all Account and Customer usecases is restricted to 10. (9) 1 (Custom Setting in spring-mvc.xml) account Not specified account~key~value The number of concurrent executions of all Account usecases should be restricted to 1. Execution of only a single business process is possible for Account create/update/delete. This is valid when screen transition using only 1 screen is assumed. (10) 1 (Custom Setting in spring-mvc.xml) account create account/create~key~value The number of concurrent executions of “create” operation of Account usecases should be restricted to 1. It is not possible to simultaneously execute Account “create” by opening 2 screens. (11) 1 (Custom Setting in spring-mvc.xml) account update account/update~key~value Same as (10) (12) 1 (Custom Setting in spring-mvc.xml) account delete account/delete~key~value Same as (10) (13) 1 (Custom Setting in spring-mvc.xml) Not specified create create~key~value” A same NameSpace called “create” is created in the application and the number of concurrent executions should be restricted to 1. When the Account and Customer business processes are independent and when “create” is specified in NameSpace of TransactionToken using “create” method, “create” for Account and Customer cannot be performed concurrently (14) 1 (Custom Setting in spring-mvc.xml) Not specified update update~key~value Same as (13) (15) 1 (Custom Setting in spring-mvc.xml) Not specified delete delete~key~value Same as (13) (16) 1 (Custom Setting in spring-mvc.xml) Not specified Not specified globalToken~key~value The business processes that can be concurrently executed in the application are restricted to 1. This should be used in projects where only 1 operation is performed in 1 session.