由防止表单重复提交引发的一系列问题--servletRequest的复制、body值的获取

1、HttpServletRequestReplacedFilter 过滤器.
package com.kdgz.service; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException; /**
* @author QGuo
* @date 2019/1/3 15:04
*/
public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void destroy() {} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String contentType = request.getContentType(); if (contentType != null && contentType.contains("application/x-www-form-urlencoded")) {
//如果是application/x-www-form-urlencoded, 参数值在request body中以 a=1&b=2&c=3...形式存在,
//若直接构造BodyReaderHttpServletRequestWrapper,在将流读取并存到copy字节数组里之后,
//httpRequest.getParameterMap()将返回空值!
//若运行一下 httpRequest.getParameterMap(), body中的流将为空! 所以两者是互斥的!
request.getParameterMap();
}
if ("POST".equals(httpServletRequest.getMethod().toUpperCase())) {
requestWrapper = new BodyHttpServletRequestWrapper((HttpServletRequest) request);
}
} if (requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter((HttpServletRequest)requestWrapper, response);
}
} @Override
public void init(FilterConfig arg0) throws ServletException {}
}
HttpServletRequestReplacedFilter
package com.kdgz.service; import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.Map; /**
* @author QGuo
* @date 2019/1/3 15:05
*/
public class BodyHttpServletRequestWrapper extends HttpServletRequestWrapper { private byte[] body; public byte[] getBody() { return body; } public void setBody(byte[] body) { this.body = body; } public BodyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = this.getBodyString(request).getBytes(Charset.forName("UTF-8"));
} @Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream(),"UTF-8"));
} @Override
public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(this.body); return new ServletInputStream() {
@Override
public boolean isFinished() { return false; } @Override
public boolean isReady() { return false; } @Override
public void setReadListener(ReadListener readListener) {} @Override
public int read() throws IOException { return bais.read(); }
};
} @Override
public String getHeader(String name) { return super.getHeader(name); } @Override
public Enumeration<String> getHeaderNames() { return super.getHeaderNames(); } @Override
public Enumeration<String> getHeaders(String name) { return super.getHeaders(name); } @Override
public Map<String, String[]> getParameterMap() { return super.getParameterMap(); } public String getBodyString(ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
HttpServletRequestWrapper
3、web.xml 中添加过滤器
<filter>
<filter-name>httpServletRequestFilter</filter-name>
<filter-class>com.kdgz.service.HttpServletRequestReplacedFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpServletRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4、添加自定义注解
package com.kdgz.annotation; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* @author QGuo
* @date 2018/12/24 13:58
* 一个用户 相同url 同时提交 相同数据 验证
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SameUrlData {
}
@SameUrlData
5、添加拦截器
package com.kdgz.service; import com.alibaba.fastjson.JSON;
import com.kdgz.annotation.SameUrlData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit; /**
* 一个用户 相同url 同时提交 相同数据 验证
* 主要通过 session中保存到的url 和 请求参数。如果和上次相同,则是重复提交表单
*
* @author QGuo
* @date 2018/12/24 14:02
*/
public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {
@Resource
StringRedisTemplate stringRedisTemplate; @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
SameUrlData annotation = method.getAnnotation(SameUrlData.class);
if (annotation != null) {
if (repeatDataValidator(request)) {//如果重复相同数据
//在此可添加response响应内容,提醒用户重复提交了
return false;
} else
return true;
}
return true;
} else {
return super.preHandle(request, response, handler);
}
} /**
* 验证同一个url数据是否相同提交 ,相同返回true
*
* @param httpServletRequest
* @return
*/
public boolean repeatDataValidator(HttpServletRequest httpServletRequest) throws IOException {
Map<String, String[]> parameterMap = new HashMap(httpServletRequest.getParameterMap());
//删除参数中的v;(v参数为随机生成的字符串,目的是为了每次访问都是最新值,既然要防止重复提交,需要剔除此参数)
if (parameterMap.containsKey("v"))
parameterMap.remove("v");
//每一位登录者都有唯一一个token认证
String tokens = "";
if (parameterMap.get("token").length > 0)
tokens = parameterMap.get("token")[0];
String method = httpServletRequest.getMethod().toUpperCase();//请求类型,GET、POST
String params;
if (StringUtils.equals(method, "POST")) {//post请求时
BodyHttpServletRequestWrapper requestWrapper = new BodyHttpServletRequestWrapper((HttpServletRequest) httpServletRequest);
byte[] bytes = requestWrapper.getBody();
if (bytes.length != 0) {
params = JSON.toJSONString(new String(bytes, "UTF-8").trim());
} else {//若body被清空,则说明参数全部被填充到Parameter集合中了
/**
* 当满足一下条件时,就会被填充到parameter集合中
* 1:是一个http/https请求
* 2:请求方法是post
* 3:请求类型(content-Type)是application/x-www-form-urlencoded
* 4: Servlet调用了getParameter系列方法
*/
Map<String, String[]> map = new HashMap(requestWrapper.getParameterMap());
// 去除 v 参数
if (map.containsKey("v"))
map.remove("v");
params = JSON.toJSONString(map);
}
} else {
params = JSON.toJSONString(parameterMap);
} String url = String.valueOf(Objects.hashCode(httpServletRequest.getRequestURI() + tokens));
Map<String, String> map = new HashMap<String, String>();
map.put(url, params);
//防止参数过多,string过大;现将储存为 hash编码;
String nowUrlParams = String.valueOf(Objects.hashCode(map));
String preUrlParams = stringRedisTemplate.opsForValue().get(url);
if (preUrlParams == null) {//如果上一个数据为null,表示还没有访问页面
//设置过期时间为3分钟
stringRedisTemplate.opsForValue().set(url, nowUrlParams, 3, TimeUnit.MINUTES);
return false;
} else if (preUrlParams.equals(nowUrlParams)) {//否则,已经访问过页面
//如果上次url+数据和本次url+数据相同,则表示重复添加数据
return true;
} else {//如果上次 url+数据 和本次url加数据不同,则不是重复提交,更新
stringRedisTemplate.opsForValue().set(url, nowUrlParams, 3, TimeUnit.MINUTES);
return false;
}
}
}
SameUrlDataInterceptor
@RequestMapping(value = "v1.0/monGraphSave")
@SameUrlData
public AjaxMessage monGraphSave(@RequestBody MonGraphFB monGraphFB){}
由防止表单重复提交引发的一系列问题--servletRequest的复制、body值的获取的更多相关文章
- Spring Boot (一) 校验表单重复提交
一.前言 在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据. 存在如上问题可以交给前端解决,判断多长时间内不能再次点 ...
- 12、Struts2表单重复提交
什么是表单重复提交 表单的重复提交: 若刷新表单页面, 再提交表单不算重复提交. 在不刷新表单页面的前提下: 多次点击提交按钮 已经提交成功, 按 "回退" 之后, 再点击 &qu ...
- java防止表单重复提交
用session防止表单重复提交 思路:在服务器端生成一个唯一的随机标识串Token,同时在当前用户的Session域中保存这个Token.然后将Token发送到客户端的Form表单中,在Form表单 ...
- java web学习总结(十三) -------------------使用Session防止表单重复提交
在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交. 一.表单重复提 ...
- 使用Struts 2防止表单重复提交
用户重复提交表单在某些场合将会造成非常严重的后果.例如,在使用信用卡进行在线支付的时候,如果服务器的响应速度太慢,用户有可能会多次点击提交按钮,而这可能导致那张信用卡上的金额被消费了多次.因此,重复提 ...
- js阻止form表单重复提交
防止表单重复提交的方法总体来说有两种,一种是在js中阻止重复提交:另一种是在后台利用token令牌实现,大致思路是生成一个随机码放到session和form表单的隐藏输入框中,提交表单时两者对比,表单 ...
- PHP简单利用token防止表单重复提交
<?php /* * PHP简单利用token防止表单重复提交 * 此处理方法纯粹是为了给初学者参考 */ session_start(); function set_token() { $_S ...
- token防止表单重复提交
出现表单重复提交的三种情况: 一.服务器响应缓慢,用户多次点击提交按钮. 二.提交成功后刷新页面. 三.提交成功后返回表单页面再次点击提交. package com.jalja.token; impo ...
- JavaWeb防止表单重复提交(转载)
转载自:http://blog.csdn.net/ye1992/article/details/42873219 在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用 ...
随机推荐
- jQuery ajax在IE浏览器的跨域问题--jquery.xdomainrequest.min.js
jquery.ajax 加载数据, chrome, firefox, IE10+ 都可以顺利加载数据,但是IE9及以后版本不执,通过执行 jquery.ajax error 函数显示未执行 拒绝访问. ...
- Linux命令locate
centos安装locate命令 centos6.3刚初始化安装完毕,有个配置文件不知道存在什么地方,想用locate命令来查找下,发现系统提示,找不到该命令.以前经常用的命令为什么找不到了呢???原 ...
- redis在项目中的使用(单机版、集群版)
1.下载jar包:jedis-2.6.2.jar 2.代码: JedisDao.java: package com.test.www.dao; public interface JedisDao { ...
- SQL Server查询数据库空间分配情况、数据库备份信息
查询数据库空间分配情况: use master go create procedure dbo.proc_getdbspaceused as begin set nocount on create t ...
- rem 、em
介绍:rem是相对字体单位:根据html根元素大小而定,同样可作为宽高等单位: 适配原理:将px替换成rem,采用rem适配移动web的原理,根据不同屏幕宽度设置html的font-size的大小: ...
- MyBatis数据持久化(一)准备工作
MyBatis简介 mybatis的前生是ibatis,它是一款非常优秀的java持久层框架,所有sql语句写在配置文件中,和另外一款比较知名的orm框架hibernate比起来显得更加小巧灵活,也是 ...
- 脱离node自己使用普通的requirejs管理js资源
首先,工程目录: 现在主页面(web框架写法.html): <!DOCTYPE html> <html lang="en"> <head> &l ...
- codeforces 493 D Vasya and Chess【 博弈 】
题意:给出n*n的棋盘,白方在(1,1),黑方在(1,n)处,每一步可以上下左右对角线走,哪个先抓到另一个,则它获胜 可以画一下,发现n是奇数的时候,白方先走,无论它怎么走,黑方和它走对称的,黑方都一 ...
- SpringCloud学习笔记(17)----Spring Cloud Netflix之服务网关Zuul的使用
1. 什么时候Zuul? Zuul是一个基于jvm路由和服务端的负载均衡器,在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架. 路由功能:相当于nginx的反向代理 比如: / 可能需要映射到 ...
- 【jQuery02】点击标题面板显示内容
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...