精华内容
下载资源
问答
  • 后台防止重复点击的实现方案比较多,这里介绍的是利用Spring的Aspect来实现接口防重点击。话 不多说,直接附上代码 1、配置自定义注解 import java.lang.annotation.ElementType; import java.lang.annotation....

    后台防止重复点击的实现方案比较多,这里介绍的是利用Spring的Aspect来实现接口防重点击。话 不多说,直接附上代码

    1、配置自定义注解

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 重复点击的切面
     *
     * @author Jonathan.WQ
     * @date 2021年05月14日 15:03
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NoRepeatSubmit {
        /**
         * 锁过期的时间
         */
        int seconds() default 5;
    
        /**
         * 锁的位置
         */
        String location() default "NoRepeatSubmit";
    
        /**
         * 要扫描的参数位置
         */
        int argIndex() default 0;
    
        /**
         * 参数名称
         */
        String name() default "";
    
        /**
         * 提示消息
         */
        String message() default "";
    }
    
    

    2、实现防重复点击的切面

    import com.alibaba.fastjson.JSON;
    import com.google.common.collect.Maps;
    import com.immo.jkzs.annotation.NoRepeatSubmit;
    import com.immo.jkzs.common.CommonResult;
    import com.immo.jkzs.common.RedisKey;
    import com.immo.jkzs.service.IRedisService;
    import com.immo.jkzs.util.StringUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.lang.reflect.Field;
    import java.util.Map;
    
    /**
     * 防重复点击的切面
     */
    @Aspect
    @Component
    public class NoRepeatSubmitAspect {
        private static final Logger logger = LoggerFactory.getLogger(NoRepeatSubmitAspect.class);
        private static final String SUFFIX = "SUFFIX";
        @Autowired
        private IRedisService redisService;
    
        /**
         * 横切点
         */
        @Pointcut("@annotation(noRepeatSubmit)")
        public void repeatPoint(NoRepeatSubmit noRepeatSubmit) {
        }
    
        /**
         * 接收请求,并记录数据
         */
        @Around(value = "repeatPoint(noRepeatSubmit)")
        public Object doBefore(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) {
            String key = RedisKey.NO_REPEAT_LOCK_PREFIX + noRepeatSubmit.location();
            Object[] args = joinPoint.getArgs();
            String name = noRepeatSubmit.name();
            int argIndex = noRepeatSubmit.argIndex();
            String message = noRepeatSubmit.message();
            String suffix;
            if (StringUtils.isEmpty(name)) {
                suffix = String.valueOf(args[argIndex]);
            } else {
                Map<String, Object> keyAndValue = getKeyAndValue(args[argIndex]);
                Object valueObj = keyAndValue.get(name);
                if (valueObj == null) {
                    suffix = SUFFIX;
                } else {
                    suffix = String.valueOf(valueObj);
                }
            }
            key = key + ":" + suffix;
            logger.info("==================================================");
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof HttpServletRequest
                        || args[i] instanceof HttpServletResponse) {
                    continue;
                }
                logger.info(JSON.toJSONString(args[i]));
            }
            logger.info("==================================================");
            int seconds = noRepeatSubmit.seconds();
            logger.info("lock key : " + key);
            Object obj = redisService.get(key);
            if (StringUtils.isNotNull(obj)) {
                return new CommonResult().failed(StringUtils.isNotNull(message) ? message : "操作过于频繁,请稍后重试");
            }
            try {
                Object proceed = joinPoint.proceed();
                return proceed;
            } catch (Throwable throwable) {
                logger.error("运行业务代码出错", throwable);
                throw new RuntimeException(throwable.getMessage());
            } finally {
                redisService.setExpire(key, "1", seconds);
            }
        }
    
        public static Map<String, Object> getKeyAndValue(Object obj) {
            Map<String, Object> map = Maps.newHashMap();
            // 得到类对象
            Class userCla = (Class) obj.getClass();
            /* 得到类中的所有属性集合 */
            Field[] fs = userCla.getDeclaredFields();
            for (int i = 0; i < fs.length; i++) {
                Field f = fs[i];
                // 设置些属性是可以访问的
                f.setAccessible(true);
                try {
                    Object val = f.get(obj);
                    // 得到此属性的值
                    // 设置键值
                    map.put(f.getName(), val);
                } catch (IllegalArgumentException e) {
                    logger.error("getKeyAndValue IllegalArgumentException", e);
                } catch (IllegalAccessException e) {
                    logger.error("getKeyAndValue IllegalAccessException", e);
                }
    
            }
            logger.info("扫描结果:" + JSON.toJSON(map));
            return map;
        }
    }
    
    
    /**
     * 锁前缀类
     *
     * @author Jonathan.WQ
     * @date 2021年05月25日 19:47
     */
    public class RedisKey {
    
        /**
         * 不可重复点击的锁前缀
         */
        public static final String NO_REPEAT_LOCK_PREFIX = "jkzs:no_repeat_lock:";
    }
    
    

    3.使用自定义注解
    将编写好的自定义注解添加到控制器类上,根据需要调整各个参数

        @Autowired
        private IMiniFeedbackService miniFeedbackService;
    
        @ApiOperation("新增意见反馈")
        @OperateLogAnnotation(remark = "新增意见反馈", operationType = OperationType.INSERT)
        @PostMapping("/add")
        @NoRepeatSubmit(location = "feedback:add", message = "您已提交过反馈,请勿重复提交!", seconds = 15)
        public CommonResult addHandler(@RequestParam(required = false) String content, @RequestParam(required = false) String contactPhone, @RequestParam(required = false) MultipartFile picture, HttpServletRequest request) {
            return miniFeedbackService.add(content, contactPhone, picture, request);
        }
    
    展开全文
  • //自定义一个防止重复提交的注解package com.mingwen.common.SubmitMore;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java....

    //自定义一个防止重复提交的注解

    package com.mingwen.common.SubmitMore;

    import java.lang.annotation.Documented;

    import java.lang.annotation.ElementType;

    import java.lang.annotation.Retention;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.Target;

    /**

    * @description 防止表单重复提交注解

    */

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.METHOD)

    @Documented

    public @interface DuplicateSubmitToken {

    // 保存重复提交标记 默认为需要保存

    boolean save() default true;

    }

    //写一个防止重复提交的拦截器

    package com.mingwen.common.SubmitMore;

    import javax.servlet.http.HttpServletRequest;

    import javax.servlet.http.HttpServletResponse;

    import org.aspectj.lang.JoinPoint;

    import org.aspectj.lang.annotation.AfterReturning;

    import org.aspectj.lang.annotation.Aspect;

    import org.aspectj.lang.annotation.Before;

    import org.aspectj.lang.annotation.Pointcut;

    import org.springframework.stereotype.Component;

    import com.mingwen.common.exception.DuplicateSubmitException;

    import cn.hutool.core.lang.UUID;

    import lombok.extern.slf4j.Slf4j;

    /**

    * @description 防止表单重复提交拦截器

    */

    @Aspect

    @Component

    @Slf4j

    public class DuplicateSubmitAspect {

    public static final String DUPLICATE_TOKEN_KEY = "duplicate_token_key";

    //需要扫描的controller地址

    @Pointcut("execution(public * com.mingwen.*.controller..*(..))")

    public void webLog() {

    }

    @Before("webLog() && @annotation(token)")

    public void before(final JoinPoint joinPoint, DuplicateSubmitToken token) throws DuplicateSubmitException {

    if (token != null) {

    Object[] args = joinPoint.getArgs();

    HttpServletRequest request = null;

    HttpServletResponse response = null;

    for (int i = 0; i < args.length; i++) {

    if (args[i] instanceof HttpServletRequest) {

    request = (HttpServletRequest) args[i];

    }

    if (args[i] instanceof HttpServletResponse) {

    response = (HttpServletResponse) args[i];

    }

    }

    boolean isSaveSession = token.save();

    if (isSaveSession) {

    Object t = request.getSession().getAttribute(DUPLICATE_TOKEN_KEY);

    if (null == t) {

    String uuid = UUID.randomUUID().toString();

    request.getSession().setAttribute(DUPLICATE_TOKEN_KEY, uuid);

    // log.info("进入方法:token=" + uuid);

    } else {

    throw new DuplicateSubmitException("请不要重复请求!");

    }

    }

    }

    }

    @AfterReturning("webLog() && @annotation(token)")

    public void doAfterReturning(JoinPoint joinPoint, DuplicateSubmitToken token) {

    // 处理完请求,返回内容

    // log.info("出来方法:");

    if (token != null) {

    Object[] args = joinPoint.getArgs();

    HttpServletRequest request = null;

    for (int i = 0; i < args.length; i++) {

    if (args[i] instanceof HttpServletRequest) {

    request = (HttpServletRequest) args[i];

    }

    }

    boolean isSaveSession = token.save();

    if (isSaveSession) {

    Object t = request.getSession().getAttribute(DUPLICATE_TOKEN_KEY);

    if (null != t) {

    // 方法执行完毕移除请求重复标记

    request.getSession(false).removeAttribute(DUPLICATE_TOKEN_KEY);

    // log.info("方法执行完毕移除请求重复标记!");

    }

    }

    }

    }

    }

    //自定义一个表单重复提交异常

    /**

    * @author wsj 自定义异常

    */

    public class DuplicateSubmitException extends Exception {

    public DuplicateSubmitException(String msg) {

    super(msg);

    }

    }

    //遇到重复提交的异常要将信息返回到前端显示  拦截器抛的异常在controller是无法捕获到的 所以这里统一异常捕获类 控制器增强

    @ControllerAdvice

    @ResponseBody

    @Slf4j

    public class ExceptionCatch  {

    protected Map getResultObjectData(ResultCode result, Object pd, String msg) {

    Map resultData = new HashMap<>();

    resultData.put("code", result.getCode());

    resultData.put("message", result.getMsg());

    resultData.put("data", pd);

    return resultData;

    }

    // 捕获CustomException此类异常

    @ExceptionHandler(DuplicateSubmitException.class)

    public Map customException(DuplicateSubmitException bmException) {

    bmException.printStackTrace();

    // 记录日志

    log.error("catch exception:{}", "不可重复提交");

    return getResultObjectData(ResultCode.NO_SUBMIT, "", "不可重复提交");

    }

    }

    //测试controller

    /**

    * 防止重复提交测试

    *

    * @param request

    * @return

    */

    @DuplicateSubmitToken

    @RequestMapping("/test")

    public Map test(HttpServletRequest request) {

    try {

    //休眠的目的是让你自己在前端能有时间重复点击一下接口调用

    Thread.sleep(5000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    Map map = new HashMap<>();

    //往request 里面存入每次请求的接口地址 这样控制器能能拿到request里面的请求地址

    request.getSession().setAttribute("request Url", request.getRequestURL());

    map.put("request Url", request.getRequestURL());

    return map;

    }

    //效果

    接口快速点击两次

    10472afdc27d22ac28e1175db42c2183.png

    展开全文
  • 后端怎么防止重复提交?(常用的做法) 客户端的抖动,快速操作,网络通信或者服务器响应慢,造成服务器重复处理。防止重复提交,除了从前端控制,后台也需要控制。因为前端的限制不能解决彻底。接口实现,通常要求...

    后端怎么防止重复提交?(常用的做法)

    客户端的抖动,快速操作,网络通信或者服务器响应慢,造成服务器重复处理。防止重复提交,除了从前端控制,后台也需要控制。因为前端的限制不能解决彻底。接口实现,通常要求幂等性,保证多次重复提交只有一次有效。对于更新操作,达到幂等性很难。

    常用后端防止重复提交方案

    • token

    访问请求到达服务器,服务器端生成token,分别保存在客户端和服务器。提交请求到达服务器,服务器端校验客户端带来的token与此时保存在服务器的token是否一致,如果一致,就继续操作,删除服务器的token。如果不一致,就不能继续操作,即这个请求是重复请求。

    • 缓存

    request进来,没有就先存在缓存中,继续操作业务,最后删除缓存或者缓存设置生命周期。如果存在,就直接对request进行验证,就不能继续操作业务。

    • 索引

    数据库中创建唯一索引,记录每次request请求。添加索引成功,就获取锁,继续操作,最后设置索引失效。添加索引失败,获取锁失败,不能继续操作。

    • Redis的计数器

    Redis的计数器是原子操作,不存储请求,又能提升QPS的峰值。每次request请求,若相同请求,计数器+1,否则新建id为key的计数器。如果>1,不能获取锁;如果=1,获取锁,操作,最后删除计数器(删除锁)。

    • Post/Redirect/Get

    提交(Post)后执行页面重定向,成功后转到提交成功页面(Get),整个流程才算结束。当刷新页面,或者浏览器前进和后退,都不会引起Post请求的重复提交。这里可以在head中设置control-cache,保存表单信息。这个方法依赖前端限制比较多。

    展开全文
  • 原创 一筐大白菜啊 最后发布于2019-12-11 09:27:21 阅读数 26 收藏展开THINKPHP5 前后端分离项目,怎样防鼠标双击产生的重复提交?在http/middleware目录下加一个中间件并在路由中设为全局,每次有POST提交时,就将...

    原创 一筐大白菜啊 最后发布于2019-12-11 09:27:21 阅读数 26 收藏

    展开

    THINKPHP5 前后端分离项目,怎样防鼠标双击产生的重复提交?

    在http/middleware目录下加一个中间件并在路由中设为全局,每次有POST提交时,就将数据MD5成字符串,然后检查redis中是否存在相同的数据,有则拒绝此次请求,没有就把字符串存入redis使用下次请求校验

    public function handle(Request $request, \Closure $next)

    {

    // 只检查POST数据

    if ($request->method() == 'POST') {

    $post = $request->post();

    $post['time'] = time();

    // 转md5

    $key = "checkRepeat:" . md5(json_encode($post));

    //此限制只阻止同一秒内产生的重复提交,并不阻止每隔一秒发送一次的重复提交

    if (Redis::get($key)) {

    die("正在处理中,请勿重复提交!");

    }

    // 缓存时间(10秒)

    Redis::set($key, 1, 10);

    }

    return $next($request);

    }

    ————————————————

    版权声明:本文为CSDN博主「一筐大白菜啊」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

    原文链接:https://blog.csdn.net/sphinx1122/article/details/103477261

    展开全文
  • 2、将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的...3、服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就...
  • 防止按钮重复点击 按钮是前端界面中承接着用户操作的很重要的一个环节,前端界面用户和系统的交互都通过按钮来完成,与系统的交互自然就少不了把用户的意向保存到系统中,如今面对前后端分离的部署方案,前端与后端...
  • 干货实战~Java如何防止接口重复提交

    千次阅读 2021-02-12 22:01:19
    简单地讲,这其实就是“重复提交”的话题,本文将从以下几个部分展开介绍:“重复提交”简介与造成的后果“防止接口重复提交”的实现思路“防止接口重复提交”的代码实战1、“重复提交”简介与造成的后果对于“重复...
  • 前台操作的抖动,快速操作,网络通信或者后端响应慢,都会增加后端重复处理的概率。 情形 由于用户误操作,多次点击表单提交按钮。 由于网速等原因造成页面卡顿,用户重复刷新提交页面。 黑客或恶意用户使用postman...
  • 方案一:利用Session防止表单重复提交 具体的做法:  1、获取用户填写用户名和密码的页面时向后台发送一次请求,这时后台会生成唯一的随机标识号,专业术语称为Token(令牌)。 2、将Token发送到客户端的Form表单中,...
  • 之前在网上看过有很多种解决重复提交的方式,在这里我想做一种特别简单实用的方式,亲测有效。 @RestController public class RedisTestController { @Autowired RedisTemplate redisTemplate; @GetMapping("/...
  • 点击上方 "编程技术圈"关注,星标或置顶一起成长后台回复“大礼包”有惊喜礼包!每日英文Life is like a cup of tea. It won't be...
  • 有位朋友,某天突然问东哥:在 Java 中,防止重复提交最简单的方案是什么? 这句话中包含了两个关键信息,第一:防止重复提交;第二:最简单。 于是东哥问他,是单机环境还是分布式环境? 得到的反馈是单机环境,那...
  • 当用户重复点击按钮,会出现多次提交表单 。 二重复提交表单操作 1 通常是鼠标的误操作,如双击了递交按钮。 2 可能是为了编辑或者再次核对填写过的信息,点击了浏览器的后退按钮,然后又再次点击了递交按钮而...
  • 用户在操作表单Post数据时往往会出现表单数据重复提交的问题,尤其在Web开发中此类问题比较常见。刷新页面,后退操作以前的页面,单机多次按钮都会导致数据重复提交。...此方法从根本上的防止了数据重复提...
  • Java中如何避免重复提交请求

    千次阅读 2021-03-06 02:23:33
    点击上方蓝字关注我们一、背景我们在使用系统过程中,经常碰到这种情况:...二、产生原因导致重复请求的原因很多,大体为以下几种:多次点击提交按钮反复刷新页面点击浏览器后退按钮,导致重复提交表单浏览器重复的H...
  • 若依如何防止请求重复提交呢?一般有两种解决方案 第一种:前端处理,在提交之后,将按钮禁用 第二种:后端处理,若依已经提供相关注解,直接使用即可。 后端处理:可以通过@RepeatSubmit注解控制 /** * 在...
  • 表单的重复提交,开发中难免遇到这个情况,用户操作前端页面提交表单,可能连续点击提交按钮,有些业务一旦重复提交,可能会导致很严重的后果,如购买商品,支付订单时重复下单了,如何解决表单重复请求的问题: ...
  • 介绍两种vue项目中防止用户在一定时间内频繁点击按钮触发事件的方法第一种方法:在规定时间内将按钮禁用的方法1.主要思想就是禁止用户在一定的时间多次点击,在一定时间内将按钮禁用,用定时器实现,一定时间之后...
  • 产生原因 由于重复点击或者网络重发 eg: 点击提交按钮两次; 点击刷新按钮; 使用浏览器后退按钮重复之前的操作,导致重复提交表单; 使用浏览器历史记录重复提交表单; 浏览器重复的HTTP请; nginx重发等情况; 分布式RPC...
  • 对于一些用户请求,在某些情况下是可能重复发送的,如果是查询类操作并无大碍,但其中有些是涉及写入操作的,一旦重复了,可能会导致很严重的后果,例如交易的接口如果重复请求可能会重复下单。重复的场...
  • 浅谈一下如何避免用户多次点击造成的多次请求一、有效地在web客户端采用一定机制去防止重复点击提交,将大大减轻服务器端压力。1> 定义标志位:点击触发请求后,标志位为false量;请求(或者包括请求后具体的业务...
  • 有位朋友,某天突然问磊哥:在 Java 中,防止重复提交最简单的方案是什么?这句话中包含了两个关键信息,第一:防止重复提交;第二:最简单。于是磊哥问他,是单机环境还是分布式环境?得到的反馈是单机环境,那就...
  • 场景: 1,前端可以拉取2个支付页面,这里的支付页面,指的就是支付宝或者微信等第三方渠道的输入密码进行支付的页面。...2,后端预支付接口、支付回调接口、退款接口如何设计,才能保证支付的幂等性呢? ...
  • 转:https://blog.csdn.net/u011821334/article/details/79390980转:https://blog.csdn.net/joshua1830/article/details/78931420转:https://blog.csdn.net/IAlexanderI/article/details/80253158(mysql的防重复提交...
  • 这篇文章主要介绍了Java后台防止客户端重复请求、提交表单实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下前言在Web / App项目中,有一些请求或操作会...
  • 在不考虑对接前端,本文先以代码形式说明后端实现 一、切点,表示需要防重复提交的地方 /** * 防重复提交切点注解 * @author XINGZI.QI * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) ...
  • Axios 应用: 实现后端接口封装 & 重复请求回避(撤销请求) 文章目录Axios 应用: 实现后端接口封装 & 重复请求回避(撤销请求)简介参考完整示例代码正文目标axios 中的请求撤销1. CacelToken.source 工厂方法2....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,614
精华内容 14,245
关键字:

后端如何防止重复点击