赞
踩
禁止重复提交
1.为什么要禁止重复提交?
在我们平时开发的过程中,有很多用户点击提交按钮提交表单或者说用户主动提交某些信息的情景。正常情况下,我们后台正常接收前台提交的内容,然后再进行增删改查等操作。但是,我们都说不能已常理去考虑用户的使用情况。一旦前台提交内容后,因为网络波动或者后台逻辑处理较慢,而前台又没有做禁止点击提交按钮或者等待页面,难免出现用户疯狂点击提交按钮的情况。这种情况就很有可能导致用户的数据多次提交、入库,产生脏数据、冗余数据等情况。
上述只是可能会出现重复提交或者请求的一种情况,实际上,会造成这种情况的场景不少:
总而言之,禁止重复提交使我们保证数据准确性及安全性的必要操作。
2.springBoot+redis禁止重复提交
首先明确一下思路:
首先定义一个注解,用来表示那些方法需要实现接口幂等性。
/**
* @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机制。个人觉得并不能完全禁止重复提交,所以这里就不提这个了,如果大家有兴趣的话,可以自己搜索一下。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。