001: /*
002: * Copyright (c) 2002-2003 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.webwork.interceptor;
006:
007: import java.util.Map;
008:
009: import com.opensymphony.webwork.util.TokenHelper;
010: import com.opensymphony.xwork.ActionContext;
011: import com.opensymphony.xwork.ActionInvocation;
012: import com.opensymphony.xwork.ValidationAware;
013: import com.opensymphony.xwork.interceptor.MethodFilterInterceptor;
014: import com.opensymphony.xwork.util.LocalizedTextUtil;
015:
016: /**
017: * <!-- START SNIPPET: description -->
018: *
019: * Ensures that only one request per token is processed. This interceptor can make sure that back buttons and double
020: * clicks don't cause un-intended side affects. For example, you can use this to prevent careless users who might double
021: * click on a "checkout" button at an online store. This interceptor uses a fairly primitive technique for when an
022: * invalid token is found: it returns the result <b>invalid.token</b>, which can be mapped in your action configuration.
023: * A more complex implementation, {@link TokenSessionStoreInterceptor}, can provide much better logic for when invalid
024: * tokens are found.
025: *
026: * <p/>
027: *
028: * <b>Note:</b> To set a token in your form, you should use the <b>token tag</b>. This tag is required and must be used
029: * in the forms that submit to actions protected by this interceptor. Any request that does not provide a token (using
030: * the token tag) will be processed as a request with an invalid token.
031: *
032: * <p/>
033: *
034: * <b>Internationalization Note:</b> The following key could be used to internationalized the action errors generated
035: * by this token interceptor
036: *
037: * <ul>
038: * <li>webwork.messages.invalid.token</li>
039: * </ul>
040: *
041: * <p/>
042: *
043: * <b>NOTE:</b> As this method extends off MethodFilterInterceptor, it is capable of
044: * deciding if it is applicable only to selective methods in the action class. See
045: * <code>MethodFilterInterceptor</code> for more info.
046: *
047: * <!-- END SNIPPET: description -->
048: *
049: * <p/> <u>Interceptor parameters:</u>
050: *
051: * <!-- START SNIPPET: parameters -->
052: *
053: * <ul>
054: *
055: * <li>None</li>
056: *
057: * </ul>
058: *
059: * <!-- END SNIPPET: parameters -->
060: *
061: * <p/> <u>Extending the interceptor:</u>
062: *
063: * <p/>
064: *
065: * <!-- START SNIPPET: extending -->
066: *
067: * While not very common for users to extend, this interceptor is extended by the {@link TokenSessionStoreInterceptor}.
068: * The {@link #handleInvalidToken} and {@link #handleValidToken} methods are protected and available for more
069: * interesting logic, such as done with the token session interceptor.
070: *
071: * <!-- END SNIPPET: extending -->
072: *
073: * <p/> <u>Example code:</u>
074: *
075: * <pre>
076: * <!-- START SNIPPET: example -->
077: *
078: * <action name="someAction" class="com.examples.SomeAction">
079: * <interceptor-ref name="token"/>
080: * <interceptor-ref name="basicStack"/>
081: * <result name="success">good_result.ftl</result>
082: * </action>
083: *
084: * <-- In this case, myMethod of the action class will not
085: * get checked for invalidity of token -->
086: * <action name="someAction" class="com.examples.SomeAction">
087: * <interceptor-ref name="token">
088: * <param name="excludeMethods">myMethod</param>
089: * </interceptor-ref name="token"/>
090: * <interceptor-ref name="basicStack"/>
091: * <result name="success">good_result.ftl</result>
092: * </action>
093: *
094: * <!-- END SNIPPET: example -->
095: * </pre>
096: *
097: * @author Jason Carreira
098: * @author Rainer Hermanns
099: * @author Nils-Helge Garli
100: * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
101: * @see TokenSessionStoreInterceptor
102: * @see TokenHelper
103: *
104: * @version $Date: 2007-01-08 17:24:16 +0100 (Mon, 08 Jan 2007) $ $Id: TokenInterceptor.java 2795 2007-01-08 16:24:16Z tmjee $
105: */
106: public class TokenInterceptor extends MethodFilterInterceptor {
107:
108: private static final long serialVersionUID = 9021366162909706309L;
109:
110: public static final String INVALID_TOKEN_CODE = "invalid.token";
111:
112: /**
113: * @see com.opensymphony.xwork.interceptor.MethodFilterInterceptor#doIntercept(com.opensymphony.xwork.ActionInvocation)
114: */
115: protected String doIntercept(ActionInvocation invocation)
116: throws Exception {
117: if (log.isDebugEnabled()) {
118: log
119: .debug("Intercepting invocation to check for valid transaction token.");
120: }
121:
122: Map session = ActionContext.getContext().getSession();
123:
124: synchronized (session) {
125: if (!TokenHelper.validToken()) {
126: return handleInvalidToken(invocation);
127: }
128:
129: return handleValidToken(invocation);
130: }
131: }
132:
133: /**
134: * Determines what to do if an invalida token is provided. If the action implements {@link ValidationAware}
135: *
136: * @param invocation the action invocation where the invalid token failed
137: * @return the return code to indicate should be processed
138: * @throws Exception when any unexpected error occurs.
139: */
140: protected String handleInvalidToken(ActionInvocation invocation)
141: throws Exception {
142: Object action = invocation.getAction();
143: String errorMessage = LocalizedTextUtil
144: .findText(
145: this .getClass(),
146: "webwork.messages.invalid.token",
147: invocation.getInvocationContext().getLocale(),
148: "The form has already been processed or no token was supplied, please try again.",
149: new Object[0]);
150:
151: if (action instanceof ValidationAware) {
152: ((ValidationAware) action).addActionError(errorMessage);
153: } else {
154: log.warn(errorMessage);
155: }
156:
157: return INVALID_TOKEN_CODE;
158: }
159:
160: /**
161: * Called when a valid token is found. This method invokes the action by can be changed to do something more
162: * interesting.
163: *
164: * @param invocation the action invocation
165: * @throws Exception when any unexpected error occurs.
166: */
167: protected String handleValidToken(ActionInvocation invocation)
168: throws Exception {
169: return invocation.invoke();
170: }
171: }
|