当前位置: 首页 > news >正文

自主实——动态 IP 黑名单过滤

一些恶意用户频繁请求服务器资源,导致资源占用过高。通过 IP 封禁,可以有效拉黑攻击者,防止资源被滥用,保障合法用户的正常访问。

Sentinel 本身就支持请求来源的 黑白名单判断 但其实引入 Sentinel 是需要一定成本的,本节主要分享更轻量的动态 IP 黑白名单过滤的常用设计和实现方法。

方案设计

想要自主实现动态 IP 黑名单,主要考虑以下几点:

1)IP 黑名单存储在哪里?

最简单的方式就是存储在内存中,但一般 IP 黑名单是动态增加的、需要持久化保存。常见的持久化方式包括数据库、配置文件或分布式存储系统(如 Redis)

2)如何便捷地动态修改 IP 黑名单?

为了方便动态修改 IP 黑名单,通常会提供一个管理页面,供管理员进行增删改查操作。

许多企业会将配置统一放入 配置中心,通过配置中心的管理页面,开发人员可以便捷地动态修改黑名单规则。Java 项目中,常用的配置中心是 Nacos。

3)黑白名单的判断逻辑应在哪里处理?

黑白名单逻辑通常部署在高性能的网关或 CDN 上,能够更早地拦截非法请求,减轻后端压力。在小型项目中,也可以直接在应用程序的过滤器中处理。

4)使用何种结构保存黑名单?如何快速匹配?

为了高效判断每个用户请求的 IP 是否在黑名单中,首先建议将 IP 黑名单从持久化存储同步到本地缓存中,避免频繁查询远程数据源。

对于黑名单数据较小的场景,可以使用简单的 Set 数据结构存储。而对于大规模黑名单,推荐使用 布隆过滤器或 DFA 来存储和过滤黑名单,可以节约内存空间、提高检测效率。

布隆过滤器

Bloom Filter 是一种高效的、基于概率的数据结构,用于判断一个元素是否存在于集合中。

原理是利用多个哈希函数将元素映射到固定的点位上(位数组中)

数组中每个元素只有 01 两种状态),初始时所有位都设为 0

向布隆过滤器插入元素(构建集合)——将位数组中这 k 个索引对应的位,全部从 0 置为 1

判断元素是否在集合中——检查位图中这 k 个索引对应的位:如果所有位都为 1:判定 “元素可能在集合中”(存在误判可能),与插入元素的原理同

如果布隆过滤器判断一个元素不存在集合中,那么这个元素一定不在集合中,如果判断元素存在集合中则不一定是真的,因为哈希可能会存在冲突。因此布隆过滤器 有误判的概率

而且它不好删除元素,只能新增,如果想要删除,只能重建。

特点:

  1. 空间效率高:相比于传统的数据结构(如哈希表),Bloom Filter 能用较少的空间存储大量的数据。

  2. 时间复杂度低:查询操作非常快速,通常是常数时间复杂度 O(1)

  3. 会有误判

因此,布隆过滤器适用于对准确性要求不高的、大规模数据量匹配的场景,比如垃圾邮件过滤、爬虫 URL 去重、缓存穿透防护等。

配置中心

配置中心的出现就是为了实现分布式系统中配置的 集中化管理,还提供动态更新、配置分组、安全管理,简化了配置管理,确保系统的灵活性和稳定性。

Nacos是常见的配置中心之一

Nacos 配置管理的核心概念

1、Namespace(命名空间)

命名空间用于隔离不同的配置集。它允许在同一个 Nacos 集群中将不同的环境(如开发、测试、生产)或者不同的业务线的配置进行隔离。

2、Group(组)

配置组是用于将多个相关的配置项进行分类管理的逻辑分组机制。

3、Data ID

Data ID 是一个唯一的配置标识符,通常与具体的应用程序相关。通过 Data ID,Nacos 知道如何获取特定应用的某个具体配置。 使用场景:每个应用的配置都会有一个独特的 Data ID。例如,一个支付系统可能有一个配置文件叫 com.payment.pay-service.yaml,这就是它的 Data ID。

4、Config Listener(配置监听器)

配置监听器用于让客户端实时监听 Nacos 配置中心中的配置变化,可以自动感知配置的更新并做出相应的处理。 使用场景:在需要动态调整配置的场景下使用,例如调整缓存大小、切换不同的服务端点等,应用可以通过监听器及时感知这些变化并应用新的配置。

推送和监听

