使用redis计数来控制单位时间内对某接口的访问量,防止刷验证码接口之类的
使用自定义注解的方式,在需要被限制访问频率的方法上加注解即可控制。
看实现方式,基于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计数来控制单位时间内对某接口的访问量,防止刷验证码接口之类的的更多相关文章
- python使用redis实现协同控制的分布式锁
python使用redis实现协同控制的分布式锁 上午的时候,有个腾讯的朋友问我,关于用zookeeper分布式锁的设计,他的需求其实很简单,就是节点之间的协同合作. 我以前用redis写过一个网络锁 ...
- Redis计数信号量
计数信号量是一种锁,它可以让用户限制一项资源最多能够同时被多少个进程访问,通常用于限定能够同时使用的资源数量.你可以把Redis分布式锁里面创建的锁看作是只能被一个进程访问的信号量. 计数信号量和其他 ...
- Redis并发锁控制
为了防止用户在页面上重复点击或者同时发起多次请求,请求处理需要操作redis缓存,这个时候需要对并发边界进行并发锁控制,实现思路: 由于每个页面发起的请求带的token具备唯一性,可以将token作为 ...
- 基于redis实现锁控制
多数据源 数据源1为锁控制,数据源2自定义,可用于存储. 锁:当出现并发的时候为了保证数据的一致性,不会出现并发问题,假设,用户1修改一条信息,用户2也同时修改,会按照顺序覆盖自修改的值,为了避免这种
- ipt_connlimit限制并发,ipt_recent限制单位时间内的请求数目
xt_connlimit(别名ipt_connlimit) 一.Centos5.8系统 需要手动的执行modprobe ipt_connlimit命令把模板加入内核中去.先查看 #lsmod |gre ...
- 解决蓝牙鼠标在 Ubuntu 中单位时间内断开的问题
1 查询你的鼠标的蓝牙地址 1.1 如:E1:DE:02:05:5E:F5 2 将查询到的设备地址写入配置文件 /etc/bluetooth/main.conf # Use vendor id sou ...
- httpparase + httpclient 的运用
这篇文章介绍了 HtmlParser 开源包和 HttpClient 开源包的使用,在此基础上实现了一个简易的网络爬虫 (Crawler),来说明如何使用 HtmlParser 根据需要处理 Inte ...
- 基于AOP和Redis实现对接口调用情况的监控及IP限流
目录 需求描述 概要设计 代码实现 参考资料 需求描述 项目中有许多接口,现在我们需要实现一个功能对接口调用情况进行统计,主要功能如下: 需求一:实现对每个接口,每天的调用次数做记录: 需求二:如果某 ...
- Nginx 对访问量的控制
目的 了解 Nginx 的 ngx_http_limit_conn_module 和 ngx_http_limit_req_module 模块,对请求访问量进行控制. Nginx 模块化 nginx ...
随机推荐
- js检测数组类型
1.instanceof 当只有一个全局执行环境时适用,如果包含多个框架,就存在两个以上不同版本的Array构造函数,如果从一个框架向另一个框架传递数组,传入的数组与在第二个框架中原生创建的数组分别具 ...
- mysql完整备份与恢复
1.备份单个数据库 mysql数据库自带了一个很好用的备份命令,就是mysqldump,他的基本使用如下: 语法:mysqldump -u 用户名 -p 数据库名 > 备份的文件名 备份一 1. ...
- Rest_framework-3
目录: 返回值的封装 分页 视图 路由 渲染器 一 返回值的封装 a. API的基本框架 setting: 1 首先注册rest_framework 2 版本配置 REST_FRAMEWORK = { ...
- java要注意的问题2
八.替Java省点内存 某些Java程序是CPU密集型的,但它们会需要大量的内存.这类程序通常运行得很缓慢,因为它们对内存的需求很大.为了能提升这类应用的性能,可得给它们多留点内存.因此,假设我们有一 ...
- LeetCode:课程表【207】
LeetCode:课程表[207] 题目描述 现在你总共有 n 门课需要选,记为 0 到 n-1. 在选修某些课程之前需要一些先修课程. 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹 ...
- HDOJ 1238 Substrings 【最长公共子串】
HDOJ 1238 Substrings [最长公共子串] Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Ja ...
- ado.net(增删改)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- sql developer 如何格式化sql
1.首先 Ctrl+A 全选需要格式的sql 2.然后 Ctrl+F7 即可格式化
- Makefile文件应用——simple项目
学习资料 本文主要参考资料:驾驭Makefile(李云).pdf 原理 (1)最基本的语法 targets: prerequisites command targets是目标,prerequisite ...
- bootstrap重置校验方法
$(function (){ $("select").change(function(){ $('#DepartForm').bootstrapValidator('resetFo ...