有时我们在项目框架搭建时并没有注意方重复提交的问题,在项目开发一半后发现许多地方需要方重复提交拦截功能,常规做法是在每个需要校验的请求接口中一一加上验证,但这样做的话工作量大并且代码入侵太严重如果后期需要改动那你会疯的。
有个简便的方法和大家分享下(自定义注解+拦截器),自用在需要防重复提交的接口中加上注解即可。
1.自定义注解只有两个方法,获取token和验证token
package com.***;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SubmitToken {
/**
* 获得token
* @return
*/
boolean save() default false;
/**
* 验证token有效性并使token失效
* @return
*/
boolean remove() default false;
}
2.拦截器两个功能,save()=true时生成token并存储在缓存中,remove()=true时验证token并清除缓存中的token
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.bingchuangapi.common.base.SubmitToken;
import com.bingchuangapi.common.constant.Constants;
import com.bingchuangapi.common.util.JsonUtil;
public class TokenInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOG = Logger.getLogger(SubmitToken.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
SubmitToken annotation = method.getAnnotation(SubmitToken.class);
if (annotation != null) {
boolean needSaveSession = annotation.save();
if (needSaveSession) {//在缓存中存储token
request.getSession(true).setAttribute("SubmitToken", UUID.randomUUID().toString());
}
boolean needRemoveSession = annotation.remove();
if (needRemoveSession) {//需要验证防重复提交
if (isRepeatSubmit(request)) {//符合重复提交
LOG.warn("please don't repeat submit,url:"+ request.getServletPath());
Map out = new HashMap();
out.put(Constants.RET_CODE, "900");
out.put(Constants.RET_MSG, "重复提交");
JsonUtil.writeJson(response,out);
return false;
}
request.getSession(true).removeAttribute("SubmitToken");
}
}
return true;
} else {
return super.preHandle(request, response, handler);
}
}
private boolean isRepeatSubmit(HttpServletRequest request) {
String serverToken = null;
serverToken = (String) request.getSession(true).getAttribute("SubmitToken");
//缓存中没有有效的token
if (serverToken == null) {
return true;
}
String clinetToken = request.getParameter("SUBMITTOKEN");
//未接收到有效的token
if (clinetToken == null) {
return true;
}
//缓存中的token和接收到的token没有匹配上
if (!serverToken.equals(clinetToken)) {
return true;
}
return false;
}
}
3.写一个获取token的请求提供给前台调用
//获取submitToken
@RequestMapping("/getSubmitToken.do")
@SubmitToken(save=true)//添加获取token的自定义注解
public ModelAndView getSubmitToken(HttpServletRequest request,HttpServletResponse response,Model model) throws Exception{
Map out = new HashMap();
out.put("SUBMITTOKEN", getSessionObj("SubmitToken"));
out.put(Constants.RET_CODE, Constants.RET_SUCCESS_CODE);
out.put(Constants.RET_MSG, Constants.RET_SUCCESS_MSG);
JsonUtil.writeJson(response,out);
return null;
}
4.后面只需要在我们需要防重复提交的请求中加入@SubmitToken(remove=true)即可
//验证submitToken
@RequestMapping("/setSubmitToken.do")
@SubmitToken(remove=true)
public ModelAndView setSubmitToken(HttpServletRequest request,HttpServletResponse response,Model model) throws Exception{
Map out = new HashMap();
out.put(Constants.RET_CODE, Constants.RET_SUCCESS_CODE);
out.put(Constants.RET_MSG, Constants.RET_SUCCESS_MSG);
JsonUtil.writeJson(response,out);
return null;
}
5.拦截器的配置文件
<!-- 拦截器配置 -->
<mvc:interceptors>
<!-- 配置Token拦截器,防止用户重复提交数据 -->
<mvc:interceptor>
<mvc:mapping path="/**"/><!--这个地方时你要拦截得路径 我这个意思是拦截所有得URL-->
<bean class="com.*****.TokenInterceptor"/><!--class文件路径改成你自己写得拦截器路径!! -->
</mvc:interceptor>
</mvc:interceptors>
完成以上几部就可以实现自定义注解少入侵的添加放重复提交,如果大家需要验证请求的合法性只需要修改拦截器中的代码逻辑即可实现。