/*
* Copyright 2012 Anton Van Zyl. http://code.google.com/p/java-swiss-knife/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package com.knife.web.threading;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import sun.misc.BASE64Encoder;
import com.knife.web.threading.exception.InvalidRequestTokenException;
/**
* This is prevention class to enable us to prevent a double submit in critical
* section, for instance; a payment in a banking web application.<br/>
* The idea around this is using a unique key in a flow and only allowing a flow
* to continue with the unique key, if the flow does not contain the unique key,
* the user is seen as a double submit, and the input is not accepted and the
* output is handles, be it a error screen or going to a result page(which would
* bring up other problems with data, overcome this by rather using
* DoubleSubmitLock).
*
* <br/>
* <br/>
* Please visit <a
* href="http://code.google.com/p/java-swiss-knife/">Java-Swiss-Knife</a> and
* comment, rate, contribute or raise a issue/enhancement for my library. <br/>
*
* @author Anton Van Zyl
*
*/
public class DoubleSubmitBlock {
/**
* Generates a random Token and adds it to the session to be used in
* validating incoming requests.<br/>
* Use the validateUniqueRequestIdentifier method to validate the key value
* against the session.<br/>This is a thread safe method.
* <b>SessionKey = requestIdentifierSessionKey</b>
*
* @param request - The <code>HttpServletRequest</code> as input
* @param requestIdentifierSessionKey - Sets the Unique key in the session with <code>UniqueRequestIdentifier</code> as the key
*/
public static void setUniqueRequestIdenitfier(HttpServletRequest request, String requestIdentifierSessionKey) throws NoSuchAlgorithmException {
synchronized (request.getSession()) {
// Generate the SHA1 encoded String
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] bytes = new byte[30];
sr.nextBytes(bytes);
request.getSession().setAttribute(requestIdentifierSessionKey, String.valueOf(new BASE64Encoder().encode(bytes)));
}
}
/**
* This will validate the key in the session and if invalid throw an
* exception to be handled.<br/> This is a thread safe method.
*
* @param key - the input that came from the page for the session key
* @param request - The <code>HttpServletRequest</code> as input
* @param requestIdentifierSessionKey - Sets the Unique key in the session with <code>UniqueRequestIdentifier</code> as the key
* @throws InvalidRequestTokenException
* @throws InterruptedException
*/
public static void validateUniqueRequestIdentifier(String key, HttpServletRequest request, String requestIdentifierSessionKey) throws InvalidRequestTokenException,
InterruptedException {
synchronized (request.getSession()) {
if (!StringUtils.equals(key, String.valueOf(request.getSession().getAttribute(requestIdentifierSessionKey)))) {
throw new InvalidRequestTokenException("User does not have token in session");
}
}
}
/**
* This will validate the key in the session and if invalid throw an
* exception to be handled.<br/>
* requestIdentifierSessionKey is removed after a valid request key is
* found. This is a thread safe method.
*
* @param key - the input that came from the page for the session key
* @param request - The <code>HttpServletRequest</code> as input
* @param requestIdentifierSessionKey - Sets the Unique key in the session with <code>UniqueRequestIdentifier</code> as the key
* @throws InvalidRequestTokenException
* @throws InterruptedException
*/
public static void validateAndRemoveUniqueRequestIdentifier(String key, HttpServletRequest request, String requestIdentifierSessionKey) throws InvalidRequestTokenException,
InterruptedException {
synchronized (request.getSession()) {
if (!StringUtils.equals(key, String.valueOf(request.getSession().getAttribute(requestIdentifierSessionKey)))) {
throw new InvalidRequestTokenException("User does not have token in session");
}
request.getSession().removeAttribute(requestIdentifierSessionKey);
}
}
}