赞
踩
幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次
比如:
本文采用第2种方式实现, 即通过redis + token机制实现接口幂等性校验
本文主要处理场景:同一个用户,一个请求,在规定的时间内只能发起1次请求。
这边主要处理的防止页面重复提交,为保证幂等性,请求接口时,后端通过header或者接口请求参数获取登录信息+请求路径判断redis中是否存在此key。
请勿重复操作
提示说明:
- <!-- Redis 依赖 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
-
- #########################本地开发环境#########################
- ##spring boot 配置
- server.port=8004
- spring.application.name=share
- ############################################################
- ## MySQL配置
- spring.datasource.url=jdbc:mysql://192.168.1.12:3306/share?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
- spring.datasource.username=root
- spring.datasource.password=123456
- spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
- ############################################################
- ## Redis配置
- spring.redis.host=192.168.1.12
- #spring.redis.password=
- spring.redis.database=1
- spring.redis.port=6379
- ############################################################
2.自定义注解 @ReSubmitCheck
- import java.lang.annotation.*;
-
- /**
- * 在需要保证接口幂等性的Controller的方法上使用此注解
- * 重复提交校验注解
- */
- @Target({ElementType.PARAMETER, ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface ReSubmitCheck {
- //校验几秒内重复提交
- int seconds() default 3;
- }
3. 防止重复提交切面处理器 PreventReSummitAspect
- import com.city.share.annotation.ReSubmitCheck;
- import com.city.share.enums.ResultEnum;
- import com.city.share.exception.ShareException;
- import lombok.extern.slf4j.Slf4j;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.stereotype.Component;
- import org.springframework.web.context.request.RequestContextHolder;
- import org.springframework.web.context.request.ServletRequestAttributes;
- import javax.servlet.http.HttpServletRequest;
- import java.util.concurrent.TimeUnit;
-
- /**
- * 防重复点击
- */
- @Aspect
- @Component
- @Slf4j
- public class PreventReSummitAspect {
-
- /**
- * redis工具类
- */
- @Autowired
- private StringRedisTemplate redisTemplate;
-
- @Before("@annotation(reSubmitCheck)")
- public void preventReSubmit(JoinPoint joinPoint, ReSubmitCheck reSubmitCheck) {
-
- ServletRequestAttributes attributes =
- (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- //获取用户登录的accesstoken
- HttpServletRequest request = attributes.getRequest();
- String token = request.getParameter("accesstoken");
- if (token == null) {
- throw new ShareException(ResultEnum.ON_LOGIN);
- }
- String lockKey = "ReSubmit:" + token + "_" + request.getServletPath();
- Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, lockKey, reSubmitCheck.seconds(), TimeUnit.SECONDS);
-
- if (!result) {
- System.out.println("重复请求:"+lockKey);
- throw new ShareException(ResultEnum.RESUBMIT_ERROR);
- }
- }
-
- }
4.测试controller HolleContraller
- import com.city.share.Dto.Result;
- import com.city.share.Utils.ResultUtil;
- import com.city.share.annotation.ReSubmitCheck;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.io.Serializable;
-
- @RestController
- public class HolleContraller implements Serializable{
-
- @GetMapping("/holle")
- @ReSubmitCheck(seconds=10)//这边设置了10秒内不能重复访问
- public Result holleTest(){
- System.out.println("hello spring boot");
- return ResultUtil.success("hello spring boot");
- }
- }
OK, 目前为止, 校验代码准备就绪, 接下来测试验证
访问:127.0.0.1:8004/holle?accesstoken=123456
查看redis
测试接口安全性: 利用jmeter测试工具模拟10个并发请求
请求结果:因为都是在10秒内,所以只有第一个请求成功
其实思路很简单, 就是每次请求保证唯一性, 从而保证幂等性, 通过spring aop+注解, 就不用每次请求都写重复代码, 其实也可以利用拦截器实现。
如果小伙伴有什么疑问或者建议欢迎提出
源码地址:https://download.csdn.net/download/zppiio/85309475https://download.csdn.net/download/zppiio/85309475
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。