Handling Duplicate Form Submissions in Struts
By: Ivan Lim
Duplicate form submissions are acceptable in some cases. Such scenarios are called idempotent transitions. When multiple submissions of data are not critical enough to impact the behavior of the application, duplicate form submissions do not pose a threat.
They can cause a lot of grief if for instance you are buying from an online store and accidentally press refresh on the page where you are charged. If storefront is smart enough, it will recognize duplicate submissions and handle it graciously without charging you twice.
Duplicate form submissions can occur in many ways Using Refresh button
Using the browser back button to traverse back and
resubmit form
Using Browser history feature and re-submit form.
Malicious submissions to adversely impact the server or
personal gains
Clicking more than once on a transaction that take longer
than usual
Why is the form submitted again after all, when the refresh button is pressed? The answer lies in the URL seen in the URL bar of your browser after the form submission. Consider a form as: <form name=CustomerForm†action=â€/App1/submitCustomerForm.doâ€>. The above form is submitted with the URL /App1/submitCustomerForm.do and the same URL is shown in the URL bar. On the back end, Struts selects the action mapping associated with submitCustomerForm and executes the action instance. When you press refresh, the same URL is submitted and the same action instance is executed again. The easy solution to this problem is to use HTTP redirect after the form submission. Suppose that the CustomerForm submission results in showing a page called Success.jsp. When HTTP redirect is used, the URL in the URL bar becomes /App1/Success.jsp instead of /App1/submitCustomerForm.do. When the page refreshed, it is the Success.jsp that is loaded again instead of /App1/submitCustomerForm.do. Hence the form is not submitted again. To use the HTTP redirect feature, the forward is set as follows:
<forward name=â€success†path=â€/Success.jsp†redirect=â€true†/>
However there is one catch. With the above setting, the actual JSP name is shown in the URL. Whenever the JSP name appears in the URL bar, it is a candidate for ForwardAction. Hence change the above forward to be as follows:
<forward name=â€success†path=â€/GotoSuccess.do†redirect=â€true†/>
where GotoSuccess.do is another action mapping using ForwardAction as follows:
<action path=â€/GotoSuccessâ€
type=â€org.apache.struts.actions.ForwardActionâ€
parameter=â€/Success.jspâ€
validate=â€false†/>
Now, you have now addressed the duplicate submission due to accidental refreshing by the customer. It does not prevent you from intentionally going back in the browser history and submitting the form again. Malicious users might attempt this if the form submissions benefit them or adversely impact the server.
Struts provides you with the next level of defense: Synchronizer Token. To understand how the Synchronizer Token works, some background about built-in functionalities in the Action class is required. The Action class has a method called saveToken() whose logic is as follows:
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 invokes the saveToken() method to create a session attribute with the above name. In the JSP, you have to use the token 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 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 (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.
NOTE: We could also have chosen to have the synchronizer token as an ActionForm attribute. In that case, the <html:hidden> tag could have been used instead of the above <input type=â€hiddenâ€> tag (which looks complicated at the first sight). However we have not chosen to go down this path since protection from duplicate submission is not a characteristic of the form and it does not logically fit there very well.
Although the above approach is good, it requires you as a 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, you have to identify the pairs and add them manually. You can use the same approach for sensitive hyperlink navigations. Just set the tranaction attribute in <html:link> to true and use the same logic in the Action classes to track the duplicate hyperlink navigations.
The reset argument of the isTokenValid() is useful for
multi-page form scenario. Consider a form that spans across multiple pages. The
form is submitted every time the user traverses 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 argument is useful.
The token is initially set before showing the first page of the form. The
reset argument is
false for all the 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.
Archived Comments
1. RobertVeita
View Tutorial By: RobertVeita at 2017-09-12 20:06:00
2. Shawncob
View Tutorial By: Shawncob at 2017-08-29 04:43:54
3. Larryhaw
View Tutorial By: Larryhaw at 2017-07-27 16:44:53
4. Georgemes
View Tutorial By: Georgemes at 2017-07-21 19:50:03
5. emolika
View Tutorial By: emolika at 2017-07-13 22:05:37
6. mepucuxas
View Tutorial By: mepucuxas at 2017-07-13 21:27:24
7. usabadigo
View Tutorial By: usabadigo at 2017-07-10 22:07:26
8. akavotirezie
View Tutorial By: akavotirezie at 2017-07-10 21:59:04
9. ijacollfiruyi
View Tutorial By: ijacollfiruyi at 2017-07-10 21:56:32
10. akanuopijoca
View Tutorial By: akanuopijoca at 2017-07-10 21:54:53
11. ajagecoswir
View Tutorial By: ajagecoswir at 2017-07-10 21:45:51
12. upuxadj
View Tutorial By: upuxadj at 2017-07-10 21:42:59
13. Donaldnob
View Tutorial By: Donaldnob at 2017-06-07 00:07:32
14. Armandomerly
View Tutorial By: Armandomerly at 2017-06-04 06:16:35
15. Hi there very cool website!! Guy .. Excellent ..
Superb .. I'll bookmark your blog and take t
View Tutorial By: height growth at 2017-06-01 20:09:05
16. Matthewnaics
View Tutorial By: Matthewnaics at 2017-05-22 02:54:11
17. Harrygeora
View Tutorial By: Harrygeora at 2017-05-22 00:14:02
18. EthanTeelm
View Tutorial By: EthanTeelm at 2017-05-09 07:04:09
19. LarryAmare
View Tutorial By: LarryAmare at 2017-05-09 03:19:36
20. Armandomerly
View Tutorial By: Armandomerly at 2017-05-03 06:09:32
21. de Bonnefoux avait flechi sous le poids de ses occupations sans nombre;
mais a l'annonce de l
View Tutorial By: institut de massage lyon at 2017-03-30 02:37:45
22. Please explain properly :O
View Tutorial By: Tuftsri at 2015-11-27 09:32:00
23. Hi friends i have one problem am register one form using jsp struts when i submit its go and save in
View Tutorial By: Inbaraj at 2014-07-04 05:28:55
24. Your solution won't work if you have 2 different forms using multiple windows/tabs. Because the toke
View Tutorial By: mkieu at 2012-06-19 00:04:29
25. Great article and helped me a lot!
Thanks!
View Tutorial By: Yasas at 2011-11-29 06:05:00
26. This does not work in following case :
Clicking more than once on a transaction that take lo
View Tutorial By: Amardeep at 2010-01-20 05:59:21
27. Excelente!!
Justo lo que necesitaba.
Gracias!!!
View Tutorial By: Cesar at 2010-01-07 17:40:52
28. I tried the first option i.e for refresh use ForwardAction and it works, thanks for the info
View Tutorial By: dinesh at 2009-07-31 05:31:23
29. good work.
if there is a ValidatorForm then i added this line in the top of validate functio
View Tutorial By: floriano at 2008-10-09 07:23:16
30. Using HTTP Redirect as per your suggesion, worked for me. Thx.
View Tutorial By: Balram at 2008-03-21 01:27:02
31. I did not understand it. please give one example explain it simply and stight forward not formally.
View Tutorial By: farhaan at 2008-03-20 12:06:11
Comment on this tutorial
- Data Science
- Android
- AJAX
- ASP.net
- C
- C++
- C#
- Cocoa
- Cloud Computing
- HTML5
- Java
- Javascript
- JSF
- JSP
- J2ME
- Java Beans
- EJB
- JDBC
- Linux
- Mac OS X
- iPhone
- MySQL
- Office 365
- Perl
- PHP
- Python
- Ruby
- VB.net
- Hibernate
- Struts
- SAP
- Trends
- Tech Reviews
- WebServices
- XML
- Certification
- Interview
categories
Related Tutorials
Configuring JDBC DataSources in Struts
When is the best time to validate input in Struts
Simple example of using the requiredif Validator rule in Struts
How to prepopulate a form in Struts
Using JavaScript to submit a form in Struts
FAQ: Why are my checkboxes not being set from ON to OFF?
FAQ: Why was reload removed from Struts (since 1.1)?
What is a Plug-in and how to use Java plug-ins with Struts?
Origin and Architecture of Struts
Handling multiple buttons in HTML Form in Struts