分享知识 传递快乐

 

防止表单重复提交有多种,经常用到的有两种:

 

一种是Token(令牌),这种可以用于from表单,但用于ajax就有点力不从心了;

另一种是对请求的url和参数验证,如果一样就不提交,否则提交(推荐)。

 

代码如下:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xh.token</groupId>
<artifactId>prevent-resubmit</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>prevent-resubmit Maven Webapp</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<spring.version>4.3.13.RELEASE</spring.version>
<fastjson.version>1.2.46</fastjson.version>
<log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.25</slf4j.version>
<servlet.version>3.1.0</servlet.version>
<ehcache.version>2.6.11</ehcache.version>
<jackson.version>2.9.0</jackson.version>
</properties>


<dependencies>
<!-- 通用包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>

<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>


<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>


<!--日志 start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>

<!-- servlet 如果用不到jsp个人认为没必要引用 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<!-- ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>${ehcache.version}</version>
</dependency>

<!-- jackson begin -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>

</dependencies>

<build>
<finalName>prevent-resubmit</finalName>
</build>
</project>

 

java核心代码:

 

SameUrlDataInterceptor

package com.xh.token.commons.same;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

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

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSON;

/**
* <p>Title: 一个用户 相同url 同时提交 相同数据 验证 主要通过 session中保存到的url 和 请求参数。如果和上次相同,则是重复提交表单 </p>
* <p>Description: </p>
*
* @author H.Yang
* @QQ 1033542070
* @date 2018年3月7日
*/
public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {

private final Logger LOGGER = LogManager.getLogger(getClass());
private static HttpSession session = null;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
session = request.getSession();
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
SameUrlData annotation = method.getAnnotation(SameUrlData.class);

// LOGGER.info("annotation=" + annotation);

if (annotation != null) {
// 如果重复相同数据
if (repeatDataValidator(request)) {
// 重定向到登录页面
// LOGGER.info("请求地址:" + session.getAttribute("oldUrl"));
response.sendRedirect(request.getContextPath() +session.getAttribute("oldUrl"));
session.removeAttribute("oldUrl");
session.removeAttribute("repeatData");
return false;
} else {
return true;
}
}
// LOGGER.info("保存请求地址:" + request.getServletPath());
session.setAttribute("oldUrl", request.getServletPath());
return true;
} else {
// LOGGER.info("handler=" + handler);
return super.preHandle(request, response, handler);
}
}

/**
* 验证同一个url数据是否相同提交 ,相同返回true
* @param request
* @return
*/
public boolean repeatDataValidator(HttpServletRequest request) {
String params = JSON.toJSONString(request.getParameterMap());
// LOGGER.info("params=" + params);
String url = request.getRequestURI();
// LOGGER.info("url=" + url);
Map<String, String> map = new HashMap<String, String>();
map.put(url, params);
String nowUrlParams = map.toString();//

Object preUrlParams = session.getAttribute("repeatData");
// 如果上一个数据为null,表示还没有访问页面
if (preUrlParams == null) {
LOGGER.info("未提交数据");
session.setAttribute("repeatData", nowUrlParams);
return false;
} else {
// 否则,已经访问过页面
// 如果上次url+数据和本次url+数据相同,则表示城府添加数据
if (preUrlParams.toString().equals(nowUrlParams)) {
LOGGER.info("已经提交数据 ");
return true;
} else {
LOGGER.info("两次提交的数据不一至,可以提交");
// 如果上次 url+数据 和本次url加数据不同,则不是重复提交
session.setAttribute("repeatData", nowUrlParams);
return false;
}

}
}
}

 

controller

package com.xh.token.controller;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.xh.token.commons.same.SameUrlData;

@Controller
public class LoginController {

// 控制器本来就是单例,这样似乎更加合理
private final Logger LOGGER = LogManager.getLogger(getClass());

@GetMapping("/login")
public String login() {
LOGGER.info("GET请求登录");
return "login";
}

@PostMapping("/login2")
@SameUrlData
public String login(HttpServletRequest request, String name, String password) {
LOGGER.info("from请求如下:name=" + name + "\t password=" + password);

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "redirect:/login";
}

}

 

 

 

源码:​​点击打开链接​