推送方法:

  1. Nacos 控制台(推荐)

  2. 应用程序 SDK。Nacos 支持和 Spring Boot 快速整合,可以参考 官方文档

  3. Open API

监听方法:使用 SDK 配置 Config Listener,参考官方文档 。示例代码如下:

 String serverAddr = "{serverAddr}";String dataId = "{dataId}";String group = "{group}";Properties properties = new Properties();properties.put("serverAddr", serverAddr);ConfigService configService = NacosFactory.createConfigService(properties);String content = configService.getConfig(dataId, group, 5000);System.out.println(content);configService.addListener(dataId, group, new Listener() {@Overridepublic void receiveConfigInfo(String configInfo) {System.out.println("recieve1:" + configInfo);}@Overridepublic Executor getExecutor() {return null;}});​// 测试让主线程不退出,因为订阅配置是守护线程,主线程退出守护线程就会退出。 正式代码中无需下面代码while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}

或者直接通过注解读取 value,能够实时获取到最新的配置值:

 @Controller@RequestMapping("config")public class ConfigController {​@NacosValue(value = "${useLocalCache:false}", autoRefreshed = true)private boolean useLocalCache;​@RequestMapping(value = "/get", method = GET)@ResponseBodypublic boolean get() {return useLocalCache;}}

后端开发——基于 Nacos 实现 IP 黑名单需求。

1)使用 Nacos 配置中心存储和管理 IP 黑名单(作为配置中心)

2)利用 Web 过滤器判断每个用户请求的 IP

3)利用布隆过滤器过滤 IP 黑名单

下载 Nacos Server

在 Nacos 官网下载对应版本的 Nacos 应用包

可以在官方下载:https://nacos.io/download/release-history/

启动 Nacos Server

解压下载好的压缩包:                            

Windows 启动命令:qTpY1g3nmzN+T15CH/FQHrQLwecyTgoI1m9koXvX66o=

 startup.cmd -m standalone

启动成功,如图:

如果报找不到 Java,那就配置 JAVA_HOME、环境变量或者安装 Java

Nacos 使用的具体的教程,请参考官方的文档内容

  • 文档:https://nacos.io/zh-cn/docs/quick-start.html

  • 教程:https://sca.aliyun.com/zh-cn/docs/2021.0.5.0/user-guide/nacos/quick-start

通过 Nacos 控制台添加配置

1)访问:http://127.0.0.1:8848/nacos ,默认用户名和密码都是 nacos

项目引入 Nacos 依赖

可以直接使用 Spring Boot Starter 快速引入 Nacos,参考文档。

1)在项目 pom.xml 文件中,引入以下依赖配置:

 <dependency><groupId>com.alibaba.boot</groupId><artifactId>nacos-config-spring-boot-starter</artifactId><version>0.2.12</version></dependency>

2)修改 application.yml 配置文件,添加 Nacos Server 地址等配置:

 # 配置中心nacos:config:server-addr: 127.0.0.1:8848  # nacos 地址bootstrap:enable: true  # 预加载data-id: mianshiya # 控制台填写的 Data IDgroup: DEFAULT_GROUP # 控制台填写的 grouptype: yaml  # 选择的文件格式auto-refresh: true # 开启自动刷新

创建黑名单过滤工具类

新建 blackfilter 包,黑名单过滤相关的代码都放到该包下模块化。

