springBoot+redis禁止重复提交

在这里插入图片描述
禁止重复提交

1.为什么要禁止重复提交?

在我们平时开发的过程中,有很多用户点击提交按钮提交表单或者说用户主动提交某些信息的情景。正常情况下,我们后台正常接收前台提交的内容,然后再进行增删改查等操作。但是,我们都说不能已常理去考虑用户的使用情况。一旦前台提交内容后,因为网络波动或者后台逻辑处理较慢,而前台又没有做禁止点击提交按钮或者等待页面,难免出现用户疯狂点击提交按钮的情况。这种情况就很有可能导致用户的数据多次提交、入库,产生脏数据、冗余数据等情况。

上述只是可能会出现重复提交或者请求的一种情况,实际上,会造成这种情况的场景不少:

  • 网络波动:
    因为网络波动,造成重复请求
  • 用户的重复性操作:
    用户误操作,或者因为接口响应慢,而导致用户耐性消失,有意多次触发请求
  • 重试机制:
    这种情况,经常出现在调用三方接口的时候。对可能出现的异常情况抛弃,然后进行固定次数的接口重复调用,直到接口返回正常结果。
  • 分布式消息消费:
    任务发布后,使用分布式消息服务来进行消费(这个我还没有碰到过)

总而言之,禁止重复提交使我们保证数据准确性及安全性的必要操作。

2.springBoot+redis禁止重复提交

首先明确一下思路:

  • 这里验证的是: 同一客户端2s内请求同样的url,即视为重复提交
  • 在请求进入业务方法之前,进入切面。已sessionId+url作为key,在redis中查询,看是否存在。存在即为重复提交。
  • 如果是重复提交则抛出异常,交由controllerAdvice处理。如果不是,则正常处理业务逻辑。

首先定义一个注解,用来表示那些方法需要实现接口幂等性。

/**
 * @ClassName ForbidRepeatCommit
 * @Description 禁止重复提交注解
 * @Author
 * @Version V1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ForbidRepeatCommit {

    String value() default "";

}

然后我们写一个切面,用来在参数进行业务操作前,验证是否为重复提交。这个切面对标注@ForbidRepeatCommit注解的方法进行切入。

/**
 * @ClassName RepeatCommitAspectAmend
 * @Description 校验重复提交切面  修正版  使用session+url机制
 * @Author
 * @Date 2019-10-23 下午 2:53
 * @Version V1.0
 */
@Aspect
@Slf4j
@Component
public class RepeatCommitAspectAmend {


    @Autowired
    TokenService tokenService;

    /***
     * controler包下的方法 并且被@ForbidRepeatCommit注解标注
     */
    @Pointcut("execution(public * com.xs.controller..*(..)) &&  @annotation(com.xs.annotations.ForbidRepeatCommit)  && !execution(public * com.xs.controller.TokenController.*(..)) ")
    public void verifyRequestToken() {

    }


    /***
     * 校验是否为重复提交
     * @param joinPoint
     * @throws Exception
     */
    @Before("verifyRequestToken()")
    public void execVerify02(JoinPoint joinPoint) throws Exception {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
        HttpServletRequest request = attributes.getRequest();
        String key = sessionId + "-" + request.getServletPath();


        //  判断是否为重复提交 使用session+url机制
        tokenService.checkTokenBySessionAndUrl(key);



    }
}

验证是否为重复提交的TokenServiceImpl:

/**
 * @ClassName TokenServiceImpl
 * @Description 表单重复提交检验service
 * @Author
 * @Version V1.0
 */
@Slf4j
@Service
public class TokenServiceImpl implements TokenService {

    @Autowired
    StringRedisTemplate redisTemplate;


    /***
     * 提交的session+url已经存在redis 视为重复提交  
     * @param key
     * @throws Exception
     */
    @Override
    public void checkTokenBySessionAndUrl(String key) throws Exception {

        if(StringUtils.isEmpty(key)){

            log.error(" key为空 ");

            throw new Exception("key为空");
        }

        //如果缓存中存在此key 视为重复提交
        if(redisTemplate.opsForValue().get(key) == null){
            //不存在 放入redis 设置超时时间为2s
            redisTemplate.opsForValue().set(key,key,2, TimeUnit.SECONDS);

        }else{
            log.error(" 重复提交 ");

            throw new Exception("重复提交");
        }

        log.info(" 此请求成功提交表单 ");
    }


}

对controller抛出异常进行统一处理的类:

/**
 * @ClassName ControllerAdviceAspect
 * @Description 全局异常处理
 * @Author
 * @Version V1.0
 */
@Slf4j
@RestControllerAdvice
public class ControllerAdviceAspect {


    /****
     * 处理controller抛出的异常
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Object handleException(HttpServletRequest request, Exception e) {
		//对所有异常进行处理 返回固定的响应体
        ResponseMessage responseMessage = new ResponseMessage();
        responseMessage.setData("");
        responseMessage.setStatus("600");
        responseMessage.setMessage(((UndeclaredThrowableException)e).getUndeclaredThrowable().getMessage());

        return responseMessage;
    }

}

以上基本是禁止重复提交的全部代码了。

事实上,在之前也看过很多禁止重复提交的文章。包括网上流传比较广的redis+token机制。个人觉得并不能完全禁止重复提交,所以这里就不提这个了,如果大家有兴趣的话,可以自己搜索一下。

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringbootRedis是常见的企业级开发系统中使用的技术。Springboot是一个用于快速开发基于Java的应用程序的框架,它简化了配置和部署的过程,提供了很多开箱即用的功能和扩展性。而Redis则是一种高性能的键值存储数据库,它支持各种数据结构和功能,如缓存、消息队列和分布式锁等。 在使用SpringbootRedis开发企业级系统时,可以利用Redis作为缓存来提高系统的性能和响应速度。通过将频繁访问的数据存储在Redis中,可以减少对数据库的访问次数,从而减轻数据库的负载。同时,Redis还提供了一些特有的功能,如发布/订阅机制和事务支持,可以帮助实现系统中的一些复杂业务逻辑。 在项目中集成SpringbootRedis可以通过引入相关的依赖和配置文件来实现。在pom.xml文件中添加相关的依赖,如spring-boot-starter-data-redis和jedis等,然后在application.properties或application.yml文件中配置Redis的连接信息和其他相关配置。 在Springboot中使用Redis可以通过注入RedisTemplate或StringRedisTemplate来进行操作。RedisTemplate提供了更丰富的操作接口,可以直接对各种类型的数据进行读写操作。而StringRedisTemplate则更加简化,主要用于对字符串类型的数据进行操作。 总而言之,SpringbootRedis的结合可以为企业级开发系统带来很多好处,包括提高系统性能、简化开发过程和实现一些复杂的业务逻辑。通过合理使用这两个技术,可以构建出高效、稳定和可扩展的企业级应用系统。<span class="em">1</span> #### 引用[.reference_title] - *1* [Java企业报表管理系统源码](https://download.csdn.net/download/m0_55416028/88269629)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值