springBoot 限制重复提交

在使用 AOP 和 Redis 结合的方式来限制不同用户短时间内不可重复调用相同接口时,可以通过在 Redis 中存储用户请求的唯一标识来实现。每次用户请求某个接口时,通过用户ID和接口的唯一标识符组合生成一个键,判断该键是否存在于 Redis 中,从而决定是否处理该请求。

下面是一个基于 Spring AOP 和 Redis 的优化实现。

1. 定义自定义注解

首先,定义一个自定义注解 PreventDuplicateSubmit,用于标记需要限制重复提交的接口。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicateSubmit {
    long timeout() default 5000; // 超时时间,单位毫秒
}

2. 创建 AOP 切面类

通过 AOP 来拦截带有 PreventDuplicateSubmit 注解的方法,使用 Redis 来检查和控制请求的重复提交。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class PreventDuplicateSubmitAspect {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Around("@annotation(preventDuplicateSubmit)")
    public Object preventDuplicateSubmit(ProceedingJoinPoint joinPoint, PreventDuplicateSubmit preventDuplicateSubmit) throws Throwable {
        // 获取用户ID或其他唯一标识
        String userId = getCurrentUserId(); // 需要实现获取当前用户ID的方法
        String methodName = joinPoint.getSignature().toShortString();
        String redisKey = "duplicate_submit:" + userId + ":" + methodName;

        // 尝试在Redis中设置键,使用SETNX实现锁
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, "1", preventDuplicateSubmit.timeout(), TimeUnit.MILLISECONDS);

        if (Boolean.FALSE.equals(success)) {
            return new ResponseEntity<>("重复提交", HttpStatus.BAD_REQUEST);
        }

        try {
            // 执行被拦截的方法
            return joinPoint.proceed();
        } finally {
            // 可选:清理redisKey,取决于业务场景
            // stringRedisTemplate.delete(redisKey);
        }
    }

    // 获取当前用户ID的实现,可以根据具体需求获取
    private String getCurrentUserId() {
        // 这里假设使用Spring Security的上下文获取当前用户ID
        // return SecurityContextHolder.getContext().getAuthentication().getName();
        // 或者从请求上下文中获取,如:从HttpServletRequest中获取token解析用户ID
        return "testUserId"; // 示例返回,实际应根据项目情况实现
    }
}

3. 在控制器中使用自定义注解

在需要限制重复提交的接口上添加 @PreventDuplicateSubmit 注解。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @PreventDuplicateSubmit(timeout = 10000) // 设置10秒内不可重复提交
    @GetMapping("/submit")
    public String handleSubmit() {
        // 处理逻辑
        return "提交成功";
    }
}

4. 配置 Redis

确保 Redis 配置正确,Spring Boot 项目中可以在 application.propertiesapplication.yml 中进行配置:

spring:
  redis:
    host: localhost
    port: 6379
    # 可根据实际情况配置其他参数,如密码、连接池配置等

总结

  • 注解:定义了一个 @PreventDuplicateSubmit 注解,用于标记需要限制重复提交的方法,并指定超时时间。
  • AOP 切面:使用 AOP 拦截带有该注解的方法,通过 Redis 存储用户请求的唯一标识符来控制短时间内的重复提交。使用 setIfAbsent 实现分布式锁,避免多实例环境下的并发问题。
  • Redis 维护:在请求处理后,如果不需要长期保持,可以选择性地删除 Redis 中的键。
  • 用户标识:需要实现获取当前用户唯一标识的方法,可以基于安全框架或其他认证方式。

这样做不仅可以有效地限制重复提交,还可以应对分布式部署的场景,并且易于扩展和维护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值