我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java结合redis实现接口防重复提交

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java结合redis实现接口防重复提交

redis 接口防重

技术点:redis/aop

说明:

简易版本实现防止重复提交,适用范围为所有接口适用,采用注解方式,在需要防重的接口上使用注解,可以设置防重时效。

场景:

在系统中,经常会有一些接口会莫名其妙的被调用两次,可能在幂等接口中不会存在太大的问题,但是非幂等接口的处理就会导致出现脏数据,甚至影响系统的正确性。

选型参考:

在常见的防重处理分为多种,粗分为前端处理,后端处理

前端处理分为:

  • 在按钮触发后便将按钮置灰,设置为不可用,在接口调用成功后回调处理,将按钮恢复
  • 发送请求时,设置一个状态,在接口请求时去获取状态,查看在保护期是否已经有调用,思路与第一条类似

后端处理分为:

  • 版本号,在数据表中增加version字段,在我们需要进行防重的接口请求到达后端后,sql处理时增加版本号条件(切记:每次在修改操作后需要对版本号进行加1哦),如果不一致则不进行处理。这也是乐观锁实现的一种思路。
  • redis,即本文所述方式

选型原因

  • 在系统安全中,防重复提交也是比较重要的一个指标,也就是接口幂等性。所以我们在日常的系统开发中,一般使用的是简化版的放重复。也就是仅仅通过前端来进行防重控制,但是这样也是具有风险性的。如果在涉及比较重要的数据的时候,可能往往会有热心同行来帮我们找bug,对于他们可以直接通过抓报文的方式拿到我们的交互信息,以此来进行各种骚操作(羊毛党做派,当然了,如果要避免接口攻击,我们还要设置ip请求限制,小黑屋,防DDOS等各种防御工作,此处只讲防重咯)。所以我们在重要数据处理时在后端也是同样需要进行防重处理的。
  • 防重提交的方式非常非常多,如上提出了四种方式,也只是冰山一角了。针对于后端侧防重,如上简述了两种方式。个人觉得在不同的时机可以进行不同的选择。如果我们在项目初期,个人觉得使用版本号处理更为合适一点,这样会降低对第三方工具的依赖,因为我们在每加入一个新东西的时候都是会增加系统的复杂性的。我们在考虑性能,安全,可靠的时候就会多出一个事项,有点给自己找事做的样子。但是如果我们的系统已经较为平稳了,那么此时对数据库进行增加字段虽然也不会太难,但是会改动一些代码。惊喜总是从这些地方来的。使用redis我觉得就要合适一些了,我们只需要面向切面进行编程,一处编写,处处可用。从代码和扩展来讲redis就更为合适了。

以上内容都是瞎BB的,其实我也是个菜鸟,欢迎各位大佬提建议或者意见,大家共同进步,共同完善,让java圈充满激情四射的爱。

代码样例


@PostMapping("/user/update")
@ApiOperation(value = "修改用户信息", notes = "修改用户信息", tags = "user module")
@AvoidReSubmit(expireTime = 1000 * 3)
public void update(@RequestBody User user){
 userMapper.updateById(user);
}

具体代码实现


// 定义自定义注解,设置注解参数默认值
package top.withu.gaof.freehope.annotate;

import java.lang.annotation.*;



@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AvoidReSubmit {

    
    long expireTime() default 30 * 1000L;
}


// 定义切面进行处理
package top.withu.gaof.freehope.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import top.withu.gaof.freehope.annotate.AvoidReSubmit;

import javax.annotation.Resource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;


@Aspect
@Component
public class AvoidResumitAspect {

    @Resource
    private RedisTemplate redisTemplate;

    
    @Pointcut(value = "@annotation(top.withu.gaof.freehope.annotate.AvoidReSubmit)")
    public void submit() {
    }

    @Before("submit()&&@annotation(avoidReSubmit)")
    public void doBefore(JoinPoint joinPoint, AvoidReSubmit avoidReSubmit) {

        // 拼装参数
        StringBuffer sb = new StringBuffer();
        for(Object object : joinPoint.getArgs()){
            sb.append(object);
        }

        String key = md5(sb.toString());
        long expireTime = avoidReSubmit.expireTime();
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Object object = valueOperations.get(key);
        if(null != object){
            throw new RuntimeException("您已经提交了请求,请不要重复提交哦!");
        }
        valueOperations.set(key, 1, expireTime, TimeUnit.MILLISECONDS);
    }