使用 Hutool 工具库,自带的 BitMapBloomFilter 。

 @Slf4j  public class BlackIpUtils {  // 2. 定义黑名单工具类​// 3. 静态成员变量,用于存储布隆过滤器实例private static BitMapBloomFilter bloomFilter;​// 4. 静态方法:判断IP是否在黑名单中public static boolean isBlackIp(String ip) {  // 5. 调用布隆过滤器的contains方法检查IP是否存在return bloomFilter.contains(ip);}​// 6. 静态方法:根据配置信息重建IP黑名单public static void rebuildBlackIp(String configInfo) {// 7. 如果配置信息为空,默认设置为空JSON字符串if (StrUtil.isBlank(configInfo)) {configInfo = "{}";}// 8. 创建YAML解析器Yaml yaml = new Yaml();// 9. 将配置信息解析为Map对象(假设配置是YAML格式)Map map = yaml.loadAs(configInfo, Map.class);// 10. 从解析后的Map中获取key为"blackIpList"的列表(这是黑名单IP集合)List<String> blackIpList = (List<String>) map.get("blackIpList");// 11. 加类级别的锁,防止多线程并发修改时出现数据不一致synchronized (BlackIpUtils.class) {// 12. 如果黑名单IP列表不为空if (CollectionUtil.isNotEmpty(blackIpList)) {// 13. 创建布隆过滤器实例(参数958506可能是预估的IP数量,用于优化布隆过滤器性能)BitMapBloomFilter bitMapBloomFilter = new BitMapBloomFilter(958506);// 14. 遍历IP列表,将所有IP添加到布隆过滤器中for (String ip : blackIpList) {bitMapBloomFilter.add(ip);}// 15. 用新创建的布隆过滤器替换旧的,完成黑名单更新bloomFilter = bitMapBloomFilter;} else {// 16. 如果黑名单为空,创建一个默认大小的布隆过滤器bloomFilter = new BitMapBloomFilter(100);}}}}

💡 注意,因为 Nacos 配置文件的监听的粒度比较粗,只能知晓配置有变更,无法知晓是新增、删除还是修改,因此最方便的处理逻辑就是重建。

创建 Nacos 配置监听类

可以直接通过 Nacos 控制台获取示例代码:

通过注解实现完成 Nacos 配置监听

 @Slf4j  @Component  public class NacosListener implements InitializingBean {  // 3. 实现InitializingBean接口,重写afterPropertiesSet方法,在Bean初始化后执行特定逻辑​@NacosInjected  // 4. Nacos的依赖注入注解,自动注入ConfigService对象private ConfigService configService;  // 5. Nacos的配置服务对象,用于操作配置​@Value("${nacos.config.data-id}")  // 6. 从配置文件中读取nacos.config.data-id属性值private String dataId;  // 7. 存储Nacos配置的dataId(配置唯一标识)​@Value("${nacos.config.group}")  // 8. 从配置文件中读取nacos.config.group属性值private String group;  // 9. 存储Nacos配置的分组信息​@Override public void afterPropertiesSet() throws Exception {  // 11. Bean初始化后执行的方法log.info("nacos 监听器启动");  // 12. 输出日志,标识监听器已启动​// 13. 调用Nacos配置服务,获取配置并注册监听器// 参数:dataId-配置ID,group-分组,3000-超时时间,Listener-监听器对象String config = configService.getConfigAndSignListener(dataId, group, 3000L, new Listener() {// 14. 定义线程工厂,用于创建特定名称的线程final ThreadFactory threadFactory = new ThreadFactory() {private final AtomicInteger poolNumber = new AtomicInteger(1);  // 15. 原子计数器,用于生成线程池编号@Overridepublic Thread newThread(@NotNull Runnable r) {  // 16. 重写线程创建方法Thread thread = new Thread(r);  // 17. 创建线程thread.setName("refresh-ThreadPool" + poolNumber.getAndIncrement());  // 18. 设置线程名称,包含自增编号return thread;  // 19. 返回创建的线程}};// 20. 创建固定大小为1的线程池,使用上面定义的线程工厂final ExecutorService executorService = Executors.newFixedThreadPool(1, threadFactory);​// 21. 实现监听器的线程池获取方法@Overridepublic Executor getExecutor() {return executorService;  // 22. 返回线程池,用于异步处理配置变更}​// 23. 实现配置变更监听方法,当Nacos配置发生变化时调用@Overridepublic void receiveConfigInfo(String configInfo) {log.info("监听到配置信息变化:{}", configInfo);  // 24. 输出配置变化日志BlackIpUtils.rebuildBlackIp(configInfo);  // 25. 调用工具类重新构建黑名单}});// 26. 初始化时就用获取到的配置构建黑名单BlackIpUtils.rebuildBlackIp(config);}}

创建黑名单过滤器

黑名单应该对所有请求生效(不止是 Controller 的接口),所以基于 WebFilter 实现而不是 AOP 切面。WebFilter 的优先级高于 @Aspect 切面,因为它在整个 Web 请求生命周期中更早进行处理。

请求进入时的顺序:

  • WebFilter:首先,WebFilter 拦截 HTTP 请求,并可以根据逻辑决定是否继续执行请求。

  • Spring AOP 切面(@Aspect):如果请求经过过滤器并进入 Spring 管理的 Bean(例如 Controller 层),此时切面生效,对匹配的 Bean 方法进行拦截。

  • Controller 层:如果 @Aspect 没有阻止执行,最终请求到达 @Controller 或 @RestController 的方法。

代码如下:

 // 1. @WebFilter注解:标识这是一个Web过滤器// urlPatterns = "/*" 表示拦截所有请求// filterName = "blackIpFilter" 是过滤器的名称@WebFilter(urlPatterns = "/*", filterName = "blackIpFilter")// 2. 实现Filter接口,成为一个过滤器public class BlackIpFilter implements Filter {​// 3. 重写doFilter方法,核心的过滤逻辑在这里实现@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {​// 4. 将ServletRequest转换为HttpServletRequest,以便获取IP地址// 使用NetUtils工具类获取客户端的IP地址String ipAddress = NetUtils.getIpAddress((HttpServletRequest) servletRequest);// 5. 调用BlackIpUtils工具类检查该IP是否在黑名单中if (BlackIpUtils.isBlackIp(ipAddress)) {// 6. 如果是黑名单IP,设置响应类型为JSONservletResponse.setContentType("text/json;charset=UTF-8");// 7. 向客户端写入错误响应信息servletResponse.getWriter().write("{\"errorCode\":\"-1\",\"errorMsg\":\"黑名单IP,禁止访问\"}");// 8. 返回,不再继续执行后续的过滤器和请求处理return;}// 9. 如果不是黑名单IP,调用过滤器链的doFilter方法,让请求继续向下传递filterChain.doFilter(servletRequest, servletResponse);}​// 注:Filter接口还有init()和destroy()方法,这里没有重写,使用默认实现}

需要在启动类上加上 @ServletComponentScan,这样过滤器才会被扫描到。dp5VbIqxE14QEWBuvuJ61culrJ2SjhMvvJ1TUu02J68=

 // 1. @SpringBootApplication:Spring Boot的核心注解,组合了@Configuration、@EnableAutoConfiguration、@ComponentScan@SpringBootApplication​// 2. @MapperScan:指定MyBatis的Mapper接口所在的包路径,让Spring自动扫描并创建Mapper接口的实现@MapperScan("com.yupi.mianshiya.mapper")​// 3. @EnableScheduling:启用Spring的定时任务功能,允许使用@Scheduled注解定义定时任务@EnableScheduling​// 4. @EnableAspectJAutoProxy:启用AspectJ风格的AOP代理// proxyTargetClass = true:使用CGLIB代理(而非JDK动态代理),可以代理类和接口// exposeProxy = true:暴露代理对象,方便在目标对象中通过AopContext获取代理对象@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)​// 5. @ServletComponentScan:启用Servlet组件扫描,使@WebFilter、@WebServlet等注解生效@ServletComponentScan​// 6. 主应用类public class MainApplication {​// 7. 主方法,Spring Boot应用的入口点public static void main(String[] args) {// 8. 启动Spring Boot应用,加载配置并初始化Spring容器SpringApplication.run(MainApplication.class, args);}​}
http://www.dtcms.com/a/439677.html

相关文章:

  • 出版社网站建设石家庄模板建站代理
  • 网站如何做服务器授权苏州微网站建设
  • springboot+vue教务管理系统(源码+文档+调试+基础修改+答疑)
  • 网络安全基础--第七课:SQL注入
  • 做低价的跨境电商网站班级网站建设图片
  • USCAR-38解读
  • 监控视频分析侦查系统
  • 个人网站建设免费分析加强门户网站建设方案
  • 国外怎么做推广网站三网合一网站
  • 广西建设工会网站install wordpress
  • 网站后台功能模块设计广州电子软件开发
  • 网页游戏网站开发wordpress头像网站
  • Windows程序字符串处理与逆向分析
  • 网站收录下降注册传媒公司需要的条件
  • Embarcadero Dev-C++ 6.3 中文乱码问题
  • 归并排序巧解计算数组的小和问题
  • 三六五网做网站吗网页设计规范字体
  • 做网站需要了解的东西营销网站的建立
  • iBizModel 树视图(PSDETREEVIEW)模型体系详解
  • 科普重庆网站新余建站公司
  • 扬中网站推广导流自助网站
  • 生物信息中的FPKM counts TPM是什么意思 名词解释
  • 广州网站改版 网站建设宁波专业外贸网站建设
  • 模板网站制作时间坂田网站设计
  • 网站被黑 原因一级a做爰片免费网站神马电影
  • 万彩办公大师(Windows):便捷高效的办公工具箱
  • 做非洲外贸的网站免费网页空间
  • 织梦做淘宝客网站视频教程科技有限公司注册
  • 网站上怎么做企业推广班级优化大师免费下载安装
  • 北京市网站公司网站html5网站模板