由防止表单重复提交引发的一系列问题--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 在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用 ...
随机推荐
- 关于lncRNA数据收集
最近需要自己收集数据库里的核酸序列,于是直接面对一些神文 http://www.360doc.com/content/17/0120/08/30227855_623625901.shtml http: ...
- 【算法】第二类斯特林数Stirling
第二类Stirling数实际上是集合的一个拆分,表示将n个不同的元素拆分成m个集合的方案数,记为 或者 . 第二类Stirling数的推导和第一类Stirling数类似,可以从定义出发考虑第n+1个元 ...
- div内快元素[div,p。。。]居中办法
方法1: .parent { width:800px; height:500px; border:2px solid #000; position:relative; } .child { width ...
- Generating SSH Keys for github
由于最近电脑重装了Windows 8.1, 想用github维护一些代码.故不得不重新生成一下ssh key. 按https://help.github.com/articles/generating ...
- POJ 3660 Cow Contest【传递闭包】
解题思路:给出n头牛,和这n头牛之间的m场比赛结果,问最后能知道多少头牛的排名. 首先考虑排名怎么想,如果知道一头牛打败了a头牛,以及b头牛打赢了这头牛,那么当且仅当a+b+1=n时可以知道排名,即为 ...
- 修改input标签中的placeholder样式
input::-webkit-input-placeholder { color: #fff !important; } input:-moz-placeholder { color: #fff !i ...
- php获取当前月份的前(后)几个月
//获取当前月份的前一月 function GetMonth($sign) { //得到系统的年月 $tmp_date=date("Ym"); //切割出年份 $tmp_year= ...
- category和关联对象
如上所见,我们知道在category里面是无法为category添加实例变量的.但是我们很多时候需要在category中添加和对象关联的值,这个时候可以求助关联对象来实现. MyClass+Categ ...
- 联想lenovo 家用电脑安装win7 无法引导问题(新电脑安装系统时提示File:\Boot\BCD错误解决方案)
安装方式 : 1.进入 PE 2.用 EasyimageX 恢复 GHO镜像 3.重启后出现 原因: 主要 是安装win7 时,格式 化选择为GUID模式. 处理: win7 以后,格式华时选择MB ...
- [Python随笔]>>range()函数?
因为自己在考核的时候没有记清range()函数的具体用法,所以特意去查了下 Python range() 函数用法 python range() 函数可创建一个整数列表,一般用在 for 循环中 函数 ...