精华内容
下载资源
问答
  • 1024技术干货 ~ Java如何防止接口重复提交
    千次阅读
    2020-10-24 09:58:04

        正如本文标题所言,今天我们来聊一聊在Java应用系统中如何防止接口重复提交;简单地讲,这其实就是“重复提交”的话题,本文将从以下几个部分展开介绍: 

        1.“重复提交”简介与造成的后果

        2.“防止接口重复提交”的实现思路

        3.“防止接口重复提交”的代码实战

        一、“重复提交”简介与造成的后果

        对于“重复提交”,想必各位小伙伴都知晓它的意思,简单的理解,它指的是前端用户在间隔很短的时间周期内对同一个请求URL发起请求,导致前端开发者在很短的时间周期内将同一份数据(请求体)提交到后端相同的接口 多次,最终数据库出现多条主键ID不一样而其他业务数据几乎一毛一样的记录;

        仔细研究上述整个过程,会发现如果发起的多次请求的时间间隔足够短,即时间趋向于无穷小 时,其过程可以归为“多线程并发导致并发安全”的问题范畴;而对于“并发安全”的话题,debug早在此前自己录制的课程以及之前的文章中介绍过多次了,在此不再赘述;

        上述在对“重复提交”的介绍中隐约也提及它所带来的的后果:

        (1)数据库DB出现多条一毛一样的数据记录;

        (2)如果重复发起的请求足够多、请求体容量足够大,很可能会给系统接口带来极大的压力,导致其出现“接口不稳定”、“DB负载过高”,严重点甚至可能会出现“系统宕机”的情况;

    更多相关内容
  • 干货实战~Java如何防止接口重复提交

    千次阅读 2021-02-12 22:01:19
    正如本文标题所言,今天我们来聊一聊在Java应用系统中如何防止接口重复提交;简单地讲,这其实就是“重复提交”的话题,本文将从以下几个部分展开介绍:“重复提交”简介与造成的后果“防止接口重复提交”的实现思路...

    正如本文标题所言,今天我们来聊一聊在Java应用系统中如何防止接口重复提交;简单地讲,这其实就是“重复提交”的话题,本文将从以下几个部分展开介绍:“重复提交”简介与造成的后果

    “防止接口重复提交”的实现思路

    “防止接口重复提交”的代码实战8b22ab26ef493287a87a82da4f4b89c6.png

    1、“重复提交”简介与造成的后果

    对于“重复提交”,想必各位小伙伴都知晓它的意思,简单的理解,它指的是前端用户在间隔很短的时间周期内对同一个请求URL发起请求,导致前端开发者在很短的时间周期内将同一份数据(请求体)提交到后端相同的接口 多次,最终数据库出现多条主键ID不一样而其他业务数据几乎一毛一样的记录;

    仔细研究上述整个过程,会发现如果发起的多次请求的时间间隔足够短,即时间趋向于无穷小 时,其过程可以归为“多线程并发导致并发安全”的问题范畴;而对于“并发安全”的话题,debug早在此前自己录制的课程以及之前的文章中介绍过多次了,在此不再赘述;

    上述在对“重复提交”的介绍中隐约也提及它所带来的的后果:

    (1)数据库DB出现多条一毛一样的数据记录;

    (2)如果重复发起的请求足够多、请求体容量足够大,很可能会给系统接口带来极大的压力,导致其出现“接口不稳定”、“DB负载过高”,严重点甚至可能会出现“系统宕机”的情况;

    因此,我们需要在一些很可能会出现“重复提交”的后端接口中加入一些处理机制(附注:前端其实也需要配合一同处理的,其处理方式在本文就不做介绍了~);8b22ab26ef493287a87a82da4f4b89c6.png

    2、“防止接口重复提交”的实现思路

    值得一提的是,绝大部分情况下,只有POST/PUT/DELETE的请求方式才会出现“重复提交”的情况,而对于GET请求方式,只要不是出现人为的意外情况,那么它就具有“幂等性”,谈不上“重复提交”现象的出现,因此,在实际项目中,出现“重复提交”现象比较多的一般是POST请求方式;

    而在实际项目开发中,“防止接口重复提交”的实现方式有两类,一类是纯粹的针对请求链接URL的,即防止对同一个URL发起多次请求:此种方式明显粒度过大,容易误伤友军;另一类是针对请求链接URL + 请求体 的,这种方式可以说是比较人性化而且也是比较合理的,而我们在后面要介绍的实现方式正是基于此进行实战的;

    为了便于小伙伴理解,接下来我们以“用户在前端提交注册信息”为例,介绍“如何防止接口重复提交”的实现思路,如下图所示为整体的实现思路:6887f65f8e1d3dad757f780efad35794.png

    从该图中可以得知,如果当前提交的请求URL已经存在于缓存中,且 当前提交的请求体 跟 缓存中该URL对应的请求体一毛一样 且 当前请求URL的时间戳跟上次相同请求URL的时间戳 间隔在8s 内,即代表当前请求属于 “重复提交”;如果这其中有一个条件不成立,则意味着当前请求很有可能是第一次请求,或者已经过了8s时间间隔的 第N次请求了,不属于“重复提交”了。8b22ab26ef493287a87a82da4f4b89c6.png

    3、“防止接口重复提交”的代码实战

    照着这个思路,接下来我们将采用实际的代码进行实战,其中涉及到的技术:Spring Boot2.0 + 自定义注解 + 拦截器 + 本地缓存(也可以分布式缓存);

    (1)首先,需要自定义一个用于加在需要“防止重复提交”的请求方法上 的注解RepeatSubmit,该注解的定义代码很简单,就是一个常规的注解定义,如下代码所示:45f1283d14e94f49593473bdb2dfd257.png

    之后,是直接创建一个新的控制器SubmitController,并在其中创建一请求方法,用于处理前端用户提交的注册信息 请求,如下代码所示:93148cd31ab82bfb41c5915a60f3fa5f.png

    其中,RegisterDto 为自定义的实体类,代码定义如下所示:e79e6f27789ace742d8f1e30f5634a07.png

    (2)将注解加上去之后,接下来需要自定义一个拦截器RepeatSubmitInterceptor,用于拦截并获取 加了上述这个注解的所有请求方法的相关信息,包括其请求URL和请求体数据,其核心代码如下所示:4b4fd547311cbe5dab68d31cc1e752b6.png

    在这里我们将其定义为抽象类,并自定义一个抽象方法:“判断当前请求是否为重复提交isRepeatSubmit()”,之所以这样做,是因为“判断是否重复提交”可以有多种实现方式,而每种实现方式可以通过继承该抽象类 并 实现该抽象方法 从而将其区分开来,某种程度降低了耦合性(面向接口/抽象类编程);如下代码所示为该抽象类的其中一种实现方式:84d336458a6a910625c4bc8f6e1259de.png

    f643be2ad46a8acac986c43d1f713516.png

    91473446cd39548a0fe4fcd14f08bb93.png

    该代码虽然看起来有点多,但是仔细研读,会发现其实这些代码 就是笔者在上文中贴出的实现流程图 的具体实现,可以说是将理论知识进行真正的落地实现;

    在这里再重复赘述一下,其整体的实现思路为:获取当前请求的URL作为键Key,暂且标记为:A1,其取值为映射Map(Map里面的元素由:请求的链接url 、 请求体的数据、和 请求时的时间戳 三部分组成) 暂且标记为V1;从缓存中(本地缓存或者分布式缓存)查找Key=A1的值V2,如果V2和V1里的请求体数据一样 且 两次请求是在8s内,即代表当前请求是重复提交的,系统将拒绝执行后续的业务逻辑;否则可以继续往后面执行 “将用户信息插入到数据库中” 的业务逻辑;

    (3)最后,需要将上述自定义的拦截器加入中系统全局配置中,如下所示:338ebb7966fd78adbe22e14990da6da3.png

    运行项目,打开Postman,连续多番进行测试,如下几张图所示:37b359349f0af781c832e356a18b978d.png

    67e29b4e48899ccfc33ad793ac5c71d5.png

    713eca2a3baa2d03b2ebe1d6141dc228.png

    至此,我们已经采用实际的代码实战实现了“如何防止接口重复提交”的功能,值得一提的是,上述代码在实现过程中,其核心在于缓存组件的搭建;在“重复提交”这一业务场景中,它需要满足两个条件方可发挥作用:一个是可以用于缓存信息,即具有Key - Value的特性;另一个是可以对存储的数据设置过期时间;

    在这里笔者采用的是google开发工具类中的CacheBuilder构建本地缓存组件的,感兴趣的小伙伴可以自行搜索相关资料;然而这种实现方式在集群多实例部署的情况下是有问题的,因为CacheBuilder只适用于单一架构体系,所以如果是多实例集群部署的情况,最好用Redis。8b22ab26ef493287a87a82da4f4b89c6.png

    (1)文中涉及到的代码已经放在gitee上了,访问链接如下所示,别忘了给个star哦:https://gitee.com/steadyjack/SpringBootTechnologyA。

    (2)期间如何有任何问题都可以私信debug。

    (3)麻烦继续关注“程序员实战基地”,您的关注和转发 就是 debug勤劳写技术文的动力!!!5a6248719eab539b6cda4d7ca48f5a7b.png

    展开全文
  • 主要介绍了Spring boot通过AOP防止API重复请求代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 有一些场景,例如申请提交之后几秒内需要防止用户重复提交,我们后端通过自定义注解实现这一功能 需要的pom依赖: 在编写项目之前,如果要用自定义注解实现这一功能,需要导入spring aop,redis依赖等 <!--...

    项目场景:

    有一些场景,例如申请提交之后几秒内需要防止用户重复提交,我们后端通过自定义注解实现这一功能


    需要的pom依赖:

    在编写项目之前,如果要用自定义注解实现这一功能,需要导入spring aop,redis依赖等

    <!--        spring aop依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
            </dependency>
    
    <!--        工具类依赖-->
            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>2.6</version>
            </dependency>
    
    <!--        reids依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>


    实现:

    1、新建一个自定义注解,定义变量

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LimitAccessTimes {
        /**
         * 指定时间内不可重复提交,单位:s
         *
         * @return
         */
        long timeout() default 3;
    }
    

    ··2、定义切面实现类

    @Aspect
    @Component
    @Slf4j
    public class LimitAccessTimesAspect {
    
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
    
        @Before("@annotation(customannotationdemo.customannotationdemo.annotation.LimitAccessTimes)")
        public void repeatSumbitIntercept(JoinPoint joinPoint)  throws Exception{
            // userTicket + 类名 + 方法 + timeout
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            Cookie[] cookieList = request.getCookies();
            String userTicket = null;
            for (Cookie cookie : cookieList) {
                if (cookie.getName().equals("userTicket")) {
                    userTicket = cookie.getValue();
                    break;
                }
            }
            //获取当前切面所设置的方法的类名、方法名
            String className = joinPoint.getTarget().getClass().getName();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            String methodName = method.getName();
    
            // 获取配置的过期时间
            LimitAccessTimes annotation = method.getAnnotation(LimitAccessTimes.class);
            long timeout = annotation.timeout();
    
            String key = userTicket + ":" + className + ":" + methodName + ":" + timeout + "s";
            log.info(" --- >> 防重提交:key -- {}", key);
            // 判断是否已经超过重复提交的限制时间
            String value = redisTemplate.opsForValue().get(key);
            if (StringUtils.isNotBlank(value)) { //如果不为空,说明redis设置的禁止访问时间未过期,抛出异常,禁止访问
                String messge = MessageFormat.format("请勿在{0}s内重复提交", timeout);
                throw new Exception(messge);
            }
            redisTemplate.opsForValue().set(key, key, timeout);
        }
    
    
    }
    

    实现思路

    在段代码,流程是在执行接口方法前(加上注解的方法),获取用户的userTicker和访问的类和方法,将这几个参数拼接组成一个字符串,然后查询redis,如果没有结果,说明是第一次访问,把这个字符串写入redis,设置过期时间;之后在为过期的时候在访问,抛出异常,后续可以定义一个全局异常捕获,把异常信息抛出给前端

    只需要在需要防止重复访问的方法上新增注解,非常方便

     

    展开全文
  • java 拦截重复请求 防止重复提交 超简单实现 准备工具 1.redis 2.自定义拦截注解 思路 1.注解里定义当前用户对当前接口访问最大间隔时间。 2.用户访问时在拦截器通过redis设置一个key-value-time。key识别用户,...

    准备工具

    1.redis
    2.自定义拦截注解

    思路

    1.注解里定义当前用户对当前接口访问最小间隔时间。
    2.用户访问时在拦截器通过redis设置一个key-value-time。key识别用户,value随意,time为当前接口上注解里设置的时间,即缓存key-value-time失效时间。
    3.用户访问当前接口时从redis里通过key获取value,如果能够获取到值,说明仍处于被拦截状态,返回被拦截提示。获取不到说明缓存已过期,访问间隔已超过拦截时间,则放行,并重新设置key-value-time拦截时间,预备下一次拦截。
    4.redis是单线程的,并发访问不影响拦截功能。

    代码

    注解
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DuplicateCommitLimit {
    
        /**
         * 拦截时间,单位为秒,默认值1秒
         */
        long time() default 1;
    }
    
    拦截实现
    @Component
    public class LoginAppInterceptor implements HandlerInterceptor {
    
        @Autowired
        private RedisUtil redisUtil;
    
        /**
         * 进入controller层之前拦截请求
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if (handler instanceof ResourceHttpRequestHandler) {
                // 静态资源不做权限处理
                return true;
            }
            // 获取 token
            String token = request.getHeader("token");
            if (token == null) {
                token = request.getParameter("token");
            }
    		//此处省去权限校验等代码。。。
    		
            HandlerMethod handlerMethod = (HandlerMethod) handler;
    
            //方法上的DuplicateCommitLimit注解
            DuplicateCommitLimit duplicateCommitLimit = handlerMethod.getMethodAnnotation(DuplicateCommitLimit.class);
            if (duplicateCommitLimit != null) {
            	String requestURI = request.getRequestURI();
                boolean isLimited = redisUtil.get(token + "duplicate_commit" + requestURI) != null;
                if (isLimited) {
                    sendDuplicateCommitMsg(response);//返回被拦截提示信息
                    return false;
                } else {
                    redisUtil.set(token + "duplicate_commit" + requestURI, true, duplicateCommitLimit.time());
                }
            }
    
    		return true;
    	}
    
        private void sendDuplicateCommitMsg(HttpServletResponse response) {
            ResultBase resultBase = new ResultBase();
            resultBase.setSuccess(false);
            resultBase.setCode(ResultCode.FAIL.getCode());
            resultBase.setMessage(ResultCode.FREQUENTLY.getDesc());
            ResponseUtils.renderJson(response, resultBase.toString());
        }
        
    }
    
    测试
    	@ApiOperation(value = "获取登录用户信息")
        @GetMapping(value = "getLoginUserInfo")
        @DuplicateCommitLimit(time = 3)
        public ResultData<User> getLoginUserInfo() {
            return userService.getLoginUserInfo();
        }
    
    效果

    在这里插入图片描述

    展开全文
  • Java接口重复提交

    千次阅读 2021-02-02 18:02:46
    背景 业务系统中的防重复提交都是由前端控制,后端在某些地方做了相应的业务逻辑相关的判断,但当某些情况下,前后端的判断都会失效,所以这里引入后端的接口重复提交校验。 方案选择 ...
  • Java后端防止频繁请求重复提交

    千次阅读 2022-04-10 15:29:21
    Java后端防止频繁请求重复提交 在客户端网络慢或者服务器响应慢时,用户有时是会频繁刷新页面或重复提交表单的,这样是会给服务器造成不小的负担的,同时在添加数据时有可能造成不必要的麻烦。所以我们在后端也有...
  • java防止重复提交解决方案

    千次阅读 2020-07-03 09:31:05
    在前端开发过程中可以将操作按钮在操作完后就行置灰操作,虽然这样做了,但是不能从根们解决问题(前端和后端都防止,这样就可以根本解决问题),然后需要后端进行提交接口进行重复提交处理。 解决思路 1.前端利用js...
  • 这篇文章主要介绍了Java后台防止客户端重复请求、提交表单实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下前言在Web / App项目中,有一些请求或操作会...
  • //返回true说明该请求不是重复提交,false表示方法还在执行中 boolean success = KEY.add(sign); if (!success) { return JsonModel.toSuccess("休息一下再点击"); } try { joinPoint.proceed(); } finally ...
  • 创建一个接口类 public @interface NoRepeatSubmit { ... * 接口重复提交拦截切面(两秒内同一客户端不允许重复提交) */ @Aspect @Configuration public class SubmitAspect { private Logger l
  • Java后端接口防止重复提交

    千次阅读 2020-06-30 13:56:27
      刚开始采用利用自定义注解+aop+redis防止重复提交这篇博客的逻辑去实现,但是后来在测试多线程访问的时候会出现问题,然后参考网上Redis分布式锁的逻辑,多线程情况下测试只有一个可以通过。参考了LockManager中...
  • Java中如何避免重复提交请求

    千次阅读 2021-03-06 02:23:33
    查看后台日志后发现一个同样的请求提交了多次,后果就是轻则导致产生多条...二、产生原因导致重复请求的原因很多,大体为以下几种:多次点击提交按钮反复刷新页面点击浏览器后退按钮,导致重复提交表单浏览器重复的H...
  • 如何处理重复请求/并发请求的

    千次阅读 2021-03-11 14:02:06
    利用唯一请求编号去重你可能会想到的是,只要请求有唯一的请求编号,那么就能借用Redis做这个去重——只要这个唯一请求编号在redis存在,证明...//1000毫秒过期,1000ms内的重复请求会认为重复long expireAt = Sys...
  • 来源:cnblogs.com/cjsblog/p/14516909.html概述为了防止掉单,这里可以这样处理:为了防止订单重复提交,可以这样处理:附上微信支付最佳实践:概述如图是一个简化...
  • JavaWeb防止用户的重复请求提交

    千次阅读 2021-03-09 17:39:17
    这里实现这个重复提交的防止,是通过在一个FIlter过滤器中生成一个令牌token,保存在Session域中,然后在对这个token加密得到ciphertext(密文),将密文保存在request域中。如果在login.jsp中的一个隐藏表单项中取得...
  • 实现思路 创建拦截器 Interceptor,拦截所有API请求 将用户唯一标识(token或者jsessionid)+接口地址进行拼接,作为后续步骤的 redis-key 判断Redis是否存在该key值,存在说明重复提交,不存在就存入Redis,过期...
  • 最简单的接口重复请求处理方法

    千次阅读 2020-08-31 18:10:32
    用户在较短时间内,可能几秒内重复提交,可以给用户提示“重复请求” 某些接口需要处理 在执行业务方法前就知道是否是重复请求,减缓服务器压力 知道当前用户和用户请求的接口,这样才能针对用户做重复判断 结合...
  • 在项目中,接口的暴露在外面,很多人就会恶意多次快速请求,那我们开发的接口和服务器在这样的频率下的话,服务器和数据库很快会奔溃的,那我们该怎么防止接口防刷呢?由于博主小白,很多都不懂,都是从网上一点一点...
  • java实现防止表单重复提交

    热门讨论 2010-07-19 10:46:14
    服务器端避免表单的重复提交,利用同步令牌来解决重复提交的基本原理如下:(1)用户访问提交数据的页面,服务器端在这次会话中,创建一个session对象,并产生一个令牌值,将这个令牌值作为隐藏输入域的值,随表单一起发送到...
  • 如何解决接口重复请求

    千次阅读 2021-05-25 11:42:28
    在一些场景用户请求有可能会重复发送,例如提交一个申请单前端没有做防重复,后端接收到两个相同的请求没有经过处理入库,就会存在两条重复的数据 唯一编码去重 如果每个请求都有一个唯一编码,例如前端每次进入...
  • java8 看不到源码 基于Java8 + Spring Boot打造Http请求拦截验证框架 :face_savoring_food: 花10分钟学会它做点什么,它会帮你检查Http Request参数。 可以防御重放攻击,校验参数被修改(数字签名) ,校验请求超时...
  • java8以后支持接口带默认实现,通过该方式定义一个接口,并实现防止重复点击逻辑,需要使用的接口只需要实现该接口即可 如: @NoFilter @ApiOperation("测试redis重复点击") @GetMapping("testJedisChongfu") ...
  • Java开发中怎么防止重复提交?

    千次阅读 2021-06-15 13:20:34
    } } } ④注解使用案例 @ApiOperation(value = "保存我的帖子接口", notes = "保存我的帖子接口") @PostMapping("/posts/save") @Resubmit(delaySeconds = 10) public ResponseDTO saveBbsPosts(@RequestBody @...
  • 利用Redis实现防止接口重复提交功能

    千次阅读 2021-12-14 20:36:42
    在划水摸鱼之际,突然听到有的用户反映增加了多条...其实想想就知道为啥会这样,在网络延迟的时候,用户多次点击,最后这几次请求都发送到了服务器访问相关的接口,最后执行插入。 既然知道了原因,该如何解决。当时我
  • 接口重复提交:是由于网络等原因,造成一瞬间发送多个请求,造成insert,update操作,多次数据被修改。如果多个请求时间间隔足够的小,那么可以理解为并发问题;即并发情况下,只有一次操作成功。 实现原理: 自定义...
  • 简化的模拟代码如下(基于 Spring Boot): import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;... * 被重复请求的方法 ...
  • 对于前后端分离的项目,后端人员通常都要对发起请求的用户的合法性和权限进行审核(比如用户每次请求都要携带token,token校验通过的才放行),只要审核通过了,基本上都允许用户的后续操作。可是这样就安全了吗?...
  • 关于重复请求,指的是我们服务端接收到很短的时间内的多个相同内容的重复请求。而这样的重复请求如果是幂等的(每次请求的结果都相同,如查询请求),那其实对于我们没有什么影响,但如果是非幂等的(每次请求都会...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 64,095
精华内容 25,638
关键字:

java防止接口重复请求

java 订阅