zoukankan      html  css  js  c++  java
  • spring boot 学习(九)小工具篇:?秒防刷新

    注解 + 拦截器:?秒防刷新

    小工具篇:工具许多都是我以前在 github 之类开源平台找到的小工具类,作者的信息什么的许多都忘了。先说声不好意思了。若有相关信息,麻烦提醒一下~

    解释

    所谓的?秒防刷新,其实就是限制用户在某个时间内对某个 Controller 的访问时间限制。最常见的,比如学校教务系统(正方)的3s防刷新。虽然我不知道正方系统具体是如何实现的,不过可以通过 注解+拦截器 来实现。

    前期准备

    关于 注解+拦截器,我在上一篇小工具中已经有所介绍。
    同时,关于系统轮询的问题,可以使用 @Scheduled 来进行一个全系统记录的轮询,但真的没必要……我觉得可以使用 java 自带的定时器小工具 TimerTimerTask
    * Timer 是一种定时器工具,用来在一个后台线程计划执行指定任务。
    * TimerTask 一个抽象类,它的子类或者重写run方法代表一个可以被Timer计划的任务

    参考资料:(Timer与TimerTask详解)http://blog.csdn.net/ahxu/article/details/249610

    正式开工

    • 自定义异常类
      HttpServletException : 用于记录http请求或响应异常。
      RequestLimitException : 用于记录用户HTTP请求超出设定的限制。
    public class RequestLimitException extends Exception {
        public RequestLimitException() {
            super("HTTP请求超出设定的限制");
        }
    
        public RequestLimitException(String message) {
            super(message);
        }
    
        public RequestLimitException(String message, Throwable cause){
            super(message, cause);
        }
    }
    
    •  注解类 RequestLimit.java
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @Documented
    public @interface RequestLimit {
    
        //允许访问的次数,默认值MAX_VALUE
        int count() default Integer.MAX_VALUE;
    
        // 时间段,单位为毫秒,默认值一分钟
        long time() default 60000;
    
    }
    
    •  拦截器 RequestLimitContract.java
    @Aspect
    @Component
    public class RequestLimitContract {
        private static final Logger logger = LoggerFactory.getLogger(RequestLimitContract.class);
    
        //用于存储记录
        private Map<String, Integer> redisTemplate=new HashMap<String,Integer>();
    
        @Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)")
        public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException {
            try {
                //获取 HttpServletRequest 参数
                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) {
                    logger.error("方法中缺失HttpServletRequest参数");
                    throw new HttpServletException("方法中缺失HttpServletRequest参数");
                }
    
                String ip = request.getLocalAddr();
                String url = request.getRequestURL().toString();
                final String key = "req_limit_".concat(url).concat(ip);
    
                System.out.println("ip = "+ip+"
    "+" url = "+url+"
    "+" key = "+key);
    
                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 task  = new TimerTask(){    //创建一个新的计时器任务。
                        @Override
                        public void run() {
                            if(!key.equals("")) {
                                redisTemplate.remove(key);
                            }
                        }
                    };
                    timer.schedule(task, limit.time());
                    //安排在指定延迟后执行指定的任务。task : 所要安排的任务。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);
            }
        }
    }
    
    •  控制器
      注意方法中一定要有 HttpServletRequest 参数!!!不然会抛出 HttpServletException 异常。
       @ResponseBody
        @RequestMapping("/hello")
        @RequestLimit(count = 10)
        public String hello(HttpServletRequest request) {
            return "Hello World";
        }
    

    效果截图

      1. 正常访问

    1. 这里写图片描述

    2. 访问受限
      这里写图片描述

    3. 服务器后台
      这里写图片描述

  • 相关阅读:
    如何将u盘、移动硬盘转化为活动分区--绝招
    jstl错误排除:According to TLD or attribute directive in tag file, attribute value does not accept any expressions
    eclipse中package explore和project explore 怎么相互切换???
    硬盘知识区
    Sublime Text 3下Emmet使用技巧
    sublime text3中设置Emmet输入标签自动闭合
    window如何分区
    HTTP缓存
    react-router 4实现代码分割(code spliting)
    Vue练手项目(包含typescript版本)
  • 原文地址:https://www.cnblogs.com/MaxElephant/p/8108496.html
Copyright © 2011-2022 走看看