Vue/Spring Boot限制用户接口访问次数

限制用户接口访问次数

前端控制

前端控制用户接口的访问次数可以使用Local Storage实现

案例:限制用户一小时内只能提交10次请求

handleSubmit() {
      if (!this.canSubmit) return;

      const nowTime = new Date().getTime();
      const data = Object.assign({ advisoryTime: nowTime }, this.redisterData);
      data.classificationId = this.$route.query.classificationId;

       const submitFrequency = getLocalStorage('solutionRegisterFrequency');
       const submitTime = getLocalStorage('solutionRegisterTime');
       if (!submitTime || nowTime - submitTime > 3600000) {
      solutionRegister(data).then((res) => {
        if (res.code === '200') {
           setLocalStorage('solutionRegisterFrequency', 1);
           setLocalStorage('solutionRegisterTime', nowTime);
          this.dialogMsg = this.successMsg;
          this.clearSubmitData();
        } else {
          this.dialogMsg = this.errorMsg;
        }
      })
        .catch((err) => {
          if (err.data.code === '403') {
            this.dialogMsg = this.warningMsg;
          } else {
            this.dialogMsg = this.errorMsg;
          }
        })
        .finally(() => {
          this.showDialog = true;
        });
       } else if (submitFrequency < 10) {
         solutionRegister(data).then((res) => {
           if (res.code === '200') {
             setLocalStorage('solutionRegisterFrequency', submitFrequency + 1);
             this.dialogMsg = this.successMsg;
             this.clearSubmitData();
           } else {
             this.dialogMsg = this.errorMsg;
           }
         });
       } else {
         this.dialogMsg = this.warningMsg;
       }
       this.showDialog = true;
    }

每次进入handleSubmit这个函数,使用getLocalStorage从Local Storage中取出key solutionRegisterFrequency对应的value submitFrequency,代表一小时内访问这个函数的频率,从Local Storage中取出key solutionRegisterTime对应的value submitTime ,代表上一次函数的访问时间,进行判断,如果submitTime为空或者当前时间减去submitTime 大于一小时,则代表这是这一个小时内第一次使用这个函数。

继而向后台发送请求,solutionRegister,若响应成功,则使用setLocalStorage向本地存储设置submitFrequency值为1,设置solutionRegisterTime为当前时间,为下一次做判断。若一小时内发送请求时solutionRegisterTime小于10,则可以继续发送请求,并且每成功发送一次,solutionRegisterTime的值加1。

后端控制

除了前端可以使用Local Storage实现,后端也可以用redis来对用于访问接口次数做限制。

String remoteIp = null;
if (request.getHeader("x-forwarded-for") == null) {
    remoteIp = request.getRemoteAddr();
} else {
    remoteIp = request.getHeader("x-forwarded-for");
}
int advisoryTimes = solutionAdvisoryService.getAdvisoryTimes(remoteIp);
if (advisoryTimes > 10) {
    return new JsonResponse<>(StatusCode.FORBIDDEN, "操作太频繁!请一小时后尝试");
}

在每次进入接口时,从redis中取出当前用户key值所对应的value,判断是否大于10。这里对应的用户的唯一key值有多种选择,若用户已经是登录状态,可以使用用户id作为redis的唯一key值,我这里是用户没有登录的状态,所以选择了用户的ip地址作为redis的key值。在取用户ip地址时也要注意,如果项目使用了nginx代理,则不能直接通过request.getRemoteAddr()来取用户ip地址,具体详见:https://www.cnblogs.com/xdjackfeng/p/3514120.html

redis service代码

 

private static final String SOLUTION_ADVISORY_PREFIX = "imcp.solution.advisory.prefix:";

    private static final int RETRY_INTERVAL =  60 * 60 * 1000;

    @Autowired
    private RedisTemplate  redisTemplate;

    /**
     * 获取用户产品咨询次数
     * @param ip
     * @return
     */
    public int getAdvisoryTimes(String ip) {

        String principle = ip;

        Boolean hasKey = redisTemplate.hasKey(SOLUTION_ADVISORY_PREFIX.concat(principle));

        /* 第一次登记 */
        if (hasKey == null || !hasKey) {
            redisTemplate.opsForValue().set(SOLUTION_ADVISORY_PREFIX.concat(principle), 1);
            redisTemplate.expire(SOLUTION_ADVISORY_PREFIX.concat(principle), RETRY_INTERVAL, TimeUnit.MILLISECONDS);
            return 1;
        }

        /* 不是第一次登记 */
        Object advisoryValue = redisTemplate.opsForValue().get(SOLUTION_ADVISORY_PREFIX.concat(principle));

        int advisoryTimes = Integer.parseInt(advisoryValue.toString());
        advisoryTimes++;

//        redisTemplate.opsForValue().set(PRODUCT_ADVISORY_PREFIX.concat(principle), advisoryTimes);
        redisTemplate.opsForValue().increment(SOLUTION_ADVISORY_PREFIX.concat(principle), 1);
        return advisoryTimes;
    }

在用户每访问一次接口时,使用redisTemplate.opsForValue().increment(SOLUTION_ADVISORY_PREFIX.concat(principle), 1);来使当前用户在redis中的value值加1,不过这里也可能会遇到问题。

RedisTemplate increment 错误:ERR value is not an integer or out of range解决

具体详见:https://blog.csdn.net/weixin_42829048/article/details/83989784

控制一小时的过期时间:redisTemplate.expire(SOLUTION_ADVISORY_PREFIX.concat(principle), RETRY_INTERVAL, TimeUnit.MILLISECONDS);

你可能感兴趣的:(Vue,JAVA)