java客户端重复请求_Java后台防止客户端重复请求、提交表单实现原理

Java后台防止客户端重复请求、提交表单实现原理

发布于 2021-1-8|

复制链接

摘记: 这篇文章主要介绍了Java后台防止客户端重复请求、提交表单实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下前言

在Web / App项目中,有一些请求或操作会对数据产生影响(比如新增、删除、修改),针对这类请求一般都需要做一些 ..

这篇文章主要介绍了Java后台防止客户端重复请求、提交表单实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下前言在Web / App项目中,有一些请求或操作会对数据产生影响(比如新增、删除、修改),针对这类请求一般都需要做一些保护,以防止用户有意或无意的重复发起这样的请求导致的数据错乱。常见处理方案

1.客户端  例如表单提交后将提交按钮设为disable 等等方法...2.服务端  前端的限制仅能解决少部分问题,且不够彻底,后端自有的防重复处理措施必不可少,义不容辞。  在此提供一个我在项目中用到的方案。简单来说就是判断请求url和数据是否和上一次相同。方法步骤

1.主要逻辑:  给所有的url加一个拦截器,每次请求将url存入session,下次请求验证url数据是否相同,相同则拒绝访问。  当然,我在此基础上做了一些优化,比如:    使用session有局限性,用户量大了以后服务器会撑不住,在此我使用了redis来替换。    加入了token令牌机制。2.实现步骤:2.1自定义一个注解

```java

/**

* @Title: SameUrlData

* @Description: 自定义注解防止表单重复提交

* @Auther: xhq

* @Version: 1.0

* @create 2019/3/26 10:43

*/

@Inherited

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface SameUrlData {

}

```

2.2自定义拦截器类

检查此接口调用的方法是否使用了SameUrlData注解,若没有使用,表示此接口不需要校验;

若使用了注解,获取请求url+参数,并去除一直在变化的参数(比如时间戳timeStamp和签名sign)

检查参数中是否有token参数(token代表不同的用户的唯一标识),没有直接放行

有token参数,将token+url作为redis的key,url+参数作为value存入redis,并设定自动销毁时间

再次访问进行验证是否重复请求

```java

import com.alibaba.fastjson.JSONObject;

import com.tuohang.hydra.framework.common.spring.SpringKit;

import com.tuohang.hydra.toolkit.basis.string.StringKit;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.data.redis.core.StringRedisTemplate;

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.lang.reflect.Method;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.concurrent.TimeUnit;

/**

* @Title: 防止用户重复提交数据拦截器

* @Description: 将用户访问的url和参数结合token存入redis,每次访问进行验证是否重复请求接口

* @Auther: xhq

* @Version: 1.0

* @create 2019/3/26 10:35

*/

@Component

public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {

private static Logger LOG = LoggerFactory.getLogger(SameUrlDataInterceptor.class);

/**

* 是否阻止提交,fasle阻止,true放行

* @return

*/

@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)){

//请求数据相同

LOG.warn("please don't repeat submit,url:"+ request.getServletPath());

JSONObject result = new JSONObject();

result.put("statusCode","500");

result.put("message","请勿重复请求");

response.setCharacterEncoding("UTF-8");

response.setContentType("application/json; charset=utf-8");

response.getWriter().write(result.toString());

response.getWriter().close();

// 拦截之后跳转页面

// String formRequest = request.getRequestURI();

// request.setAttribute("myurl", formRequest);

// request.getRequestDispatcher("/WebRoot/common/error/jsp/error_message.jsp").forward(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){

//获取请求参数map

Map parameterMap = httpServletRequest.getParameterMap();

Iterator> it = parameterMap.entrySet().iterator();

String token = "";

Map parameterMapNew = new HashMap();

while(it.hasNext()){

Map.Entry entry = it.next();

if(!entry.getKey().equals("timeStamp") && !entry.getKey().equals("sign")){

//去除sign和timeStamp这两个参数,因为这两个参数一直在变化

parameterMapNew.put(entry.getKey(), entry.getValue());

if(entry.getKey().equals("token")) {

token = entry.getValue()[0];

}

}

}

if (StringKit.isBlank(token)){

//如果没有token,直接放行

return false;

}

//过滤过后的请求内容

String params = JSONObject.toJSONString(parameterMapNew);

System.out.println("params==========="+params);

String url = httpServletRequest.getRequestURI();

Map map = new HashMap();

//key为接口,value为参数

map.put(url, params);

String nowUrlParams = map.toString();

StringRedisTemplate smsRedisTemplate = SpringKit.getBean(StringRedisTemplate.class);

String redisKey = token + url;

String preUrlParams = smsRedisTemplate.opsForValue().get(redisKey);

if(preUrlParams == null){

//如果上一个数据为null,表示还没有访问页面

//存放并且设置有效期,2秒

smsRedisTemplate.opsForValue().set(redisKey, nowUrlParams, 2, TimeUnit.SECONDS);

return false;

}else{//否则,已经访问过页面

if(preUrlParams.equals(nowUrlParams)){

//如果上次url+数据和本次url+数据相同,则表示重复添加数据

return true;

}else{//如果上次 url+数据 和本次url加数据不同,则不是重复提交

smsRedisTemplate.opsForValue().set(redisKey, nowUrlParams, 1, TimeUnit.SECONDS);

return false;

}

}

}

}

```

