先说结论:给 SpringBoot 项目的 swagger-ui.html 加登录页面,真正费时间的不是写登录接口,而是搞清楚"到底要拦哪些路径、放行哪些资源"。我见过不止一个项目,辛辛苦苦把登录页和拦截器写好,部署到服务器上之后,要么登录页样式全丢,要么有人绕过入口直接拿接口文档,要么 Spring Boot 一升级 Swagger 直接启动失败。这篇文章就把这件事从头到尾拆一遍:方案怎么选、Swagger 的静态资源链路是什么、拦截器怎么实现、Spring Security 怎么做、以及那些年报上查不到但百分之百会踩的坑。
先说清楚,不是所有项目都需要这一步。开发环境自己本地调试,Swagger 裸奔没有任何问题;但一旦项目打成 jar 包部署到测试服务器、生产服务器,http://服务器IP:端口/swagger-ui.html 谁都能打开,意味着系统的全部接口路径、入参出参、接口说明全都暴露在公网上,这相当于把系统说明书贴在了大门上。给 Swagger 加登录页,本质就是给这份说明书加一道门禁。
1. 先想清楚方案:拦截器、Filter 还是 Spring Security
1.1 为什么"加个登录页"不能只加页面
很多人第一次做这个需求,第一反应是写一个 login.html,再写个接口校验用户名密码,就以为完了。实际上一旦做了登录拦截,你要处理的不只是"登录"这一个动作,而是一整条链路:
- 登录页本身必须放行,否则会循环重定向到登录页又回到登录页。
- 登录接口必须放行,否则表单提交直接被拦截器拦下。
- Swagger 的资源如果放在拦截范围内,未登录用户访问这些资源时会被重定向到登录页,导致登录成功后页面样式和脚本加载不完整。
- 登录成功后要跳转到 swagger-ui.html,但跳转后的二次资源加载如果仍然被拦,就要重新走一遍会话校验。
这还只是最基础的逻辑。实际项目里还要考虑多环境配置:开发环境不想每次都登录,测试环境和生产环境又必须强制登录。所以这个需求真正考验的是"路径梳理"和"方案选型",不是那几行登录代码。
1.2 三种主流方案的取舍
我自己的经验是,这种场景下有三种主流做法,各有各的适用场景。
第一种是 Spring MVC 的 HandlerInterceptor。它只拦截 controller 层的请求,代码量最小、概念最简单,普通开发者一眼能看懂,适合大多数 SpringBoot 单体项目。缺点是不会拦截静态资源的直接访问,但 Swagger 的入口都是 controller 映射的 URL,所以用它足够。
第二种是 Servlet Filter。Filter 比拦截器更底层,可以拦到静态资源、JSP、任何 Servlet 路径。它的好处是控制力强,坏处也是控制力太强——你不小心就可能把 JS、CSS、图片全部拦了,排查起来比拦截器麻烦。除非你有特殊需求(比如统一给所有请求加白名单校验),否则我建议优先用拦截器。
第三种是 Spring Security。它是标准的安全框架,登录、会话、CSRF、密码加密全套都有,适合对安全要求高的项目。但代价是学习成本高、配置繁琐,而且 Spring Security 和 Springfox / springdoc 之间存在版本兼容问题,后面我会专门讲到。
我的建议是:中小型项目、团队里没有专门安全开发人员的,直接用拦截器方案;项目本身已经引入 Spring Security 的,就顺着 Security 的体系做,不要混用,否则两套会话体系会互相打架。
1.3 先搞清楚 Swagger 页面的资源加载链路
这是整个需求中最容易忽略、也最容易出错的地方。/swagger-ui.html 看起来只是一个页面,但它背后是一条完整的资源加载链:
- 浏览器请求
/swagger-ui.html,这是 springfox 提供的一个转发入口。 - 页面加载后,会向
/swagger-resources发起请求,获取当前项目里配置了哪些 Swagger 分组。 - 拿到分组后,再向
/v2/api-docs或/v3/api-docs请求接口定义 JSON,里面包含了所有 controller 的路径、参数、返回结构。 - 同时页面还需要加载
/webjars/springfox-swagger-ui/**下的一堆 JS、CSS、字体文件,这些是 Swagger UI 的静态资源。
也就是说,如果你只拦 /swagger-ui.html 这一个路径,未登录用户虽然打不开页面,但直接访问 /v2/api-docs 或者 /swagger-resources 一样能拿到接口数据。所以在配置拦截范围时,入口页面、接口文档 API、Swagger 资源配置这几类路径都要一起处理。
这个链路还会直接影响你配置登录成功后的跳转逻辑。我见过有人登录成功后跳转到 /,结果进了项目首页而不是文档页,还要手动再点一次菜单,体验很差。正确做法是登录成功后重定向回 /swagger-ui.html,让用户一步到位。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手前先排雷:SpringBoot 高版本与 Swagger 的兼容性问题
2.1 springfox 在 SpringBoot 2.6+ 上的经典报错
热词里有人提到"springboot 版本太高",这确实是 Swagger 项目最常见的问题之一。如果你用的是 springfox 3.0.0 搭配 SpringBoot 2.6 及以上版本,启动项目时大概率会遇到类似这样的报错:
code复制Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
或者:
code复制java.lang.NoSuchMethodError: org.springframework.util.PathMatcher.combine(Ljava/lang/String;Ljava/lang/String;)Lorg/springframework/util/PathMatcher;
这个坑的根源在于 SpringBoot 2.6 之后,Spring MVC 的默认路径匹配策略从 AntPathMatcher 换成了 PathPatternParser。而 springfox 3.0.0 内部仍然依赖 AntPathMatcher,两者一冲突就启动失败。
解决方案是在 application.yml 里加一行配置,强制 Spring MVC 使用老的匹配策略:
yaml复制spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
加了这一行,绝大多数 springfox 3.0 项目都能正常启动。但要注意,这只是"兼容"方案,不是"根治"方案。如果项目里同时用了其他依赖 PathPatternParser 的组件,可能还会有隐性冲突。如果你在新建项目,我的建议是用 springdoc-openapi 替代 springfox,它对高版本 SpringBoot 的支持更积极,UI 入口也从 /swagger-ui.html 变成了 /swagger-ui/index.html,风格更现代,相关问题也少得多。
2.2 版本差异带来的路径变化
这里顺便把版本差异理清楚,因为很多网上教程只说 /swagger-ui.html,但不同框架、不同版本的实际访问路径不一样:
| 框架版本 | 默认访问路径 | 说明 |
|---|---|---|
| springfox 2.x | /swagger-ui.html |
经典入口,网上教程最多 |
| springfox 3.0.0 | /swagger-ui.html |
仍然兼容,但加了 /swagger-ui/ 新路径 |
| springdoc-o |
