• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Preventing multiple posts

 
Greenhorn
Posts: 22
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In order to avoid the multiple posts by the user, in the action class's execute method I am checking whether the token is valid or not. But always it is false. Why isTokenValid in Action class returns false always??

if (isTokenValid(request)) {
//process the request
return mapping.findForward("success");
} else {
return mapping.findForward("failure");
}

How to solve this one?
 
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Did you call saveToken()?

The flow is usually like this:

On initial request:
1. saveToken(request)
2. forward to JSP

When user submits form:
3. check if isTokenValid(request)
4. if true, process request then call resetToken(); otherwise, submission is not valid.

What's happening under the hood:
@1 Struts will generate a unique value (the token) and keep it in the session context
@2 When the JSP is rendered, Struts inserts the token as a hidden field
@3 The hidden field token is submitted along with the rest of the form and isValidToken() checks the value that came in with the current request against the value that was saved in the session context by the most recent saveToken() call. If the two token values match, the submission is valid.
 
author
Posts: 11962
5
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
FYI, I created an entry into our StrutsFaq based on Junilu's answer...
 
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Junilu Lacar:
Did you call saveToken()?

The flow is usually like this:

On initial request:
1. saveToken(request)
2. forward to JSP

When user submits form:
3. check if isTokenValid(request)
4. if true, process request then call resetToken(); otherwise, submission is not valid.



What's meant by "initial request"?
 
Krishnappan Muthuraman
Greenhorn
Posts: 22
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Step 1:
-------
In your jsp page add the token to the session and have a hidden field and store the same value

<%
String token = TokenProcessor.getInstance().generateToken(request);
session.setAttribute("org.apache.struts.action.TOKEN", token);
%>

<html:hidden property="org.apache.struts.taglib.html.TOKEN" value="<%=token%>"/>

Step 2:
--------
In action class execute method addd below lines..

if (isTokenValid(request)) {
resetToken(request);
//process the request..
} else {
return mapping.findForward("failure");
}

Then it works!!!. This is actcally a round about way.

But why savetoken(request) doesn't work??
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Step 1:
-------
In your jsp page add the token to the session and have a hidden field and store the same value

<%
String token = TokenProcessor.getInstance().generateToken(request);
session.setAttribute("org.apache.struts.action.TOKEN", token);
%>


This is pretty much what saveToken() and Struts does only you're choosing to do it yourself in the JSP which, IMO, is a bad design choice.

This is actcally a round about way.

Yes, it is. And unnecessary.

But why savetoken(request) doesn't work??

saveToken() works, I use it all the time. You're probably doing something wrong but it's hard to tell without any code to diagnose. Post the code that you think should work.
[ August 04, 2004: Message edited by: Junilu Lacar ]
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Roel De Nijs:
What's meant by "initial request"?



The "initial request" is made when the user clicks on a button or link (i.e. user submits a request) that will bring up the page that you want to protect from multiple submits. Assuming you are following the advice to Link Only to Actions, you can then call saveToken() before forwarding to the requested page. This causes Struts to insert the hidden token field in any html forms included in the response.
[ August 04, 2004: Message edited by: Junilu Lacar ]
 
Krishnappan Muthuraman
Greenhorn
Posts: 22
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
How will you set the value of the hidden field? I used this approach because saveToken(request) doesn't return anything!. Do you know how to the hidden field?

<html:hidden property="org.apache.struts.taglib.html.TOKEN" value="<%=token%>"/>

----Krishnappan Muthuraman
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Krishnappan Muthuraman:
How will you set the value of the hidden field?



You don't. Struts does it for you.

OK, since you won't post any code, I'll try to write some sample skeleton code and you can compare it with what you're doing :roll: . Caveat: this is off the top of my head and untested.


 
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi
Our application is a State Based one...we have to allow user to submit form only once. We are using struts1.1,servlet2.3,jstl,jboss.

StrutsConfig.xml
-----------------

<form-beans>
<form-bean name="testForm" type="com.bluephant.form.TestForm" />
</form-beans>

<action-mappings>

<action path="/test" type="com.bluephant.form.TestPreAction" scope="session">
<forward name="success" path="/test.jsp" />
</action>

<action path="/test2"
type="com.bluephant.form.TestPostAction"
name="testForm"
validate="true"
input="/test.jsp"
scope="request">
<forward name="next" path="/test_next.jsp" />
</action>

TestPreAction
-------------
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

saveToken(request);
return (mapping.findForward("success"));

}
TestPostAction
----------------
public ActionForward execute(ActionMapping mapping,ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

if (isTokenValid(request)) {
resetToken(request);
saveToken(request);
//valid token process the form
return (mapping.findForward("next"));
}else{
MessageResources messages = getResources(request);
ActionErrors errors = new ActionErrors();
errors.add("org.apache.struts.action.GLOBAL_ERROR", new ActionError( "dvd.error.invalidToken" ) );
saveErrors( request, errors );
//duplicate submission
return (new ActionForward(mapping.getInput()));
}
test.jsp
---------
<html:form action="/test2.do" method="POST">

If i use browser back button once i submit form isTokenValid(request)) in TestPostAction always returns the true..... instead of false.....

it working fine if i use refresh button.
 
Greenhorn
Posts: 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
That's because if the saveToken is not called before isTokenValid,
it will return false as there is no TOKEN set in the session.

How do you solve it? You have to have forward action whose job is to saveToken and forward you to the form.

Now when the submit button is pressed, the Action class's job is to check
by isValidToken().

If you do not use 2 actions or Forward to the form page, there will be problem in the first submission (thinks it is duplicate because it has no token) but subsequent duplicate submissions will be known.

One work around is to check if it is firstSubmission by checking for
org.apache.struts.taglib.html.TOKEN in the action class
if it is not set at all, that means it is not a duplicate form and should be allowed to run.
 
Greenhorn
Posts: 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

hi!
thanks for this post, helped me very much.
to test and understanding better, i printed in console the content of tokens before the test isTokenValid:
request.getSession().getAttribute("org.apache.struts.action.TOKEN"))
request.getParameter("org.apache.struts.taglib.html.TOKEN"))






 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
reply
    Bookmark Topic Watch Topic
  • New Topic