java 后端做重复提交拦截基于aop

系统管理员 2023-03-14 13:21 29阅读 0赞

基于注解配置与aop完美结合。在指定时间内。用redis+lua脚本获取锁。不会出现插队。看代码实现。

1、创建AopInterceptor

  1. /**
  2. * @author:wwz
  3. */
  4. @Aspect
  5. @Component
  6. public class AopInterceptor{
  7. @Autowired
  8. private RedisUtils redisUtils;
  9. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  10. @Pointcut("@annotation(com.ditop.modules.gcjsy.company.wsba.constant.RejectMultiPost)")
  11. public void pointcut(){
  12. }
  13. private final static class Holder{
  14. private static HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  15. }
  16. @Around("pointcut()&& @annotation(rejectMultiPost)")
  17. public Object around(ProceedingJoinPoint joinPoint, RejectMultiPost rejectMultiPost){
  18. String key = null;
  19. try {
  20. key = PermissionHelper.currentUserSession().getUserId()+Holder.request.getRequestURI();
  21. //这里是redis+lua。一个key在指定时间内。只获取一次。不会出现插队
  22. if(redisUtils.lock(key,key,rejectMultiPost.timeout())){
  23. return joinPoint.proceed(joinPoint.getArgs());
  24. }else{
  25. logger.error("重复提交【代码未执行】");
  26. System.out.println("重复提交【代码未执行】");
  27. }
  28. } catch (Throwable throwable) {
  29. logger.error(throwable.getMessage());
  30. throwable.printStackTrace();
  31. if(key!=null){
  32. redisUtils.unLock(key,key);
  33. }else{
  34. try {
  35. return joinPoint.proceed(joinPoint.getArgs());
  36. } catch (Throwable throwable1) {
  37. logger.error(throwable1.getMessage());
  38. throwable1.printStackTrace();
  39. }
  40. }
  41. }
  42. return JsonResult.success();
  43. }
  44. }

2、创建RedisUtils

  1. /**
  2. * @author wwz
  3. * @date 2020-05-15
  4. * @descrption:
  5. */
  6. @Component
  7. public class RedisUtils {
  8. private final Logger log = LoggerFactory.getLogger(this.getClass());
  9. /* private final String expireTime = "5000";*/
  10. @SuppressWarnings("rawtypes")
  11. @Autowired
  12. private RedisTemplate stringRedisTemplateDemo;
  13. private DefaultRedisScript<String> getLockRedisScript;
  14. private DefaultRedisScript<String> releaseLockRedisScript;
  15. private StringRedisSerializer argsStringSerializer = new StringRedisSerializer();
  16. private StringRedisSerializer resultStringSerializer = new StringRedisSerializer();
  17. private final String EXEC_RESULT = "1";
  18. @SuppressWarnings("unchecked")
  19. @PostConstruct
  20. private void init() {
  21. getLockRedisScript = new DefaultRedisScript<String>();
  22. getLockRedisScript.setResultType(String.class);
  23. releaseLockRedisScript = new DefaultRedisScript<String>();
  24. releaseLockRedisScript.setResultType(String.class);
  25. // 初始化装载 lua 脚本
  26. getLockRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/lock.lua")));
  27. releaseLockRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/releaseLock.lua")));
  28. }
  29. /**
  30. * 加锁操作
  31. * @param key Redis 锁的 key 值
  32. * @param requestId 请求id,防止解了不该由自己解的锁 (随机生成)
  33. * @param expireTime 锁的超时时间(毫秒)
  34. * @param retryTimes 获取锁的重试次数
  35. * @return true or false
  36. */
  37. @SuppressWarnings("unchecked")
  38. public boolean lock(String key, String requestId, String expireTime) {
  39. int retryTimes = 1;
  40. try {
  41. int count = 0;
  42. while (true) {
  43. Object result = stringRedisTemplateDemo.execute(getLockRedisScript, argsStringSerializer, resultStringSerializer,
  44. Collections.singletonList(key), requestId, expireTime);
  45. log.debug("result:{},type:{}", result, result.getClass().getName());
  46. if (EXEC_RESULT.equals(result)) {
  47. return true;
  48. } else {
  49. count++;
  50. if (retryTimes == count) {
  51. log.warn("has tried {} times , failed to acquire lock for key:{},requestId:{}", count, key, requestId);
  52. return false;
  53. } else {
  54. log.warn("try to acquire lock {} times for key:{},requestId:{}", count, key, requestId);
  55. Thread.sleep(500);
  56. continue;
  57. }
  58. }
  59. }
  60. } catch (InterruptedException e) {
  61. e.printStackTrace();
  62. }
  63. return false;
  64. }
  65. /**
  66. * 解锁操作
  67. * @param key Redis 锁的 key 值
  68. * @param requestId 请求 id, 防止解了不该由自己解的锁 (随机生成)
  69. * @return true or false
  70. */
  71. @SuppressWarnings("unchecked")
  72. public boolean unLock(String key, String requestId) {
  73. Object result = stringRedisTemplateDemo.execute(releaseLockRedisScript, argsStringSerializer, resultStringSerializer,
  74. Collections.singletonList(key), requestId);
  75. if (EXEC_RESULT.equals(result)) {
  76. return true;
  77. }
  78. return false;
  79. }
  80. }

3、引入lua脚本。放到resource目录下

lock.lua:

  1. if redis.call('set',KEYS[1],ARGV[1],'NX','PX',ARGV[2]) then
  2. return '1'
  3. else
  4. return '0'
  5. end

releaseLock.lua:

  1. if redis.call('get',KEYS[1]) == ARGV[1] then
  2. return tostring(redis.call('del',KEYS[1]))
  3. else
  4. return '0'
  5. end

4、创建注解:RejectMultiPost

  1. @Documented
  2. @Target(ElementType.METHOD)
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public @interface RejectMultiPost {
  5. /**
  6. * ms
  7. * @return
  8. */
  9. String timeout() default "5000";
  10. }

5、防止重复操作的方法加入注解

20200518095137912.png

超时时间可以自己配置。这个拦截的思路是。某个用户在指定时间内不能请求相同地址。完美破解同一个用户在不同浏览器同时操作一条数据以及同一个浏览器重复单击按钮。不足之处请指出。同是学习

同一个浏览器

发表评论

表情:
评论列表 (有 0 条评论,29人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JAVA AOP防止重复提交

      通过Aop进行防止重复提交, 把 pds-sessionId-请求路径作为请求的唯一key,用户发送请求时,判断key是否存在,如果存在则重复提交,不存在,则进行保存,执