avoid duplicate submission 结合spring-mvc的防重复提交

采用Annotation和Interceptor方式提供切面拦截,这个技术一直在用,仅是记录一下。注意此种方法只适用于页面支持刷新提交的流程,如果是打开静态页添加数据进行多次提交的,需要扩展处理token问题。


首先提供一个自定义的注释 AvoidDuplicateSubmission。注释提供两个方法:needSaveToken() 和 needRemoveToken()。 从字面就可以知道,在进入时增加token,提交后删除token

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 避免重复提交Annotation
 * 提交页面或者表单进入时增加 needSaveToken=true,表单提交后增加 needRemoveToken=true
 * @author Paris
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidDuplicateSubmission {
	 boolean needSaveToken() default false;
	 boolean needRemoveToken() default false;
}

第二步增加拦截器AvoidDuplicateSubmissionInterceptor

类成员tokenKey有个默认值token,可以通过spring bean属性注入的方式根据实际情况改动。

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.chinesedreamer.stocks.common.annotation.AvoidDuplicateSubmission;
import com.chinesedreamer.stocks.common.generator.TokenProcessor;

/**
 * 避免重复提交拦截器
 * 
 * @author Paris
 *
 */
public class AvoidDuplicateSubmissionInterceptor extends HandlerInterceptorAdapter {

	private String tokenKey = "token";

	public String getTokenKey() {
		return tokenKey;
	}

	public void setTokenKey(String tokenKey) {
		this.tokenKey = tokenKey;
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();

		AvoidDuplicateSubmission annotation = method.getAnnotation(AvoidDuplicateSubmission.class);
		if (null != annotation) {
			boolean needSaveToken = annotation.needSaveToken();
			if (needSaveToken) {
				request.getSession(false).setAttribute(this.tokenKey,
						TokenProcessor.getInstance().generateToken(request));
			}

			boolean needRemoveToken = annotation.needRemoveToken();
			if (needRemoveToken) {
				if (needRemoveToken) {
					if (isRepeatSubmit(request)) {
						return false;
					}
					request.getSession(false).removeAttribute(this.tokenKey);
				}
			}
		}
		return true;
	}

	private boolean isRepeatSubmit(HttpServletRequest request) {
		String serverToken = (String) request.getSession(false).getAttribute(this.tokenKey);
		if (null == serverToken) {
			return true;
		}

		String clientToken = request.getParameter(this.tokenKey);
		if (null == clientToken) {
			return true;
		}

		if (!serverToken.equals(clientToken)) {
			return true;
		}

		return false;
	}
}


这里用到里一个token的生成器
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * token生成器
 * @author Paris
 *
 */
public class TokenProcessor {

	private static TokenProcessor instance = new TokenProcessor();

	public static TokenProcessor getInstance() {
		return instance;
	}

	protected TokenProcessor() {
		super();
	}

	public String generateToken(HttpServletRequest request) {

		HttpSession session = request.getSession();
		try {
			byte id[] = session.getId().getBytes();
			byte now[] = new Long(System.currentTimeMillis()).toString().getBytes();
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(id);
			md.update(now);
			return this.toHex(md.digest());

		} catch (IllegalStateException e) {
			return null;
		} catch (NoSuchAlgorithmException e) {
			return null;
		}

	}

	public String toHex(byte buffer[]) {
		StringBuffer sb = new StringBuffer();
		String s = null;
		for (int i = 0; i < buffer.length; i++) {
			s = Integer.toHexString((int) buffer[i] & 0xff);
			if (s.length() < 2) {
				sb.append('0');
			}
			sb.append(s);
		}
		return sb.toString();
	}

}

根据tokenKey保持前后台一致即可以完成拦截操作

猜你喜欢

转载自blog.csdn.net/u013275741/article/details/49634643