基于Nacos实现IP动态黑白名单过滤
发表于更新于
字数总计:1.6k阅读时长:7分钟 上海
后端后端基于Nacos实现IP动态黑白名单过滤
xukun一、什么是IP黑白名单?
一些恶意用户(可能是黑客、爬虫、DDoS 攻击者)可能频繁请求服务器资源,导致资源占用过高。因此我们需要一定的手段实时阻止可疑或恶意的用户,减少攻击风险。
通过 IP 封禁,可以有效拉黑攻击者,防止资源被滥用,保障合法用户的正常访问。
对于我们的需求,不让拉进黑名单的 IP 访问任何接口。
二、使用步骤
1.通过Nacos添加配置
访问Nacos后台,创建自己的配置


之后选择发布
2.pom.xml引入依赖
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>nacos-config-spring-boot-starter</artifactId> <version>0.2.12</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.8</version> </dependency>
|
1 2 3 4 5
| <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.8</version> </dependency>
|
在application.yml中添加依赖
1 2 3 4 5 6 7 8 9 10
| # 配置中心 nacos: config: server-addr: 127.0.0.1:8848 # nacos 地址 bootstrap: enable: true # 预加载 data-id: 这里填写上面你在配置文件里面填写的Data ID # 控制台填写的 Data ID group: DEFAULT_GROUP # 控制台填写的 group type: yaml # 选择的文件格式 auto-refresh: true # 开启自动刷新
|
3.使用方法
1.定义一个获取IP的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.ikun.aimian.utils; import java.net.InetAddress; import javax.servlet.http.HttpServletRequest;
public class NetUtils {
public static String getIpAddress(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(); if (ip.equals("127.0.0.1")) { InetAddress inet = null; try { inet = InetAddress.getLocalHost(); } catch (Exception e) { e.printStackTrace(); } if (inet != null) { ip = inet.getHostAddress(); } } } if (ip != null && ip.length() > 15) { if (ip.indexOf(",") > 0) { ip = ip.substring(0, ip.indexOf(",")); } } if (ip == null) { return "127.0.0.1"; } return ip; } }
|
2.创建黑名单过滤工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| package com.ikun.aimian.blackfilter; import cn.hutool.bloomfilter.BitMapBloomFilter; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.yaml.snakeyaml.Yaml; import java.util.Collections; import java.util.List; import java.util.Map; @Slf4j public class BlackIpUtils { private static volatile BitMapBloomFilter bloomFilter;
public static boolean isBlackIp(String ip) { if (bloomFilter == null) { log.warn("Bloom filter is not initialized. Returning false for IP: {}", ip); return false; } return bloomFilter.contains(ip); }
public static void rebuildBlackIp(String configInfo) { if (StrUtil.isBlank(configInfo)) { log.warn("你没有传递配置文件的内容"); configInfo = "{}"; } try { Yaml yaml = new Yaml(); Map<String, Object> map = yaml.loadAs(configInfo, Map.class); List<String> blackIpList = (List<String>) map.getOrDefault("blackIpList", Collections.emptyList()); synchronized (BlackIpUtils.class) { BitMapBloomFilter bitMapBloomFilter = new BitMapBloomFilter( Math.max(blackIpList.size(), 100)); for (String ip : blackIpList) { bitMapBloomFilter.add(ip); } bloomFilter = bitMapBloomFilter; log.info("Bloom filter rebuilt successfully with {} IPs.", blackIpList.size()); } } catch (Exception e) { log.error("Failed to rebuild Bloom filter. Config info: {}", configInfo, e); synchronized (BlackIpUtils.class) { bloomFilter = new BitMapBloomFilter(100); } } } }
|
3.创建Nacos配置监听类
在 blackfilter 包中新增监听器代码,追求性能的话可以自定义线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| package com.ikun.aimian.blackfilter; import com.alibaba.nacos.api.annotation.NacosInjected; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.validation.constraints.NotNull; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Component public class NacosListener implements InitializingBean { @NacosInjected private ConfigService configService; @Value("${nacos.config.data-id}") private String dataId; @Value("${nacos.config.group}") private String group; @Override public void afterPropertiesSet() throws Exception { log.info("nacos 监听器启动"); String config = configService.getConfigAndSignListener(dataId, group, 3000L, new Listener() { final ThreadFactory threadFactory = new ThreadFactory() { private final AtomicInteger poolNumber = new AtomicInteger(1); @Override public Thread newThread(@NotNull Runnable r) { Thread thread = new Thread(r); thread.setName("refresh-ThreadPool" + poolNumber.getAndIncrement()); return thread; } }; final ExecutorService executorService = Executors.newFixedThreadPool(1, threadFactory); @Override public Executor getExecutor() { return executorService; } @Override public void receiveConfigInfo(String configInfo) { log.info("监听到配置信息变化:{}", configInfo); BlackIpUtils.rebuildBlackIp(configInfo); } }); BlackIpUtils.rebuildBlackIp(config); } }
|
4.创建黑白名单过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @WebFilter(urlPatterns = "/*", filterName = "blackIpFilter") public class BlackIpFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { String ipAddress = NetUtils.getIpAddress((HttpServletRequest) servletRequest); if (BlackIpUtils.isBlackIp(ipAddress)) { servletResponse.setContentType("text/json;charset=UTF-8"); servletResponse.getWriter().write("{\"errorCode\":\"-1\",\"errorMsg\":\"黑名单IP,禁止访问\"}"); return; } filterChain.doFilter(servletRequest, servletResponse); } }
|
此时添加上这个过滤器之后需要在主类上面添加注解
访问接口