2.3注册拦截器

```java

@Configuration

public class WebMvcConfigExt extends WebMvcConfig {

/**

* 防止重复提交拦截器

*/

@Autowired

private SameUrlDataInterceptor sameUrlDataInterceptor;

@Override

public void addInterceptors(InterceptorRegistry registry) {

// 避开静态资源

List resourcePaths = defineResourcePaths();

registry.addInterceptor(sameUrlDataInterceptor).addPathPatterns("/**").excludePathPatterns(resourcePaths);// 重复请求

}

/**

* 自定义静态资源路径

*

* @return

*/

@Override

public List defineResourcePaths() {

List patterns = new ArrayList();

patterns.add("/assets/**");

patterns.add("/upload/**");

patterns.add("/static/**");

patterns.add("/common/**");

patterns.add("/error");

return patterns;

}

}

```

在相应方法上加@SameUrlData注解

```java

@SameUrlData

@ResponseBody

@RequestMapping(value = "/saveOrUpdate")

public String saveOrUpdate(){

}

```

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python生成yaml文件_Python实践34-读写yaml文件

关于yamlYAML语言的设计目标,就是方便人类读写。如果你想要实现一些用ini不好做到的配置,可以使用yaml格式作为配置文件大小写敏感使用缩进表示层级关系缩进时不允许使用Tab键,只允许使用空格。缩进的空格数目不重要,只要相同层级…

Module build failed (from ./node_modules/babel-loader/lib/index.js): Error: [BABEL] D:\moblie\web_co

原因: 出现错误是由于babel的版本冲,有两种解决方式: (1). babel-loader 8.x | babel 7.x (升级版本) npm install -D babel-loader babel/core babel/preset-env webpack (2). babel-loader 7.x | babel 6.x (降级版本) npm install -D…

qwt自定义时间标尺TimeScale

需要重载QwtScaleDraw类的virtual QwtText label(double)const;方法 该方法在需要绘制scale label时会调用。 主要思路是标尺上的点对应QDateTime::toTime_t()的值。然后重载label方法,在QwtText label(double v) const 方法中将参数v转化为QDateTime对象&#xff0…

二维数组排序 行与列分别升序_6个经典排序技巧,尤其是最后一个,绝对的个性化...

点击上方"Excel函数公式"免费订阅排序,从字面意思理解就是将一组“无序”的记录调整为“有序”的记录。在Excel中,排序是一种常见的操作,那么,如何高效的完成排序操作呢?一、Excel排序:常规(命令…

java平移变换_java移位运算符:(左移)、(带符号右移)和(无符号右移)。...

1、 左移运算符 左移运算符<<使指定值的所有位都左移规定的次数。 1)它的通用格式如下所示&#xff1a; value << num num 指定要移位值value 移动的位数。 左移的规则只记住一点&#xff1a;丢弃最高位&#xff0c;0补最低位 如果移动的位数超过了该类型的最大位数…

Quantumas,作者太NB了,俺发现俺菜得跟猪一样!!!

