news 2026/5/15 18:44:09

Redis滑动窗口做登录限流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis滑动窗口做登录限流

这里实现一分钟内只允许5次登录,废话不多说直接上代码:

1.使用 Lua 脚本 将“清理过期数据 → 统计计数 → 判断是否超限 → 添加新记录”四个操作封装为一个原子操作,避免高并发下 count 与 add 之间的竞争条件。
-- KEYS[1] : 限流key,例如 "login:limit:phone:138****0000" -- ARGV[1] : 当前时间戳(毫秒) -- ARGV[2] : 窗口大小(毫秒) -- ARGV[3] : 限流阈值(最大请求次数) -- ARGV[4] : 本次请求的唯一标识(例如时间戳+随机数) local window_start = tonumber(ARGV[1]) - tonumber(ARGV[2]) -- 1. 删除窗口外的旧数据 redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, window_start) -- 2. 统计窗口内当前请求数 local current_count = redis.call('ZCARD', KEYS[1]) -- 3. 判断是否超过阈值 if current_count < tonumber(ARGV[3]) then -- 4. 添加本次请求记录 redis.call('ZADD', KEYS[1], ARGV[1], ARGV[4]) -- 5. 设置 key 过期时间(窗口大小 + 1秒缓冲) redis.call('EXPIRE', KEYS[1], math.ceil(tonumber(ARGV[2]) / 1000) + 1) return 1 -- 放行 else return 0 -- 限流 end
2.新建一个limit包,编写服务如下,也可以写在service实现类中:
package com.hmdp.limit; import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.scripting.support.ResourceScriptSource; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.Collections; import java.util.UUID; @Service public class LoginLimitService { @Resource private StringRedisTemplate stringRedisTemplate; //定义一个redis lua脚本,返回long类型数据 private DefaultRedisScript<Long> loginLimitScript; private static final long WINDOW_SIZE_MS = 60 * 1000L; // 1分钟 窗口 private static final int LIMIT_COUNT = 5; @PostConstruct public void init() { loginLimitScript = new DefaultRedisScript<>(); // 加载 resources/lua/login_limit.lua loginLimitScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("login_limit.lua"))); loginLimitScript.setResultType(Long.class); } public boolean tryAcquire(String phoneNumber) { String key = "login:limit:phone:" + phoneNumber; long now = System.currentTimeMillis(); //获取当前系统时间的时间戳 String member = now + "_" + UUID.randomUUID().toString(); //在zset中member如果一样 后写的会覆盖前写的 保证member的唯一 Long result = stringRedisTemplate.execute( loginLimitScript, Collections.singletonList(key), String.valueOf(now), String.valueOf(WINDOW_SIZE_MS), String.valueOf(LIMIT_COUNT), member ); return result != null && result == 1L; } }

注意这里StringRedisTemplate需要配置序列化器

3.登录接口:
@PostMapping("/login") public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session, HttpServletRequest request){ String phone =loginForm.getPhone(); // 实现登录功能 if ( phone== null){ phone = IpUtils.getIp( request); //拿到ip 作为手机号 } //添加滑动窗口进行登录限流 每一分钟 最多5次 if (!loginLimitService.tryAcquire(phone)) { return Result.fail("登录尝试过于频繁,请稍后再试"); } return userService.login(loginForm, session); }
4.如果请求手机号是空的,拿到请求ip做key,在config包下添加iputils如下:
package com.hmdp.utils; import javax.servlet.http.HttpServletRequest; //获取ip的工具类 public class IpUtils { public static String getIp(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } // 取第一个IP if (ip != null && ip.contains(",")) { ip = ip.split(",")[0].trim(); } return ip; } }

当然如果手机号码是强校验也可以省略ip来做key

5.postman做测试:在连续点击5次登录请求之后,可以看到直接返回了登录过于频繁

注意401 要带请求token

6.redis中窗口如图所示:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 18:43:25

Obsidian Encrypt:终极隐私保护指南,三步打造你的数字保险箱

Obsidian Encrypt&#xff1a;终极隐私保护指南&#xff0c;三步打造你的数字保险箱 【免费下载链接】obsidian-encrypt Hide secrets in your Obsidian.md vault 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-encrypt 你是否曾经担心过自己的私密笔记被他人窥…

作者头像 李华
网站建设 2026/5/15 18:41:04

Spek音频频谱分析器:3分钟掌握专业音频分析技术

Spek音频频谱分析器&#xff1a;3分钟掌握专业音频分析技术 【免费下载链接】spek Acoustic spectrum analyser 项目地址: https://gitcode.com/gh_mirrors/sp/spek 音频频谱分析是理解音频文件内在结构的关键技术&#xff0c;而Spek正是这一领域的专业工具。这款免费开…

作者头像 李华
网站建设 2026/5/15 18:39:05

手把手教你搞定Apple MFI证书申请与Token生成(附避坑指南)

手把手教你搞定Apple MFI证书申请与Token生成&#xff08;附避坑指南&#xff09; 对于初次接触Apple MFI&#xff08;Made for iPhone/iPad/iPod&#xff09;认证的开发者来说&#xff0c;整个申请流程可能会让人望而生畏。作为一位曾经在这个流程中踩过无数坑的"过来人…

作者头像 李华
网站建设 2026/5/15 18:34:14

免费实时屏幕翻译神器:Translumo完整使用指南

免费实时屏幕翻译神器&#xff1a;Translumo完整使用指南 【免费下载链接】Translumo Advanced real-time screen translator for games, hardcoded subtitles in videos, static text and etc. 项目地址: https://gitcode.com/gh_mirrors/tr/Translumo 还在为外语游戏剧…

作者头像 李华