    @Around("submit()&&@annotation(avoidReSubmit)")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint, AvoidReSubmit avoidReSubmit) throws Throwable {
        System.out.println("环绕通知:");
        Object result = null;
        result = proceedingJoinPoint.proceed();
        return result;
    }

    @After("submit()")
    public void doAfter() {
        System.out.println("******拦截后的逻辑******");
    }

    private String md5(String str){
        if (str == null || str.length() == 0) {
            throw new IllegalArgumentException("String to encript cannot be null or zero length");
        }
        StringBuffer hexString = new StringBuffer();
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte[] hash = md.digest();
            for (int i = 0; i < hash.length; i++) {
                if ((0xff & hash[i]) < 0x10) {
                    hexString.append("0" + Integer.toHexString((0xFF & hash[i])));
                } else {
                    hexString.append(Integer.toHexString(0xFF & hash[i]));
                }
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return hexString.toString();
    }
}

思路:

简单的通过redis实现,估计版本在网上非常多了。这里的一个思路还是mark一下,现在我这代码只有我和上帝知道什么意思,我怕一个月以后就只有上帝知道了。

  • 自定义注解,注解中申明有效时间
  • 使用aop切面拦截自定义注解,获取注解中有效时间参数,此处默认设置保护期为30 * 1000L,单位毫秒
  • 在切面中获取接口请求的参数,将参数拼接成string,然后进行md5,这样操作是因为降低key长度,避免看起来过于恶心。但是这里有一个情况没有进行测试,那就是key碰撞的问题。在大量数据操作下是否会产生相同key值。
  • 使用md5加密后的key值到redis中查询,如果存在记录则表明已经有接口访问且处于保护期,不可继续提交。此处使用异常处理。如果不存在记录,则表明此接口在保护期内没有访问过,则不操作。此处的场景在使用时可以根据自己需求而定。
  • 此处在环绕通知和after通知均没有操作,因为我们只是对于放重复提交处理,业务场景中不存在后处理的情况,故而没有具体实现。

到此这篇关于Java结合redis实现接口防重复提交的文章就介绍到这了,更多相关redis 接口防重复提交内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网! 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java结合redis实现接口防重复提交

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

java接口防止重复提交的方法是什么

一种常见的方法是使用Token令牌来防止重复提交。具体步骤如下:1. 在接口中定义一个获取Token的方法,例如`getToken()`。2. 在接口调用前,先调用`getToken()`方法获取一个Token,并将其存储在客户端(例如放入
2023-08-17

怎么用SpringBoot+Redis实现重复提交

本篇内容介绍了“怎么用SpringBoot+Redis实现重复提交”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在实际的开发项目中,一个对外
2023-06-16

PHP 防抖和防重复提交的实现方式对比

在开发 Web 应用程序时,我们经常会遇到一些需要进行防抖(Debounce)和防重复提交(Preventing Duplicate Form Submission)的情况。防抖是指当用户频繁触发某个事件时,我们希望只执行最后一次触发的动作
2023-10-21

PHP 中实现防抖和防重复提交的最佳实践方法

引言:在开发Web应用程序时,我们经常会遇到一些需要避免重复提交或过于快速触发的问题。这些问题可能导致一些不必要的操作,或者是对服务器造成过大的压力。为了解决这些问题,我们可以使用PHP中的防抖和防重复提交的方法来限制用户行为。什么是防抖?
2023-10-21

架构设计 | 接口幂等性原则,防重复提交Token管理

本文源码:GitHub·点这里 || GitEE·点这里一、幂等性概念1、幂等简介编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。就是说,一次和多次请求某一个资源会产生同样的作用影响。2、HTTP请求遵循Htt
2023-06-02

如何在 PHP 中实现防抖和防重复提交的功能

在Web开发中,防抖和防重复提交是两个常见的问题。防抖用于处理频繁触发的事件,防止过度请求;而防重复提交则是为了避免用户多次提交相同的表单。本文将介绍如何在PHP中实现这两个功能,并提供具体的代码示例。一、防抖功能实现防抖功能主要是在一定的
2023-10-21

如何在 PHP 中实现防抖功能,避免重复提交

防抖功能是指在用户连续触发某个事件时,只执行一次事件处理函数,并且在最后一次触发后的一段时间内不会再执行。在实际的开发中,经常会遇到用户重复提交表单的情况,使用防抖功能可以有效地避免这种情况的发生。在 PHP 中实现防抖功能的方法有很多,下
2023-10-21

PHP 防抖和防重复提交的实现原理对比及优缺点分析

引言:在Web开发中,防抖和防重复提交是常见的问题。当用户频繁触发某个事件时,我们希望能够控制事件的触发频率,防抖机制能够帮助我们减少不必要的请求。另一方面,防止用户多次提交相同的表单也是一个重要的安全考虑。本文将介绍PHP中实现防抖和防重
2023-10-21

Vue3 + elementplus实现表单验证+上传图片+ 防止表单重复提交功能

这篇文章主要介绍了Vue3 + elementplus 表单验证+上传图片+ 防止表单重复提交,本文给大家展示效果图和完整代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-11-13

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录