后台重复提交表单拦截器(Springboot或者ssm解决方案)

486

1.首先编写自定义注解

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

/**
 * @author NealZhi
 * @version 1.0.0
 * @Description: 阻止表单提交自定义注解
 * @date 2019-08-19
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventRepeat
{
}

2.继承HandlerInterceptorAdapter类并且重写方法

package com.test.admin.filter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.admin.annotation.PreventRepeat;
import com.test.common.utils.MD5Utils;
import com.test.common.utils.R;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author NealZhi
 * @version 1.0.0
 * @Description: 相同url和数据拦截器 为了防止重复提交等操作
 * @date 2019-08-19
 */
@Component
public class SameUrlDataInterceptor extends HandlerInterceptorAdapter
{
	public final Logger log = Logger.getLogger(this.getClass());

	/**
	 * 覆盖父类的preHandle方法
	 * 预处理回调方法,实现处理器的预处理,验证是否为重复提交,第三个参数为响应的处理器,自定义Controller
	 * 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
	 *
	 * @param request
	 * @param response
	 * @param handler
	 * @return
	 * @throws Exception
	 */
	@Override public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception
	{

		// 1. 判断handler参数是否为HandlerMethod类的实例
		if (handler instanceof HandlerMethod)
		{

			// 2. 获取方法注解查看方式是否有PreventRepeat注解
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			Method method = handlerMethod.getMethod();
			PreventRepeat annotation = method
					.getAnnotation(PreventRepeat.class);
			PrintWriter out = null;
			if (annotation != null)
			{

				boolean lock = true;

				//计时10秒内重复提交会进行锁定
				Object timekeeper = request.getSession()
						.getAttribute("timekeeper");

				if (timekeeper == null) {
					request.getSession()
							.setAttribute("timekeeper",System.currentTimeMillis());
				}else
				{
					Long lockTime = (Long)timekeeper;
					if (lockTime+10000L < System.currentTimeMillis() ) {
						log.info("重复提交锁释放");
						request.getSession().setAttribute("timekeeper", null);
						lock = false;
					}
				}

				// 3. 调用重复数据验证方法
				boolean result = repeatDataValidator(request);
				if (result&&lock)
				{
					R r = new R();
					response.setCharacterEncoding("UTF-8");
					response.setContentType("application/json; charset=utf-8");
					out = response.getWriter();
					out.append(JSON.toJSONString(R.error("请不要重复提交")));
					log.info("重复提交被拦截了");
					return false;
				}
				else
				{
					return true;
				}

			}
			else
			{
				return true;
			}
		}
		else
		{

			// 4. 如果参数不是HandlerMethod类的实例则调用父类的preHandle方法
			return super.preHandle(request, response, handler);
		}
	}

	/**
	 * 验证同一个url数据是否相同提交,相同返回true
	 *
	 * @param httpServletRequest
	 * @return
	 */
	public boolean repeatDataValidator(HttpServletRequest httpServletRequest)
			throws Exception
	{

		try
		{

			// 1. 将请求参数转换为json字符串 需要在pom内引用jackson-databind
			ObjectMapper objectMapper = new ObjectMapper();

			String params = "";


			//区分提交类型是否为表单形式,如果是则根据文件流判断
			if (httpServletRequest.getHeader("content-type")
					.contains("form-data")) {
				params = MD5Utils.getMD5String(
						JSON.toJSONString(httpServletRequest.getParts()));
			}else
			{
				params = objectMapper.writeValueAsString(
						httpServletRequest.getParameterMap());
			}



			// 2. 获取当前请求的url地址 并以url为key 参数为值存在map内
			String url = httpServletRequest.getRequestURI();
			Map<String, String> map = new HashMap(4);
			map.put(url, params);
			String nowUrlParams = map.toString();

			// 3. 获取session中上一次请求存储的url和参数字符串
			Object preUrlParams = httpServletRequest.getSession()
					.getAttribute("oldUrlParams");

			// 4. 如果上一个数据为null,表示还没有访问页面 将当前方位的url和请求参数存储到session中
			if (preUrlParams == null)
			{
				httpServletRequest.getSession()
						.setAttribute("oldUrlParams", nowUrlParams);
				return false;
			}
			else
			{
				// 5. 判断上一次访问的url和参数与本次是否相同 如相同则表示重复数据
				if (preUrlParams.toString().equals(nowUrlParams))
				{
					return true;
				}
				else
				{
					httpServletRequest.getSession()
							.setAttribute("oldUrlParams", nowUrlParams);
					return false;
				}

			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
			// 此处是我自定义异常
			throw new RuntimeException("验证是否为重复请求时出错了!");
		}
	}
}

3.将监听器交于spring监管

#### Springboot处理方法
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    @Autowired
    private GlobalInterceptor globalInterceptor;

    public static void main(String[] args) {
        SpringApplication.run(SuperrescueReportingApplication.class, args);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ReportInterceptor()).addPathPatterns("/**");
	//将globalInterceptor交于spring托管
        registry.addInterceptor(globalInterceptor);
        super.addInterceptors(registry);
    }
}
### ssm处理方法
<mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.thinkgem.jeesite.common.repeat_form_validator.SameUrlDataInterceptor"/>
        </mvc:interceptor>