找回密码
 立即注册
首页 业界区 业界 安全漏洞修复导致SpringBoot2.7与Springfox不兼容,问题 ...

安全漏洞修复导致SpringBoot2.7与Springfox不兼容,问题排查与处理

寇秀娟 2025-6-3 00:35:50
开心一刻

2021,朋友某一次核酸检测,跟我聊天
朋友:今天我们小区做核算,队长死了
我:卧槽,过劳死吗
朋友:?????
我:啊?
朋友:队 长死了,队伍很长!
我:哈哈哈,我以为做核算的队长死了呢
1.jpeg
背景介绍

项目基于 springboot2.5.2 实现的,用 springfox-swagger2 生成与前端对接的 API 文档;pom.xml 中依赖如下
  1. <parent>
  2.     <groupId>org.springframework.boot</groupId>
  3.     spring-boot-starter-parent</artifactId>
  4.     <version>2.5.2</version>
  5. </parent>
  6. <properties>
  7.     <maven.compiler.source>8</maven.compiler.source>
  8.     <maven.compiler.target>8</maven.compiler.target>
  9.     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  10. </properties>
  11. <dependencies>
  12.     <dependency>
  13.         <groupId>org.springframework.boot</groupId>
  14.         spring-boot-starter-web</artifactId>
  15.     </dependency>
  16.     <dependency>
  17.         <groupId>io.springfox</groupId>
  18.         springfox-boot-starter</artifactId>
  19.         <version>3.0.0</version>
  20.     </dependency>
  21. </dependencies>
复制代码
启动服务后,就可以访问 Swagger UI
http://localhost:8080/swagger-ui/index.html
2.png
前端同事就可以访问这个来对接接口了,后端省去了写接口文档的工作,一切都是那么美好!可突然有一天,安全部门发来报告,说服务存在很多安全漏洞
CVE-2023-20860、CVE-2022-45143、CVE-2023-46589、...
让我们根据报告中的建议进行修复,然后就开始了我的踩坑之旅!
springboot 与 springfox 兼容问题

粗略看了一眼,将 spring-boot 升级,可以解决很多漏洞,既然要升,那就升到可升的最高版本;因为是基于 JDK8,所以 spring-boot 最高能升级到 2.7.8。那就升嘛,不要怂就是干!
  1. <parent>
  2.     <groupId>org.springframework.boot</groupId>
  3.     spring-boot-starter-parent</artifactId>
  4.     <version>2.7.18</version>
  5. </parent>
复制代码
启动服务,第一个坑来了:NullPointerException
  1. 2025-05-30 21:13:42.264|ERROR|main|818|o.s.boot.SpringApplication              :Application run failed
  2. org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
  3.         at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182)
  4.         at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
  5.         at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:357)
  6.         at java.lang.Iterable.forEach(Iterable.java:75)
  7.         at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:156)
  8.         at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:124)
  9.         at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:946)
  10.         at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:594)
  11.         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
  12.         at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732)
  13.         at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409)
  14.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
  15.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300)
  16.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289)
  17.         at com.qsl.Application.main(Application.java:16)
  18. Caused by: java.lang.NullPointerException: null
  19.         at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56)
  20.         at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113)
  21.         at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89)
  22.         at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)
  23.         at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
  24.         at java.util.TimSort.sort(TimSort.java:220)
  25.         at java.util.Arrays.sort(Arrays.java:1512)
  26.         at java.util.ArrayList.sort(ArrayList.java:1454)
  27.         at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:387)
  28.         at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
  29.         at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
  30.         at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
  31.         at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
  32.         at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
  33.         at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
  34.         at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
  35.         at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
  36.         at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
  37.         at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81)
  38.         at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
  39.         at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
  40.         at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
  41.         at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
  42.         at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
  43.         at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
  44.         at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
  45.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107)
  46.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91)
  47.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82)
  48.         at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100)
  49.         at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:179)
  50.         ... 14 common frames omitted
复制代码
遇到问题不要怕
3.gif
查询问题并解决问题嘛;从哪查,我想大家已经达成了统一的共识:大数据模型。deepseek 就是热门之一,我们直接将堆栈信息扔给他,让他提供解决方案。他一针见血分析出了原因
这个错误通常是由于 Springfox Swagger(springfox-swagger2)与 Spring Boot 版本不兼容Spring MVC 路径匹配策略冲突 导致的。以下是几种解决方案

  • 降级 Spring Boot 版本
    Spring Boot 2.6+ 开始与 springfox 不兼容,但 Springfox Swagger 2.x 已经停止维护了,所以说通过升级 Springfox Swagger 来适配 Spring Boot 2.6+ 是不行了。
    我们的目的是升级 Spring Boot,那么降级 Spring Boot 这个方案肯定是行不通的。

  • 升级到 SpringDoc OpenAPI
    SpringDoc 是 Swagger 的替代方案,支持 OpenAPI 3.0,兼容 Spring Boot 2.6+ 和 3.x
    考虑到注解变动大,需要调整的地方太多,这个方案不到万不得已不采用

  • 修改路径匹配策略
    如果不想降级 Spring Boot,可以调整路径匹配策略
    1. spring:
    2.   mvc:
    3.     pathmatch:
    4.       matching-strategy: ant_path_matcher
    复制代码
    这个调整简单,感觉可行,试试发现雀氏可以,采用这种方案

  • Swagger 配置是否正确
    确保 @EnableSwagger2 和 Docket 配置正确
    1. @Configuration
    2. @EnableSwagger2
    3. public class SwaggerConfig {
    4.     @Bean
    5.     public Docket api() {
    6.         return new Docket(DocumentationType.SWAGGER_2)
    7.             .select()
    8.             .apis(RequestHandlerSelectors.basePackage("com.your.package")) // 替换成你的 Controller 包名
    9.             .paths(PathSelectors.any())
    10.             .build();
    11.     }
    12. }
    复制代码
    这个确定是配置正确的,不是这个问题
