资讯动态

Spring Boot整合Spring Security与Swagger 2.9.2:放行配置与避坑指南

发布时间:2026/10/14 4:38:19 来源:尧图企业网站定制
1. 项目缘起与整体设计思路1.1 为什么要在Spring Boot里同时引入Security和Swagger做过Java后端的朋友大概率都遇到过这个场景项目初期为了快速出接口文档引入了Swagger页面上点点就能调试前后端联调效率很高。后来安全需求上来了要加登录认证、权限控制于是引入了Spring Security。结果两个东西一整合问题就来了——Swagger的页面打不开了浏览器一访问就弹401或者直接跳登录页接口文档形同虚设。这个问题的本质其实不复杂。Spring Security默认会对所有进入应用的HTTP请求做安全过滤而Swagger2.9.2这个版本暴露出来的静态资源路径和API描述端点恰好落在了Security的拦截范围里。Security不认识这些路径自然就拦下来了。所以“完美整合”的核心就是让Security对Swagger相关的资源“放行”同时又不影响业务接口的安全防护。我这次要分享的就是基于Spring Boot Spring Security Swagger 2.9.2这套组合把两者整合到能正常使用、互不干扰的完整方案。这套方案适合正在做前后端分离项目、需要接口文档又要权限控制的后端开发者尤其是刚接触Spring Security、被拦截规则绕晕的朋友。下面我会从设计思路、核心配置、实操步骤到踩坑排查一步步拆开讲清楚。1.2 整体方案选型与版本搭配的考量版本搭配这件事在Spring生态里从来不是随便选的。Spring Boot的版本直接决定了Spring Security的默认行为而Swagger 2.9.2又是一个相对“老派”但极其稳定的版本很多存量项目还在用。我这次选用的组合是Spring Boot 2.x2.1到2.3区间都验证过、Spring Security 5.x随Boot版本走、Swagger 2.9.2。为什么不用Swagger 3.x也就是springfox 3.0因为3.0在Spring Boot 2.6以上会遇到路径匹配策略的兼容问题需要额外配置PathPatternParser反而增加复杂度。而2.9.2虽然老但它的资源路径固定、文档清晰配合Security做放行规则时更好控制。这是一个典型的“稳定优先”选择尤其适合已经在生产环境跑着的项目做增量改造。方案的整体思路分三层第一层是让Swagger的资源配置类正常生成文档描述第二层是在Security的配置里把Swagger涉及的所有URL路径显式放行第三层是确保放行规则不会误伤业务接口也不会因为顺序问题被其他规则覆盖。这三层缺一不可很多人只做了第二层结果还是打不开就是因为忽略了资源路径的完整性和规则顺序。1.3 整合后能达到的效果预期整合完成后你应该能达到这样的状态访问Swagger UI页面通常是/swagger-ui.html不需要登录能正常看到所有接口分组和描述点击具体接口调试时如果该接口本身需要认证Swagger会提示你输入token或认证信息业务接口依然受Security保护未认证请求返回401或跳转登录。换句话说文档页面是“公开可读”的但文档里描述的接口该安全还是安全。这里有个细节要提前说清楚Swagger UI页面公开意味着接口的路径、参数、返回值结构对外可见。这在内部开发环境没问题但如果生产环境也要暴露文档就需要额外考虑是否加一层访问限制比如只允许内网IP访问或者给Swagger页面本身也加一个简单的认证。这个取舍我在后面的注意事项里会详细展开。2. 核心细节解析与实操要点2.1 Swagger 2.9.2的资源配置类怎么写才规范Swagger的配置类是整个整合的基础。很多人写配置类时只写一个DocketBean就完事了结果文档页面能打开但接口分组混乱或者某些接口不显示。一个规范的配置类应该包含Docket实例的构建明确指定扫描的包路径、分组名、文档类型以及是否启用安全认证上下文。Configuration EnableSwagger2 public class SwaggerConfig { Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(com.example.demo.controller)) .paths(PathSelectors.any()) .build() .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(项目接口文档) .description(后端API说明) .version(1.0) .build(); } private ListApiKey securitySchemes() { ListApiKey list new ArrayList(); list.add(new ApiKey(Authorization, Authorization, header)); return list; } private ListSecurityContext securityContexts() { ListSecurityContext list new ArrayList(); list.add(SecurityContext.builder() .securityReferences(defaultAuth()) .forPaths(PathSelectors.regex(^(?!auth).*$)) .build()); return list; } private ListSecurityReference defaultAuth() { AuthorizationScope scope new AuthorizationScope(global, accessEverything); AuthorizationScope[] scopes {scope}; ListSecurityReference list new ArrayList(); list.add(new SecurityReference(Authorization, scopes)); return list; } }这段代码里securitySchemes和securityContexts是关键。它们让Swagger UI上出现一个“Authorize”按钮你可以把登录后拿到的token填进去之后调试需要认证的接口时Swagger会自动在请求头里带上Authorization字段。没有这部分配置你调试受保护接口时永远返回401还以为是Security配置错了。注意forPaths里的正则^(?!auth).*$表示除了路径以auth开头的接口外其他接口都应用这个安全上下文。这个正则需要根据你实际的登录接口路径调整否则可能出现登录接口也要求填token的死循环。2.2 Spring Security放行规则的核心配置逻辑Security的配置是整合成败的关键。在Spring Security 5.x里推荐用WebSecurityConfigurerAdapter虽然新版本标记过时但2.x项目里仍是主流或者直接配置SecurityFilterChain。核心动作是在configure(HttpSecurity http)方法里对Swagger相关路径调用permitAll()。Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers( /swagger-ui.html, /swagger-ui/**, /swagger-resources/**, /v2/api-docs, /v2/api-docs-ext, /webjars/**, /doc.html, /favicon.ico ).permitAll() .antMatchers(/auth/login, /auth/register).permitAll() .anyRequest().authenticated() .and() .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }这里放行的路径列表不是随便写的每一条都对应Swagger 2.9.2运行时的实际请求。/swagger-ui.html是入口页面/swagger-resources/**是分组资源/v2/api-docs是文档数据接口/webjars/**是页面依赖的静态JS和CSS。少放行任何一个页面都可能白屏或者报404。csrf().disable()这一行也很重要。Swagger UI在调试POST接口时会发跨站请求如果CSRF防护开着请求会被拦截返回403。开发环境通常直接关掉CSRF生产环境如果必须开启就需要额外处理Swagger的请求令牌复杂度会上升不少。2.3 路径匹配的坑antMatchers的顺序与通配符antMatchers的匹配顺序是从上到下一旦匹配成功就不再往下走。所以放行Swagger的规则必须写在anyRequest().authenticated()之前否则所有请求先被要求认证Swagger路径也不例外。这个顺序问题我见过太多人踩坑配置写对了但位置放错排查半天。通配符的使用也有讲究。/swagger-resources/**能匹配/swagger-resources本身以及其下所有子路径但/swagger-resources/*只能匹配一级。Swagger 2.9.2实际会请求/swagger-resources/configuration/ui和/swagger-resources/configuration/security这样的二级路径所以必须用/**。同理/webjars/**也不能写成/webjars/*。另外如果你用了server.servlet.context-path给应用加了前缀比如/api那么Swagger的路径也会变成/api/swagger-ui.html。这时候Security里的放行路径要么加上前缀要么用antMatchers的相对路径写法Security默认会去掉context-path再匹配。这个细节在部署到有网关的环境时特别容易出问题。3. 完整实操过程与关键环节实现3.1 依赖引入与版本锁定第一步是把依赖加进pom.xml。Swagger 2.9.2需要两个包springfox-swagger2和springfox-swagger-ui。Security的包通常由Spring Boot的starter自动带入但为了明确版本建议显式声明。dependency groupIdio.springfox/groupId artifactIdspringfox-swagger2/artifactId version2.9.2/version /dependency dependency groupIdio.springfox/groupId artifactIdspringfox-swagger-ui/artifactId version2.9.2/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency这里有个隐藏坑如果你的项目里同时引入了其他版本的swagger依赖比如通过某个第三方starter间接带入会出现NoSuchMethodError或者ClassNotFoundException。排查方法是执行mvn dependency:tree | grep springfox确认只有一个2.9.2版本。我遇到过引入某个工具库时带入了springfox 2.6结果Docket类的构造方法对不上启动直接报错。3.2 配置文件中的Swagger开关与路径调整在application.yml里可以加一些Swagger的开关配置方便在不同环境控制文档是否启用。springfox: documentation: swagger-ui: enabled: true auto-startup: true server: servlet: context-path: /auto-startup设为true时应用启动就会初始化Swagger文档。如果生产环境不想暴露文档可以设为false这样Swagger的端点根本不会注册Security里放不放行都无所谓了。这是一个比在Security里做IP限制更彻底的方案。context-path如果改了记得同步调整Security的放行路径。比如设为/api那么放行列表里要写成/api/swagger-ui.html或者干脆用requestMatchers配合servletPath的写法。我个人的习惯是保持context-path为/把前缀交给网关处理这样Security配置最简单。3.3 启动验证与页面访问的完整流程配置写完后启动应用控制台会打印Swagger初始化的日志类似Started Swagger in xxx ms。这时候打开浏览器访问http://localhost:8080/swagger-ui.html应该能看到接口文档页面。如果页面出来了但接口列表是空的检查basePackage是否写对了或者Controller类上有没有加Api、方法上有没有加ApiOperation。验证Security放行是否生效可以打开浏览器开发者工具的Network面板刷新Swagger页面看/v2/api-docs这个请求返回的是200还是401。返回200说明放行成功返回401说明还有路径没放行或者顺序不对。这个排查方法比看日志快得多。调试受保护接口时先在登录接口拿到token点击Swagger页面右上角的“Authorize”按钮把token填进去如果配置了ApiKey的话然后调用其他接口请求头里会自动带上Authorization。如果没配置securitySchemes就需要手动在请求头里加比较麻烦。3.4 生产环境下的安全加固建议开发环境把Swagger页面完全公开没问题但生产环境直接暴露接口文档是有风险的。我通常的做法是通过springfox.documentation.swagger-ui.enabledfalse在生产配置里关掉Swagger这样最干净。如果确实需要生产环境也能查文档那就加一层简单的访问控制比如在Security里对Swagger路径要求一个特定的角色或者用Nginx做IP白名单。还有一种折中方案Swagger页面公开但/v2/api-docs这个数据接口需要认证。这样页面能打开但看不到具体接口列表只有登录后才能看到。实现方式是在Security里对/v2/api-docs单独设置authenticated()其他Swagger静态资源放行。这个方案适合内部系统既方便登录后查文档又不会让未登录用户看到接口结构。4. 常见问题与排查技巧实录4.1 Swagger页面404或白屏的排查路径页面404通常有两种原因一是springfox-swagger-ui依赖没引入二是Security把静态资源拦了。先检查依赖再检查Security放行列表里有没有/webjars/**和/swagger-ui.html。如果页面能打开但白屏按F12看Console有没有JS报错通常是/webjars/**下的某个JS文件被拦截返回401浏览器加载失败。还有一种情况是Spring Boot 2.6以上版本默认路径匹配策略从AntPathMatcher改成了PathPatternParser导致Swagger的某些路径匹配不上。解决办法是在配置里加spring.mvc.pathmatch.matching-strategyant_path_matcher强制用回旧策略。这个坑在2.6刚发布时坑了一大批人。4.2 401与403错误的区分与处理401是未认证403是已认证但无权限。Swagger页面打不开如果返回401说明Security的放行没生效请求被要求先登录。如果返回403可能是CSRF拦截或者权限规则配置过严。区分方法很简单看响应头里有没有WWW-Authenticate有就是401。处理401就检查放行路径和顺序处理403先看CSRF是否关闭再看authorizeRequests里有没有对Swagger路径设置了hasRole之类的限制。我遇到过一种情况放行规则写对了但前面有一条antMatchers(/**).authenticated()把Swagger路径也覆盖了。这就是顺序问题放行规则必须写在最前面。4.3 接口文档不显示或分组混乱的解决接口不显示先确认Controller是否在basePackage指定的包下再确认方法上有没有ApiOperation注解。Swagger 2.9.2默认只扫描带有Api的Controller类如果类上没加整个类的接口都不显示。分组混乱通常是配置了多个DocketBean但没有指定groupName或者groupName重复了。如果用了ApiIgnore注解被标注的接口会从文档中隐藏这是有意为之的。排查时搜一下代码里有没有这个注解。另外如果接口路径有重复Swagger会覆盖显示需要检查RequestMapping的路径是否唯一。4.4 常见问题速查表问题现象可能原因排查方法解决方案页面404依赖缺失或路径被拦检查pom依赖和Security放行列表补依赖加放行路径页面白屏静态资源被拦F12看Console报错放行/webjars/**返回401放行未生效看Network里/v2/api-docs状态码调整放行路径和顺序返回403CSRF或权限限制看响应头关闭CSRF或调整权限规则接口不显示包路径或注解问题检查basePackage和Api修正包路径加注解启动报错版本冲突mvn dependency:tree排除旧版本springfox提示每次修改Security配置后建议清一下浏览器缓存再测试因为Swagger的静态资源可能被缓存导致你以为配置没生效。4.5 几个我踩过的坑和独家经验第一个坑是EnableWebSecurity和EnableGlobalMethodSecurity同时使用时如果方法级安全注解配置不当会导致Swagger的某些端点被方法级拦截。我的经验是Swagger相关的Controller不要加方法级安全注解或者把prePostEnabled设为false先排除干扰。第二个坑是用了自定义的Filter或Interceptor在Security之前就把Swagger请求拦截了。排查时可以在Filter里打印请求路径看Swagger的请求有没有到达Security层。如果被自定义Filter拦了需要在Filter里对Swagger路径做放行。第三个经验是关于token的。Swagger UI的“Authorize”按钮填的token默认只在当前浏览器会话有效刷新页面就没了。如果调试频繁可以把token存在浏览器的localStorage里或者用Postman之类的工具配合Swagger的/v2/api-docs数据做调试。Swagger UI本身更适合看文档结构复杂调试还是专业工具更顺手。最后一个建议整合完成后把Security的放行路径和Swagger的配置类写进项目的README或者内部Wiki标注清楚每个路径的作用。下次换人维护或者升级版本时能省下大量排查时间。这套组合虽然稳定但版本升级时的兼容性问题不少文档留痕是最低成本的风险对冲。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