使用自定义注解的方式,在需要被限制访问频率的方法上加注解即可控制。

看实现方式,基于springboot,aop,redis。

新建Springboot工程,引入redis,aop。

创建注解

package com.tianyalei.annotation;

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

import java.lang.annotation.*;

/**
 * Created by wuwf on 17/7/6.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
//最高优先级
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {
    /**
     * 允许访问的次数
     */
    int count() default 5;

    /**
     * 时间段,多少时间段内运行访问count次
     */
    long time() default 60000;

}

Aspect切面处理逻辑

package com.tianyalei.aspect;

import com.tianyalei.annotation.RequestLimit;
import com.tianyalei.util.HttpRequestUtil;
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.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/**
 * Created by wuwf on 17/7/6.
 */
@Component
@Aspect
public class RequestLimitAspect {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Before("execution(public * com.tianyalei.controller.*.*(..)) && @annotation(limit)")
    public void requestLimit(JoinPoint joinpoint, RequestLimit limit) {
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        String ip = HttpRequestUtil.getIpAddr(request);
        String url = request.getRequestURL().toString();
        String key = "req_limit_".concat(url).concat(ip);

        //加1后看看值
        long count = redisTemplate.opsForValue().increment(key, 1);
        //刚创建
        if (count == 1) {
            //设置1分钟过期
            redisTemplate.expire(key, limit.time(), TimeUnit.MILLISECONDS);
        }
        if (count > limit.count()) {
            logger.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
            throw new RuntimeException("超出访问次数限制");
        }
    }
}

获取IP的工具类

package com.tianyalei.util;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * Created by admin on 17/7/6.
 */
public class HttpRequestUtil {
    /**
     * 获取当前网络ip
     *
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress = inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
}

通过Controller验证

@RestController
public class IndexController {
    @RequestLimit(count = 4)
    @GetMapping("/index")
    public Object index() {
        return 1;
    }
}

启动工程,多次访问index看看效果即可。


使用redis计数来控制单位时间内对某接口的访问量,防止刷验证码接口之类的的更多相关文章

  1. python使用redis实现协同控制的分布式锁

    python使用redis实现协同控制的分布式锁 上午的时候,有个腾讯的朋友问我,关于用zookeeper分布式锁的设计,他的需求其实很简单,就是节点之间的协同合作. 我以前用redis写过一个网络锁 ...

  2. Redis计数信号量

    计数信号量是一种锁,它可以让用户限制一项资源最多能够同时被多少个进程访问,通常用于限定能够同时使用的资源数量.你可以把Redis分布式锁里面创建的锁看作是只能被一个进程访问的信号量. 计数信号量和其他 ...

  3. Redis并发锁控制

    为了防止用户在页面上重复点击或者同时发起多次请求,请求处理需要操作redis缓存,这个时候需要对并发边界进行并发锁控制,实现思路: 由于每个页面发起的请求带的token具备唯一性,可以将token作为 ...

  4. 基于redis实现锁控制

    多数据源 数据源1为锁控制,数据源2自定义,可用于存储. 锁:当出现并发的时候为了保证数据的一致性,不会出现并发问题,假设,用户1修改一条信息,用户2也同时修改,会按照顺序覆盖自修改的值,为了避免这种

  5. ipt_connlimit限制并发,ipt_recent限制单位时间内的请求数目

    xt_connlimit(别名ipt_connlimit) 一.Centos5.8系统 需要手动的执行modprobe ipt_connlimit命令把模板加入内核中去.先查看 #lsmod |gre ...

  6. 解决蓝牙鼠标在 Ubuntu 中单位时间内断开的问题

    1 查询你的鼠标的蓝牙地址 1.1 如:E1:DE:02:05:5E:F5 2 将查询到的设备地址写入配置文件 /etc/bluetooth/main.conf # Use vendor id sou ...

  7. httpparase + httpclient 的运用

    这篇文章介绍了 HtmlParser 开源包和 HttpClient 开源包的使用,在此基础上实现了一个简易的网络爬虫 (Crawler),来说明如何使用 HtmlParser 根据需要处理 Inte ...

  8. 基于AOP和Redis实现对接口调用情况的监控及IP限流

    目录 需求描述 概要设计 代码实现 参考资料 需求描述 项目中有许多接口,现在我们需要实现一个功能对接口调用情况进行统计,主要功能如下: 需求一:实现对每个接口,每天的调用次数做记录: 需求二:如果某 ...

  9. Nginx 对访问量的控制

    目的 了解 Nginx 的 ngx_http_limit_conn_module 和 ngx_http_limit_req_module 模块,对请求访问量进行控制. Nginx 模块化 nginx ...

随机推荐

  1. js检测数组类型

    1.instanceof 当只有一个全局执行环境时适用,如果包含多个框架,就存在两个以上不同版本的Array构造函数,如果从一个框架向另一个框架传递数组,传入的数组与在第二个框架中原生创建的数组分别具 ...

  2. mysql完整备份与恢复

    1.备份单个数据库 mysql数据库自带了一个很好用的备份命令,就是mysqldump,他的基本使用如下: 语法:mysqldump -u 用户名 -p 数据库名 > 备份的文件名 备份一 1. ...

  3. Rest_framework-3

    目录: 返回值的封装 分页 视图 路由 渲染器 一 返回值的封装 a. API的基本框架 setting: 1 首先注册rest_framework 2 版本配置 REST_FRAMEWORK = { ...

  4. java要注意的问题2

    八.替Java省点内存 某些Java程序是CPU密集型的,但它们会需要大量的内存.这类程序通常运行得很缓慢,因为它们对内存的需求很大.为了能提升这类应用的性能,可得给它们多留点内存.因此,假设我们有一 ...

  5. LeetCode:课程表【207】

    LeetCode:课程表[207] 题目描述 现在你总共有 n 门课需要选,记为 0 到 n-1. 在选修某些课程之前需要一些先修课程. 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹 ...

  6. HDOJ 1238 Substrings 【最长公共子串】

    HDOJ 1238 Substrings [最长公共子串] Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Ja ...

  7. ado.net(增删改)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  8. sql developer 如何格式化sql

    1.首先  Ctrl+A  全选需要格式的sql 2.然后  Ctrl+F7 即可格式化

  9. Makefile文件应用——simple项目

    学习资料 本文主要参考资料:驾驭Makefile(李云).pdf 原理 (1)最基本的语法 targets: prerequisites command targets是目标,prerequisite ...

  10. bootstrap重置校验方法

    $(function (){ $("select").change(function(){ $('#DepartForm').bootstrapValidator('resetFo ...