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 redisTemplate=new HashMap();

    @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+"\n"+" url = "+url+"\n"+" 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. 正常访问
    spring boot 学习(九)小工具篇:?秒防刷新_第1张图片

  2. 访问受限
    spring boot 学习(九)小工具篇:?秒防刷新_第2张图片

  3. 服务器后台
    spring boot 学习(九)小工具篇:?秒防刷新_第3张图片

项目参考地址

参考项目地址: https://github.com/FunriLy/springboot-study/tree/master/%E6%A1%88%E4%BE%8B5

你可能感兴趣的:(spring,boot)