前不久一个名为“Quantumas”的3D Demo引起了我们的关注&#xff0c;其作者称“这个引擎整合了各种最先进的渲染技术&#xff0c;在画质上超越了虚幻3&#xff0c;部分超越了CE2”&#xff0c;而我们在这个Demo中也的确看 到了高阶动态范围、多频软阴影、空间环境遮掩、HDR景深…

python制作查询网页_peewee数据查询之分页返回——python学习笔记

请求示例&#xff1a;后端代码# 查询客服列表kf.route("/list", methods[POST])def kf_list():start request.values.get(start)length request.values.get(length)try:find model.py_user_kf.select().order_by(model.py_user_kf.create_time).paginate(int(star…

java薪资年龄交叉表_巧用参数实现交叉表行列互换

对于普通的没有olap分析功能的快逸报表而言&#xff0c;一旦发布到web页面后&#xff0c;其样式就被固定了&#xff0c;如果想把报表换种形式展现通常的做法是重新设计一张报表模板来实现&#xff0c;难道快逸报表就没有一种特殊的方法可以解决这样的需求吗&#xff1f;答案就在…

spring 环境

引用&#xff1a;http://www.189works.com/article-96241-1.html 准备阶段&#xff1a; 到Spring官网下载所需的API包,其中spring-framework-X.X.X.RELEASE-with-docs.zip压缩包需要下载&#xff0c;里面的dist目录下有所需的API,还有一个是com.springsource.org.apache.common…

php 上传多文件_php 多文件上传的实现实例

首先向大家讲解一下实现的方法。要实现多文件上传&#xff0c;我们可以在form表单中添加多个input file域&#xff0c;然后将这些input file的name属性设置为相同的名称且使用数组的形式命名&#xff0c;例如filename[]。至于文件上传的php代码和单个文件上传是一样的道理。下面…

JavaScript代码优化实战之一:缓存变量,关键字过滤

无意中看到某网站的一段JS代码&#xff1a; 1 function clearSearchText(){2 var searchtext document.getElementById("searchwordl").value3 document.getElementById("searchwordl").value"";4 }5 function replaceALL(){6 va…

html.parser python_python模块之HTMLParser

HTMLParser是python用来解析html的模块。它可以分析出html里面的标签、数据等等&#xff0c;是一种处理html的简便途径。 HTMLParser采用的是一种事件驱动的模式&#xff0c;当HTMLParser找到一个特定的标记时&#xff0c;它会去调用一个用户定义的函数&#xff0c;以此来通知程…

php 5.4 aws,使用 Amazon EC2 管理 AWS SDK for PHP 实例 - 适用于 PHP 的 AWS 开发工具包

AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异&#xff0c;请参阅中国的 AWS 服务入门。本文属于机器翻译版本。若本译文内容与英语原文存在差异&#xff0c;则一律以英文原文为准。使用 Amazon EC2 管理 AWS SDK for PHP 实例以下示例演示如何&…

python图片分类毕业设计成果报告书_毕业设计成果报告书模板.doc

毕业设计成果报告书成 果&#xff1a;姓 名&#xff1a;学 号&#xff1a;专 业&#xff1a;区 队&#xff1a;指导老师&#xff1a;二〇XX年X月目  录TOC \o "1-3" \h \z \u HYPERLINK \l "_Toc432664597" 一、选题背景 PAGEREF _Toc432664597 \h 1HYPE…

常用正则表达式例子

1。^\d$  //匹配非负整数&#xff08;正整数 0&#xff09; 2。^[0-9]*[1-9][0-9]*$  //匹配正整数 3。^((-\d)|(0))$  //匹配非正整数&#xff08;负整数 0&#xff09; 4。^-[0-9]*[1-9][0-9]*$  //匹配负整数 5。^-?\d$    //匹配整数 6。^\d(\.\d)?$  //…

msf生成php,使用msfpayload生成后门(asp,aspx,php,jsp,exe)等

msfpayload与msfencode参数说明执行msfpayload -h查看都有哪些参数执行msfencode -h查看都有哪些参数生成backdoor类型可以生成asp、aspx、php、jsp、war、exe等多种类型,下面介绍的使用方法就不一一测试了.msfpayload生成linux backdoor目标机器运行linux2,本机监听下端口,使用…