deepseek 还给了其他方案,大家可以结合自己的实际情况,看看方案是否适用。如果你们以为坑就这么填平了,那只能说你们还是太年轻啦
4.gif
我再加个依赖
  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     spring-boot-starter-actuator</artifactId>
  4. </dependency>
复制代码
启动服务,同样的问题又来了
  1. org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
  2.         at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182)
  3.         at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
  4.         at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:357)
  5.         at java.lang.Iterable.forEach(Iterable.java:75)
  6.         at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:156)
  7.         at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:124)
  8.         at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:946)
  9.         at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:594)
  10.         at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
  11.         at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732)
  12.         at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409)
  13.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
  14.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300)
  15.         at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289)
  16.         at com.qsl.Application.main(Application.java:16)
  17. Caused by: java.lang.NullPointerException: null
  18.         at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56)
  19.         at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113)
  20.         at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89)
  21.         at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469)
  22.         at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
  23.         at java.util.TimSort.sort(TimSort.java:220)
  24.         at java.util.Arrays.sort(Arrays.java:1512)
  25.         at java.util.ArrayList.sort(ArrayList.java:1454)
  26.         at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:387)
  27.         at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
  28.         at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
  29.         at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
  30.         at java.util.stream.Sink$ChainedReference.end(Sink.java:258)
  31.         at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
  32.         at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
  33.         at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
  34.         at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
  35.         at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
  36.         at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81)
  37.         at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
  38.         at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
  39.         at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
  40.         at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
  41.         at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
  42.         at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
  43.         at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
  44.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107)
  45.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91)
  46.         at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82)
  47.         at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100)
  48.         at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:179)
  49.         ... 14 common frames omitted
复制代码
同个问题出现一次也就算了,换个方式出现第二次,有点欺负人了!
5.gif
此时我们再去问 deepseek,给出的解决方案,尝试了都不对,好在在网上找到了解决方案,增加如下配置
[code]@Configuration@EnableSwagger2public class SwaggerConfig {    /**     * springboot 2.7.x不支持swagger2,注册bean进行兼容     * 该方法在Spring Boot 2.7.x中手动注册WebMvcEndpointHandlerMapping,用于解决Swagger无法直接访问Actuator端点的问题。下面详细说明其作用:     * 1. 收集所有可暴露的端点     * 使用以下组件获取不同类型的端点:     * webEndpointsSupplier.getEndpoints():获取所有基于Web的端点(如/actuator/health)。     * servletEndpointsSupplier.getEndpoints():获取Servlet类型的端点(如/actuator/servlet)。     * controllerEndpointsSupplier.getEndpoints():获取Controller类型的端点。     * 将这些端点统一添加到allEndpoints列表中,以便后续处理。     * 2. 设置端点的基础路径     * 从WebEndpointProperties中获取配置的端点基础路径(basePath),默认值通常是/actuator。     * 创建一个EndpointMapping对象,并传入基础路径,用于定义端点的URL映射规则。     * 3. 判断是否需要注册端点链接映射     * 判断条件如下:     * 如果启用了端点发现(webEndpointProperties.getDiscovery().isEnabled())。     * 并且设置了有效的基础路径(StringUtils.hasText(basePath)),或者管理端口与应用端口不同(ManagementPortType.DIFFERENT)。     * 如果满足条件,则创建端点链接映射,用于生成包含所有可用端点的首页链接(例如/actuator)。     * 4. 构建并返回WebMvcEndpointHandlerMapping     * 创建WebMvcEndpointHandlerMapping实例时传入以下参数:     * endpointMapping:定义端点的基础路径。     * webEndpoints:需要注册的Web类型端点集合。     * endpointMediaTypes:定义端点支持的媒体类型(如JSON、YAML等)。     * corsConfiguration:跨域资源共享(CORS)配置。     * new EndpointLinksResolver(allEndpoints, basePath):用于生成端点链接的解析器。     * shouldRegisterLinksMapping:是否注册端点链接映射。     * null:通常用于指定自定义的请求谓词,此处为默认值。     * 返回的WebMvcEndpointHandlerMapping使Swagger能够正常访问和展示Actuator端点的信息。     * 5. 兼容性适配     * Spring Boot 2.7.x之后,Swagger 2不再直接支持访问Actuator端点,此方法通过手动注册WebMvcEndpointHandlerMapping来恢复兼容性,确保Swagger UI可以正确显示和调用这些监控和管理接口。     * 总结     * 此方法的核心目的是手动注册端点处理器映射,以确保Swagger能够正确访问Spring Boot Actuator提供的各种监控和管理端点。通过整合多种端点类型、设置基础路径、启用链接映射等方式,使得开发者能够在Swagger UI中方便地测试和使用这些端点。     */    @Bean    public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,                                                                         ServletEndpointsSupplier servletEndpointsSupplier,                                                                         ControllerEndpointsSupplier controllerEndpointsSupplier,                                                                         EndpointMediaTypes endpointMediaTypes,                                                                         CorsEndpointProperties corsProperties,                                                                         WebEndpointProperties webEndpointProperties,                                                                         Environment environment) {        List
您需要登录后才可以回帖 登录 | 立即注册