解决方案
一、token机制
场景一:前端重复提交
前端发起请求时,进行多次提交
解决方案:
token的获取可以和前端进行沟通,例如:在进入订单提交页面时(还未点击提交订单),通过钩子函数去获取token
1.新建一个注解,并编写AOP切面类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Idempotent {
}
@Slf4j
@Component
@Aspect
public class IdempotentAspect {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Pointcut("@annotation(top.hyqstudio.idempotent.annotation.Idempotent)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 使用RequestContextHolder来获取当前线程的请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request= attributes.getRequest();
String token = request.getHeader("token");
log.info("token:" + token);
if (ObjectUtils.isEmpty(token)) {
log.error("token为空,非法请求");
throw new RuntimeException("token为空,非法请求");
}
Boolean b = redisTemplate.delete(token);
if (Boolean.FALSE.equals(b)) {
log.error("重复请求");
throw new RuntimeException("重复请求");
}
return joinPoint.proceed();
}
}
2.编写两个接口
一个获取token接口,一个业务接口,其中业务接口加上 @Idempotent
注解
@RestController
@RequestMapping("/idempotent")
public class IdempotentController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@GetMapping("/token")
public String getToken() {
String token = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(token, token, 5, TimeUnit.MINUTES);
return token;
}
@Idempotent
@PostMapping
public String idempotent() {
//提交订单
//业务逻辑。。。。
return "success";
}
}
3.jmeter测试
提前获取token
18f260f2-c665-4d32-ba65-7d88579fd840
配置jmeter参数
结果为一个请求成功: