黑马的登录模块加ip地址黑名单
考虑一种场景,我们的短信发送接口被同一个人使用不同的手机号多次恶意调用,那么同样会导致之前说过的问题。
要实现 IP 地址黑名单功能,核心是基于 IP 标识请求方,并通过 Redis 维护黑名单列表,结合拦截器在请求入口处校验。在redisconstants中
// ========== 新增【登录IP管控】核心配置 ==========
// IP 1分钟限流Key(格式:verify:ip:limit:xxx.xxx.xxx.xxx):1IP1分钟仅能发1次验证码
public static final String VERIFY_IP_LIMIT_KEY = "verify:ip:limit:";
// IP请求次数统计Key(格式:verify:ip:count:xxx.xxx.xxx.xxx):统计短时间内IP总请求数
public static final String VERIFY_IP_COUNT_KEY = "verify:ip:count:";
// IP黑名单Key(格式:verify:ip:black:xxx.xxx.xxx.xxx):IP被封禁的标识
public static final String VERIFY_IP_BLACK_KEY = "verify:ip:black:";
// 通用过期时间(复用原有,无需新增):限流60s、统计1h、黑名单24h
在usersericeimpl中
//ip黑名单检测
// 从session中获取HttpServletRequest对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// ====== 第一步:获取客户端真实IP(必须!适配本地/服务器/代理环境) ======
String clientIp = getRealIp(request);
// ====== 第二步:校验黑名单【优先级最高】 ======
// 1. 校验IP是否在黑名单 → IP拉黑直接拒绝
String ipBlackKey = RedisConstants.VERIFY_IP_BLACK_KEY + clientIp;
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(ipBlackKey))) {
return Result.fail("ip在黑名单中!!");
}
// ====== 第三步:校验1分钟限流【防高频请求】 ======
// 1. 校验IP:同一个IP,1分钟内仅能请求1次
String ipLimitKey = RedisConstants.VERIFY_IP_LIMIT_KEY + clientIp;
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(ipLimitKey))) {
return Result.fail("同一个IP,1分钟内仅能请求1次");
}
// 1. IP请求次数统计:短时间内超3次 → 拉黑IP
String ipCountKey = RedisConstants.VERIFY_IP_COUNT_KEY + clientIp;
Long ipCount = stringRedisTemplate.opsForValue().increment(ipCountKey);
if (ipCount == 1) { // 首次请求,设置1小时过期
stringRedisTemplate.expire(ipCountKey, RedisConstants.VERIFY_COUNT_TTL, TimeUnit.SECONDS);
}
// IP超3次 → 封禁IP(24小时)
if (ipCount > 2) {
stringRedisTemplate.opsForValue().set(ipBlackKey, "1", RedisConstants.VERIFY_BLACK_TTL, TimeUnit.SECONDS);
}
// ========== 工具方法1:获取客户端真实IP(关键!适配所有环境) ==========
private String getRealIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 处理多代理场景,取第一个有效IP
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
// ========== 工具方法2:超阈值时,清理无用的限流/统计Key ==========
private void cleanLimitKey(String... keys) {
for (String key : keys) {
stringRedisTemplate.delete(key);
}
}
要实现 IP 地址黑名单功能,核心是基于 IP 标识请求方,并通过 Redis 维护黑名单列表,结合拦截器在请求入口处校验。在redisconstants中
// ========== 新增【登录IP管控】核心配置 ==========
// IP 1分钟限流Key(格式:verify:ip:limit:xxx.xxx.xxx.xxx):1IP1分钟仅能发1次验证码
public static final String VERIFY_IP_LIMIT_KEY = "verify:ip:limit:";
// IP请求次数统计Key(格式:verify:ip:count:xxx.xxx.xxx.xxx):统计短时间内IP总请求数
public static final String VERIFY_IP_COUNT_KEY = "verify:ip:count:";
// IP黑名单Key(格式:verify:ip:black:xxx.xxx.xxx.xxx):IP被封禁的标识
public static final String VERIFY_IP_BLACK_KEY = "verify:ip:black:";
// 通用过期时间(复用原有,无需新增):限流60s、统计1h、黑名单24h
在usersericeimpl中
//ip黑名单检测
// 从session中获取HttpServletRequest对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// ====== 第一步:获取客户端真实IP(必须!适配本地/服务器/代理环境) ======
String clientIp = getRealIp(request);
// ====== 第二步:校验黑名单【优先级最高】 ======
// 1. 校验IP是否在黑名单 → IP拉黑直接拒绝
String ipBlackKey = RedisConstants.VERIFY_IP_BLACK_KEY + clientIp;
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(ipBlackKey))) {
return Result.fail("ip在黑名单中!!");
}
// ====== 第三步:校验1分钟限流【防高频请求】 ======
// 1. 校验IP:同一个IP,1分钟内仅能请求1次
String ipLimitKey = RedisConstants.VERIFY_IP_LIMIT_KEY + clientIp;
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(ipLimitKey))) {
return Result.fail("同一个IP,1分钟内仅能请求1次");
}
// 1. IP请求次数统计:短时间内超3次 → 拉黑IP
String ipCountKey = RedisConstants.VERIFY_IP_COUNT_KEY + clientIp;
Long ipCount = stringRedisTemplate.opsForValue().increment(ipCountKey);
if (ipCount == 1) { // 首次请求,设置1小时过期
stringRedisTemplate.expire(ipCountKey, RedisConstants.VERIFY_COUNT_TTL, TimeUnit.SECONDS);
}
// IP超3次 → 封禁IP(24小时)
if (ipCount > 2) {
stringRedisTemplate.opsForValue().set(ipBlackKey, "1", RedisConstants.VERIFY_BLACK_TTL, TimeUnit.SECONDS);
}
// ========== 工具方法1:获取客户端真实IP(关键!适配所有环境) ==========
private String getRealIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 处理多代理场景,取第一个有效IP
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
// ========== 工具方法2:超阈值时,清理无用的限流/统计Key ==========
private void cleanLimitKey(String... keys) {
for (String key : keys) {
stringRedisTemplate.delete(key);
}
}
全部评论
相关推荐
Rain_Codin...:简历感觉有点乱了 点赞 评论 收藏
分享
2025-12-09 23:27
商丘学院 嵌入式软件工程师
程序员花海:实习和校招简历正确格式应该是教育背景+实习+项目经历+个人评价 其中项目经历注意要体现业务 实习经历里面的业务更是要自圆其说 简历模板尽可能保持干净整洁 不要太花哨的 点赞 评论 收藏
分享