xy1110 2018-04-24 03:53 采纳率: 50%
浏览 3018
已采纳

我想采用SpringBoot实现限制ip访问次数,为啥没有效果?

注解类:图片说明
判断方法:
图片说明
限制的接口方法:
图片说明
麻烦大神帮忙看看,知道一下

  • 写回答

2条回答 默认 最新

  • 苏-李 2018-04-24 05:38
    关注

    有时候存在着一些恶意访问的情况,为了阻止这种情况的发生,我们可以写一个拦截器,当某个IP的访问在单位时间内超过一定的次数时,将禁止他继续访问。
    在这里我们使用了SpringBoot搭配注解来使用
    除了springboot需要的依赖之外,我们还需要加上Aspect依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.10.RELEASE</version>
    </dependency>
    

    1
    2
    3
    4
    5
    6
    我们可以自定义一个注解,里面填入单位时间长度和单位时间访问的最大次数

    package com.example.web.demo.test;
    

    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;

    import java.lang.annotation.*;

    /**

    • @Author:高键城
    • @time:
    • @Discription:
      /
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      @Documented
      @Order(Ordered.HIGHEST_PRECEDENCE)
      public @interface RequestLimit {
      /
      *

      • 允许访问的最大次数 */ int count() default Integer.MAX_VALUE;

      /**

      • 时间段,单位为毫秒,默认值一分钟 */ long time() default 60000; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 接下来我们要定义一个异常类

    package com.example.web.demo.test;

    /**

    • @Author:高键城
    • @time:
    • @Discription:
      */
      public class RequestLimitException extends Exception {
      private static final long serialVersionUID = 1364225358754654702L;

      public RequestLimitException(){
      super("HTTP请求超出设定的限制");
      }

      public RequestLimitException(String message){
      super(message);
      }
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      有了注解之后,我们要对注解实行一些相应的操作

      package com.example.web.demo.test;

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;

    import javax.servlet.http.HttpServletRequest;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Timer;
    import java.util.TimerTask;

    /**

    • @author Administrator
    • @time:
    • @Discription:
      */
      @Aspect
      @Component
      public class RequestLimitContract {
      private static final Logger logger = LoggerFactory.getLogger("requestLimitLogger");
      private Map redisTemplate = new HashMap<>();

      @Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)")
      public void requestLimit(final JoinPoint joinPoint , RequestLimit limit) throws RequestLimitException {
      try {
      Object[] args = joinPoint.getArgs();
      HttpServletRequest request = null;
      for (int i = 0; i < args.length; i++) {
      if (args[i] instanceof HttpServletRequest) {
      request = (HttpServletRequest) args[i];
      break;
      }
      }
      if (request == null) {
      throw new RequestLimitException("方法中缺失HttpServletRequest参数");
      }
      String ip = request.getLocalAddr();
      String url = request.getRequestURL().toString();
      String key = "req_limit_".concat(url).concat(ip);
      if (redisTemplate.get(key) == null || redisTemplate.get(key) == 0) {
      redisTemplate.put(key, 1);
      } else {
      redisTemplate.put(key, redisTemplate.get(key) + 1);
      }
      int count = redisTemplate.get(key);
      if (count > 0) {
      //创建一个定时器
      Timer timer = new Timer();
      TimerTask timerTask = new TimerTask() {
      @Override
      public void run() {
      redisTemplate.remove(key);
      }
      };
      //这个定时器设定在time规定的时间之后会执行上面的remove方法,也就是说在这个时间后它可以重新访问
      timer.schedule(timerTask, limit.time());
      }
      if (count > limit.count()) {
      logger.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
      throw new RequestLimitException();
      }
      }catch (RequestLimitException e){
      throw e;
      }catch (Exception e){
      logger.error("发生异常",e);
      }
      }
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      @before 注解代表在请求发送到控制器之前会先来到这里执行相应的内容,within里面的书写表示写在控制器上方并且有对应注解的控制器会来到这里。
      在获得ip和对应的url之后将它作为一个key,存放到map中,假如map中已经存在了这个key,那么多产生一个时间处理器,当时间超过注解书写的单位时间之后又会将这个key从map中移走。
      假如访问的次数超过了限制,将会抛出异常说明访问次数过多
      最后在使用控制器的时候加上对应的注解即可

    @Controller
    public class testController{
    @RequestMapping("/hello")
    @RequestLimit(count=10,time=60000)
    public String test(HttpServletRequest request){
    return "hello";
    }
    }

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 python天天向上类似问题,但没有清零
  • ¥30 3天&7天&&15天&销量如何统计同一行
  • ¥30 帮我写一段可以读取LD2450数据并计算距离的Arduino代码
  • ¥15 C#调用python代码(python带有库)
  • ¥15 矩阵加法的规则是两个矩阵中对应位置的数的绝对值进行加和
  • ¥15 活动选择题。最多可以参加几个项目?
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型
  • ¥15 vs2019中数据导出问题
  • ¥20 云服务Linux系统TCP-MSS值修改?
  • ¥20 关于#单片机#的问题:项目:使用模拟iic与ov2640通讯环境:F407问题:读取的ID号总是0xff,自己调了调发现在读从机数据时,SDA线上并未有信号变化(语言-c语言)