安全漏洞修復(fù)導(dǎo)致SpringBoot2.7與Springfox不兼容的偽命題解決
項目基于 springboot2.5.2
實現(xiàn)的,用 springfox-swagger2
生成與前端對接的 API 文檔;pom.xml
中依賴如下
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> </parent> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency> </dependencies>
啟動服務(wù)后,就可以訪問 Swagger UI
http://localhost:8080/swagger-ui/index.html
前端同事就可以訪問這個來對接接口了,后端省去了寫接口文檔的工作,一切都是那么美好!可突然有一天,安全部門發(fā)來報告,說服務(wù)存在很多安全漏洞
CVE-2023-20860、CVE-2022-45143、CVE-2023-46589、...
讓我們根據(jù)報告中的建議進行修復(fù),然后就開始了我的踩坑之旅!
springboot 與 springfox 兼容問題
粗略看了一眼,將 spring-boot
升級,可以解決很多漏洞,既然要升,那就升到可升的最高版本;因為是基于 JDK8
,所以 spring-boot
最高能升級到 2.7.8
。那就升嘛,不要慫就是干!
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> </parent>
啟動服務(wù),第一個坑來了:NullPointerException
2025-05-30 21:13:42.264|ERROR|main|818|o.s.boot.SpringApplication :Application run failed org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182) at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:357) at java.lang.Iterable.forEach(Iterable.java:75) at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:156) at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:124) at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:946) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:594) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289) at com.qsl.Application.main(Application.java:16) Caused by: java.lang.NullPointerException: null at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56) at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113) at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89) at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469) at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) at java.util.TimSort.sort(TimSort.java:220) at java.util.Arrays.sort(Arrays.java:1512) at java.util.ArrayList.sort(ArrayList.java:1454) at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:387) at java.util.stream.Sink$ChainedReference.end(Sink.java:258) at java.util.stream.Sink$ChainedReference.end(Sink.java:258) at java.util.stream.Sink$ChainedReference.end(Sink.java:258) at java.util.stream.Sink$ChainedReference.end(Sink.java:258) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107) at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91) at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82) at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100) at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:179) ... 14 common frames omitted
遇到問題不要怕
查詢問題并解決問題嘛;從哪查,我想大家已經(jīng)達成了統(tǒng)一的共識:大數(shù)據(jù)模型
。deepseek
就是熱門之一,我們直接將堆棧信息扔給他,讓他提供解決方案。他一針見血分析出了原因
這個錯誤通常是由于 Springfox Swagger(
springfox-swagger2
)與 Spring Boot 版本不兼容 或 Spring MVC 路徑匹配策略沖突 導(dǎo)致的。以下是幾種解決方案
降級 Spring Boot 版本
Spring Boot 2.6+
開始與springfox
不兼容,但Springfox Swagger 2.x
已經(jīng)停止維護了,所以說通過升級 Springfox Swagger 來適配 Spring Boot 2.6+ 是不行了。我們的目的是升級 Spring Boot,那么降級 Spring Boot 這個方案肯定是行不通的。
升級到 SpringDoc OpenAPI
SpringDoc 是 Swagger 的替代方案,支持 OpenAPI 3.0,兼容 Spring Boot 2.6+ 和 3.x
考慮到注解變動大,需要調(diào)整的地方太多,這個方案不到萬不得已不采用
修改路徑匹配策略
如果不想降級 Spring Boot,可以調(diào)整路徑匹配策略
spring: mvc: pathmatch: matching-strategy: ant_path_matcher
這個調(diào)整簡單,感覺可行,試試發(fā)現(xiàn)雀氏可以,采用這種方案
Swagger 配置是否正確
確保
@EnableSwagger2
和Docket
配置正確@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.your.package")) // 替換成你的 Controller 包名 .paths(PathSelectors.any()) .build(); } }
這個確定是配置正確的,不是這個問題
deepseek
還給了其他方案,大家可以結(jié)合自己的實際情況,看看方案是否適用。如果你們以為坑就這么填平了,那只能說你們還是太年輕啦
我再加個依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
啟動服務(wù),同樣的問題又來了
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:182) at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:357) at java.lang.Iterable.forEach(Iterable.java:75) at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:156) at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:124) at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:946) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:594) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289) at com.qsl.Application.main(Application.java:16) Caused by: java.lang.NullPointerException: null at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56) at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113) at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89) at java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469) at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) at java.util.TimSort.sort(TimSort.java:220) at java.util.Arrays.sort(Arrays.java:1512) at java.util.ArrayList.sort(ArrayList.java:1454) at java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:387) at java.util.stream.Sink$ChainedReference.end(Sink.java:258) at java.util.stream.Sink$ChainedReference.end(Sink.java:258) at java.util.stream.Sink$ChainedReference.end(Sink.java:258) at java.util.stream.Sink$ChainedReference.end(Sink.java:258) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107) at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91) at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82) at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100) at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:179) ... 14 common frames omitted
同個問題出現(xiàn)一次也就算了,換個方式出現(xiàn)第二次,有點欺負人了!
此時我們再去問 deepseek
,給出的解決方案,嘗試了都不對,好在在網(wǎng)上找到了解決方案,增加如下配置
@Configuration @EnableSwagger2 public 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類型的端點。 * 將這些端點統(tǒng)一添加到allEndpoints列表中,以便后續(xù)處理。 * 2. 設(shè)置端點的基礎(chǔ)路徑 * 從WebEndpointProperties中獲取配置的端點基礎(chǔ)路徑(basePath),默認值通常是/actuator。 * 創(chuàng)建一個EndpointMapping對象,并傳入基礎(chǔ)路徑,用于定義端點的URL映射規(guī)則。 * 3. 判斷是否需要注冊端點鏈接映射 * 判斷條件如下: * 如果啟用了端點發(fā)現(xiàn)(webEndpointProperties.getDiscovery().isEnabled())。 * 并且設(shè)置了有效的基礎(chǔ)路徑(StringUtils.hasText(basePath)),或者管理端口與應(yīng)用端口不同(ManagementPortType.DIFFERENT)。 * 如果滿足條件,則創(chuàng)建端點鏈接映射,用于生成包含所有可用端點的首頁鏈接(例如/actuator)。 * 4. 構(gòu)建并返回WebMvcEndpointHandlerMapping * 創(chuàng)建WebMvcEndpointHandlerMapping實例時傳入以下參數(shù): * endpointMapping:定義端點的基礎(chǔ)路徑。 * 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來恢復(fù)兼容性,確保Swagger UI可以正確顯示和調(diào)用這些監(jiān)控和管理接口。 * 總結(jié) * 此方法的核心目的是手動注冊端點處理器映射,以確保Swagger能夠正確訪問Spring Boot Actuator提供的各種監(jiān)控和管理端點。通過整合多種端點類型、設(shè)置基礎(chǔ)路徑、啟用鏈接映射等方式,使得開發(fā)者能夠在Swagger UI中方便地測試和使用這些端點。 */ @Bean public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) { List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>(); Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints(); allEndpoints.addAll(webEndpoints); allEndpoints.addAll(servletEndpointsSupplier.getEndpoints()); allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints()); String basePath = webEndpointProperties.getBasePath(); EndpointMapping endpointMapping = new EndpointMapping(basePath); boolean shouldRegisterLinksMapping = webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT)); return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null); } }
啟動服務(wù),不再報錯,Swagger UI
也能正常訪問
也許你們會覺得這個坑解決的也快呀,沒什么大不了的,可實際是項目中有眾多的依賴,你如何知道是因為 spring-boot-starter-actuator
導(dǎo)致的?我實際排查這個問題的過程,可不是如上所說的那般容易,但有一點我們要清楚
我們遇到的坑,肯定有前輩遇到過
所以我們要做的是想清楚關(guān)鍵詞,到大數(shù)據(jù)模型搜索,或者到搜索引擎搜索
總結(jié)
歸根結(jié)底,還是
Springfox
沒有去適配Spring Boot
的升級,所以可能的話,還是推薦大家用SpringDoc OpenAPI
不到萬不得已,不要去升級組件
坑我已經(jīng)替你們踩過
都是血的教訓(xùn),希望大家引以為戒
遇到問題,當(dāng)下推薦的做法是用
大數(shù)據(jù)模型
,關(guān)鍵詞給的好,得到的回答八九不離十就是正確的解決方案
到此這篇關(guān)于安全漏洞修復(fù)導(dǎo)致SpringBoot2.7與Springfox不兼容的偽命題解決的文章就介紹到這了,更多相關(guān)SpringBoot2.7與Springfox不兼容內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何應(yīng)對spring框架的HTTP ERROR 400 Bad Request錯
這篇文章主要介紹了如何應(yīng)對spring框架的HTTP ERROR 400 Bad Request錯誤返回問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08Spring MVC學(xué)習(xí)之DispatcherServlet請求處理詳析
這篇文章主要給大家介紹了關(guān)于Spring MVC學(xué)習(xí)教程之DispatcherServlet請求處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11solr 配置中文分析器/定義業(yè)務(wù)域/配置DataImport功能方法(測試用)
下面小編就為大家?guī)硪黄猻olr 配置中文分析器/定義業(yè)務(wù)域/配置DataImport功能方法(測試用)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09Springboot整合多數(shù)據(jù)源代碼示例詳解
這篇文章主要介紹了Springboot整合多數(shù)據(jù)源代碼示例詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08spring boot 加載web容器tomcat流程源碼分析
本文章主要描述spring boot加載web容器 tomcat的部分,為了避免文章知識點過于分散,其他相關(guān)的如bean的加載,tomcat內(nèi)部流程等不做深入討論,具體內(nèi)容詳情跟隨小編一起看看吧2021-06-06SpringBoot集成WebSocket的兩種方式(JDK內(nèi)置版和Spring封裝版)
這篇文章主要介紹了SpringBoot集成WebSocket的兩種方式,這兩種方式為JDK內(nèi)置版和Spring封裝版,本文結(jié)合示例代碼給大家介紹的非常詳細,需要的朋友可以參考下2023-06-06java中StringBuffer的length()和capacity()方法對比
這篇文章主要介紹了java中StringBuffer的length()和capacity()方法對比,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07Eclipse搭建spring開發(fā)環(huán)境圖文教程(推薦)
下面小編就為大家?guī)硪黄狤clipse搭建spring開發(fā)環(huán)境圖文教程(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07Spring boot事件監(jiān)聽實現(xiàn)過程解析
這篇文章主要介紹了,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06