zl程序教程

您现在的位置是:首页 >  Java

当前栏目

自定义注解实现防重复提交(纯后端解决)

2023-02-18 16:43:34 时间

1.思路

当请求的时候,直接根据 token(或者其他标识)+请求信息(自定义)= 唯一的 key 然后把这个 key 存储在 cache 中 借助 guava 的 key 过期,redis 的自动过期、数据库的过期

2.操作流程

1.获取唯一标识符(例如在登录时返回的token) 2.发起请求(携带token进行访问) 3.在进入请求之前进行处理 3.1 根据token获取缓存中的数据 3.2 判断数据是否存在,如果不存在访问则放行,存在数据则终止请求 3.3 放行后把访问的令牌和访问路径放入缓存中

3.代码

依赖库

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.6.8</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

1.创建注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RepeatSubmit {
    /**
     * 重复提交间隔时间
     */
    int time() default 3000;

    String msg() default "请勿重复提交";
}

创建访问的资源路径

@RestController
@RequestMapping("/order")
public class OrderController {

    @GetMapping("/login")
    public String login(String username, String password) {
        Assert.isTrue("admin".equals(username) && "admin".equals(password), "用户名或密码错误");
        return UUID.randomUUID().toString();
    }

    @RepeatSubmit(time = 5000)
    @GetMapping("/createOrder")
    public String createOrder() {
        return "order:" + UUID.randomUUID();
    }
}

创建AOP切面类

@Slf4j
@Component
@Aspect
public class RepeatSubmitAspect {

    private static final String TOKEN = "token";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Around("@annotation(repeatSubmit)")
    private Object doAround(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) {
        HttpServletRequest request = Objects.requireNonNull(getRequestAttributes()).getRequest();
        // 获取访问路径
        String uri = request.getRequestURI();
        // 获取唯一标识符
        String token = request.getParameter(TOKEN);
        //获取缓存中的数据
        String value = redisTemplate.opsForValue().get(token);
        //判断缓存中是否存在
        if (uri.equals(value)) {
            return repeatSubmit.msg();
        } else {
        	//不存在,放行请求,并设置缓存
            redisTemplate.opsForValue().set(token, uri, repeatSubmit.time(), TimeUnit.MILLISECONDS);
        }
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return proceed;
    }

	
    public static ServletRequestAttributes getRequestAttributes() {
        try {
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            return (ServletRequestAttributes) attributes;
        } catch (Exception e) {
            return null;
        }
    }
}