You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@struts.apache.org by Brad Balmer <bb...@peapod.com> on 2005/04/09 23:22:33 UTC

Use of tokens

Do people use tokens to disable multiple requests?  I have pages that use
images to submit forms, thus I cannot use javascript to disable the submit
on multiple clicks.  I was reading about tokens but couldn't find a full
example except the three functions to call.

If you can use tokens to disable multiple submits, does anybody have a full
working example of how to use them?

Thanks.


---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Use of tokens

Posted by Erik Weber <er...@mindspring.com>.
Yes, tokens are used to prevent duplicate submits of a single form.

These are the four methods your Action inherits from 
org.apache.struts.action.Action:


   protected String generateToken(HttpServletRequest request) {
        return token.generateToken(request);
    }

    protected boolean isTokenValid(HttpServletRequest request) {
        return token.isTokenValid(request, false);
    }

    protected void resetToken(HttpServletRequest request) {
        token.resetToken(request);
    }

    protected void saveToken(HttpServletRequest request) {
        token.saveToken(request);
    }

token is a singleton instance of org.apache.struts.util.TokenProcessor. 
As you can see, these methods are just wrappers.

Here are the TokenProcessor implementations:

    public synchronized String generateToken(HttpServletRequest request) {

        HttpSession session = request.getSession();
        try {
            byte id[] = session.getId().getBytes();
            long current = System.currentTimeMillis();
            if (current == previous) {
                current++;
            }
            previous = current;
            byte now[] = new Long(current).toString().getBytes();
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(id);
            md.update(now);
            return toHex(md.digest());
        } catch (NoSuchAlgorithmException e) {
            return null;
        }

    }

    public synchronized boolean isTokenValid(
        HttpServletRequest request,
        boolean reset) {

        // Retrieve the current session for this request
        HttpSession session = request.getSession(false);
        if (session == null) {
            return false;
        }

        // Retrieve the transaction token from this session, and
        // reset it if requested
        String saved = (String) 
session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
        if (saved == null) {
            return false;
        }

        if (reset) {
            this.resetToken(request);
        }

        // Retrieve the transaction token included in this request
        String token = request.getParameter(Constants.TOKEN_KEY);
        if (token == null) {
            return false;
        }

        return saved.equals(token);
    }

    public synchronized void resetToken(HttpServletRequest request) {

        HttpSession session = request.getSession(false);
        if (session == null) {
            return;
        }
        session.removeAttribute(Globals.TRANSACTION_TOKEN_KEY);
    }

    public synchronized void saveToken(HttpServletRequest request) {

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

    }

And finally, this method is part of org.apache.struts.taglib.html.FormTag:

    protected String renderToken() {
        StringBuffer results = new StringBuffer();
        HttpSession session = pageContext.getSession();

        if (session != null) {
            String token =
                (String) 
session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
               
            if (token != null) {
                results.append("<input type=\"hidden\" name=\"");
                results.append(Constants.TOKEN_KEY);
                results.append("\" value=\"");
                results.append(token);
                if (this.isXhtml()) {
                    results.append("\" />");
                } else {
                    results.append("\">");
                }
            }
        }

        return results.toString();
    }


Typically, in your setup Action's execute method, you will put tokens in 
place. A unique token will be saved as a HttpSession attribute. A copy 
of this token is inserted as a hidden form variable (this is done for 
you by the html:form tag) and goes to the user with the form. When the 
user submits the form, in comes the token -- it serves as a unique ID 
for a form. In your form processing Action's execute method, the first 
thing you will do is check the validity of this incoming token, by 
comparing it with the expected token (the HttpSession attribute). As 
soon as you finish checking the validity of the token (whether pass or 
fail), you should eliminate the token from the HttpSession attributes 
(the check and reset should be atomic as an execute method allows 
concurrent access -- there is a synchronized method of the singleton 
that provides this) -- that token has been processed and is no longer 
useful. Now if a repeat submit arrives with a duplicate form ID, the 
validity check will fail, as the HttpSession no longer carries that 
value as an attribute -- it either carries a new value or no value at 
all. This ensures that the form processing Action can only occur once 
for a given form.

A typical way do it:

Setup Action.execute:
{
  . . .
  //setup form/populate if necessary
  . . .
  saveToken(request); // generates token; inserts HttpSession attribute;
  //form tag will use this when sending the response
  //that contains the form, including it as a hidden variable
  return mapping.findForward("success");
}


Form processing Action.execute:
{
 . . .
  //check credentials, etc.
 . . .
  //check and reset token as an atomic operation
  if (!isTokenValid(request, true)) throw new Exception("invalid 
transaction token");
 . . .
  //optional -- saveToken(request);
  //token is valid, continue processing
  //optional -- resetToken(request);
  . . .
}


Personally I prefer to use a token processor that uses 
parameter/attribute keys that are more specific (not one key for all 
forms but one key per logical form). This allows the user to have more 
than one form "checked out" and to submit them in any order, but still 
prevents duplicate submits of the same form. But the idea is basically 
the same.

Also, there can be some trickiness involved if the "continue processing" 
block of code above might encounter a manager-layer validation failure, 
and declarative Exception handling directs the user back to the form -- 
this will be a "new" form, so you will need to embed fresh tokens, or 
the user will correct his input, resubmit the form and encounter a 
duplicate submit error and then get really frustrated (see "optional" 
code above).

Erik




Brad Balmer wrote:

>Do people use tokens to disable multiple requests?  I have pages that use
>images to submit forms, thus I cannot use javascript to disable the submit
>on multiple clicks.  I was reading about tokens but couldn't find a full
>example except the three functions to call.
>
>If you can use tokens to disable multiple submits, does anybody have a full
>working example of how to use them?
>
>Thanks.
>
>
>---------------------------------------------------------------------
>To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
>For additional commands, e-mail: user-help@struts.apache.org
>
>
>  
>

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Use of tokens

Posted by Phil Steitz <ph...@gmail.com>.
It's a toy example, but the mailreader sample application included in
the struts distribution demonstrates the use of tokens.

Phil

On Apr 9, 2005 5:22 PM, Brad Balmer <bb...@peapod.com> wrote:
> Do people use tokens to disable multiple requests?  I have pages that use
> images to submit forms, thus I cannot use javascript to disable the submit
> on multiple clicks.  I was reading about tokens but couldn't find a full
> example except the three functions to call.
> 
> If you can use tokens to disable multiple submits, does anybody have a full
> working example of how to use them?
> 
> Thanks.
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
> For additional commands, e-mail: user-help@struts.apache.org
> 
>

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org