实现防止接口重复访问-Java-SpringBoot

接口请求拦截,防止重复访问

方法一:注解

AOP+Redis
依赖导入

<!--        spring aop依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
 
<!--        工具类依赖-->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
 
<!--        reids依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

注解类

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitAccessTimes {
    /**
     * 指定时间内不可重复提交,单位:s
     *
     * @return
     */
    long timeout() default 5;
}

切面实现类

@Aspect
@Component
@Slf4j
public class LimitAccessTimesAspect {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Before("@annotation(com.leayun.dubhe.annotation.LimitAccessTimes)")
    public void repeatSumbitIntercept(JoinPoint joinPoint)  throws Exception{
        // userTicket + 类名 + 方法 + timeout
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        Cookie[] cookieList = request.getCookies();
//        String userTicket = null;
//        for (Cookie cookie : cookieList) {
//            if (cookie.getName().equals("userTicket")) {
//                userTicket = cookie.getValue();
//                break;
//            }
//        }
        //获取当前切面所设置的方法的类名、方法名
        String className = joinPoint.getTarget().getClass().getName();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();

        // 获取配置的过期时间
        LimitAccessTimes annotation = method.getAnnotation(LimitAccessTimes.class);
        long timeout = annotation.timeout();

//        String key = userTicket + ":" + className + ":" + methodName + ":" + timeout + "s";
        String key =  className + ":" + methodName + ":" + timeout + "s";
        System.out.println(" --- >> 防重提交:key -- {"+key+"}");
        // 判断是否已经超过重复提交的限制时间
        String value = redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(value)) { //如果不为空,说明redis设置的禁止访问时间未过期,抛出异常,禁止访问
            String messge = MessageFormat.format("请勿在{0}s内重复提交", timeout);
            throw new RequestDataException(messge);
        }
        redisTemplate.opsForValue().set(key, JSON.toJSONString(timeout),timeout,TimeUnit.SECONDS);
    }
}

异常捕获

@Slf4j
@ControllerAdvice
public class CustomExceptionHandler {

    @ResponseBody
    @ExceptionHandler(RequestDataException.class)
    public CommonResponse<String> handleException(RequestDataException ex) {
        log.error("捕获到RequestDataException异常", ex);
        return CommonResponse.failure(ex.getLocalizedMessage());
    }


}
@Getter
@Setter
public class RequestDataException extends RuntimeException {

    public RequestDataException(String message) {
        super(message);
    }

}

方法二:拦截器

首先,需要在Spring Boot中定义一个拦截器,用于拦截需要限制访问时间间隔的接口。在拦截器中可以从Redis中获取该用户上次访问该接口的时间,并计算与当前时间的时间间隔。如果时间间隔小于5分钟,则拦截该请求,否则放行。

@Component
public class ApiRequestInterceptor implements HandlerInterceptor {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String userId = request.getHeader("userId"); // 假设从请求头中获取userId
        String apiPath = request.getRequestURI(); // 获取接口路径
//        String key = "api_request_interval_" + userId + "_" + apiPath;
        String key = "api_request_interval:" + userId + ":" + apiPath;
        String lastAccessTime = redisTemplate.opsForValue().get(key);
        if (lastAccessTime != null) {
            long interval = System.currentTimeMillis() - Long.parseLong(lastAccessTime);
            if (interval < 1 * 60 * 1000) { // 假设限制访问间隔为5分钟
                response.setContentType("application/json;charset=utf-8");
                response.getWriter().write("{\"code\": 1002, \"msg\": \"访问时间间隔过短\"}");
                return false;
            }
        }
        redisTemplate.opsForValue().set(key, JSON.toJSONString(System.currentTimeMillis()));

        return true;
    }
}

在上面的代码中,我们假设从请求头中获取了userId,然后将其与接口路径拼接成Redis中的键,用于存储该用户上次访问该接口的时间。在preHandle方法中,首先从Redis中获取该用户上次访问该接口的时间,如果时间间隔小于5分钟,则返回错误信息;否则,将当前时间存储到Redis中,并放行该请求。

最后,需要在Spring Boot的配置类中注册拦截器:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Resource
    private ApiRequestInterceptor apiRequestInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(apiRequestInterceptor).addPathPatterns("/param/saveData/limitTimesNew");
    }
}

在上面的代码中,我们假设需要限制访问时间间隔的接口都以“api”为前缀,因此只需要对以“api”开头的请求进行拦截即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值