SpringBoot集成Swagger UI访问403及页面无法加载问题修复
SpringBoot+SpringSecurity+JWT集成Springfox Swagger2问题修复方案
问题说明
项目集成Springfox Swagger2时出现两个异常:
- 本地启动服务后Swagger文档不会自动打开,手动访问
host/v2/api-docs可正常返回JSON格式接口文档,但Swagger UI界面无法加载,已引入springfox-swagger-ui依赖仍未生效 - 除
v2/api-docs路径外,访问Swagger相关链接(如localhost:8000/swagger-ui.html#!/signin)返回403授权错误,页面提示There was an unexpected error (type=Forbidden, status=403). Access Denied
根因排查
Swagger UI无法加载根因
- 静态资源放行规则缺失:Swagger UI渲染依赖webjars目录下的js、css等静态资源,以及
swagger-resources接口返回的配置元数据,原配置未将这些路径加入放行列表,资源被拦截后无法完成页面渲染 - 路径匹配规则无效:原配置中编写的
/**/swagger-ui.html#/**包含URL锚点#,锚点后的内容仅在浏览器前端解析,不会作为请求路径发送到服务端,Spring Security的antMatchers永远无法匹配到该规则,等于swagger-ui.html路径本身未被放行 - 静态资源未跳过安全过滤器链:Swagger相关静态资源会经过JWT校验过滤器,增加了被拦截的概率
Swagger页面403错误根因
核心原因为Swagger全量依赖路径未被完整放行,除已配置放行的v2/api-docs外,其余Swagger运行必需的路径都被Spring Security的anyRequest().authenticated()规则拦截,未携带有效JWT时直接返回403无权限错误。
可落地修复步骤
1. 修正Spring Security放行规则
修改WebSecurityConfig类的两个配置方法,原有业务权限规则全部保留,仅替换/新增Swagger相关配置:
- 替换
configure(HttpSecurity http)方法中Swagger相关的错误放行规则:
http.authorizeRequests() // 原有业务放行路径保留 .antMatchers("/**/signin/otp", "/**/signin/**").permitAll() // 替换为全量Swagger路径放行 .antMatchers( "/**/v2/api-docs/**", "/**/swagger-ui.html", "/**/swagger-resources/**", "/**/webjars/**", "/**/swagger-ui/**" ).permitAll() // 以下原有业务权限规则全部保持不变,不要修改 .antMatchers("/**/customers/create").hasAnyAuthority(SALES) .antMatchers("/**/customers/update").hasAnyAuthority(SALES) .antMatchers("/**/customers/all").hasAnyAuthority(SALES) .antMatchers("/**/customers/deactivate").hasAnyAuthority(SALES) .antMatchers("/**/customers/reactivate").hasAnyAuthority(SALES) .antMatchers("/**/products/create").hasAnyAuthority(SALES) .antMatchers("/**/products/update").hasAnyAuthority(SALES) .antMatchers("/**/users/create").hasAnyAuthority(SALES) .antMatchers("/**/users/update").hasAnyAuthority(SALES) .antMatchers("/**/users/deactivate").hasAnyAuthority(SALES) .antMatchers("/**/users/reactivate").hasAnyAuthority(SALES) .antMatchers("/**/admin/user/all").hasAnyAuthority(ADMIN) .antMatchers("/**/xicustomers/create").hasAnyAuthority(SALES) .antMatchers("/**/xicustomers/update").hasAnyAuthority(SALES) .antMatchers("/**/xicustomers/all").hasAnyAuthority(SALES) .antMatchers("/**/partner/create").hasAnyAuthority(SALES) .antMatchers("/**/xicustomers/list").hasAnyAuthority(XI_PARTNER,XI_CONSULTANT) .antMatchers("/**/report/list/**").hasAnyAuthority(XI_CONSULTANT) .antMatchers("/**/originator").hasAnyAuthority(STANDART) .antMatchers("/**/blackhour/add").hasAnyAuthority(STANDART) .antMatchers("/**/blackhour").hasAnyAuthority(STANDART) .antMatchers("/**/access/**").anonymous() .antMatchers("/**/pwd/forgot").anonymous() .antMatchers("/**/maximo").anonymous() .anyRequest().authenticated();
- 修改
configure(WebSecurity web)方法,将Swagger静态资源加入全局忽略列表,不经过安全过滤器链:
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/*/") .antMatchers("/eureka/**") .antMatchers(HttpMethod.OPTIONS, "/**") // 新增Swagger静态资源全局忽略 .antMatchers( "/swagger-ui.html", "/swagger-ui/**", "/swagger-resources/**", "/v2/api-docs/**", "/webjars/**" ); }
2. 优化JWT过滤器逻辑,跳过Swagger路径校验
修改JwtTokenFilter的doFilter方法,在token校验逻辑前新增Swagger路径判断,直接放行不做JWT校验:
@Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; String requestURI = request.getRequestURI(); // 新增:Swagger相关路径直接放行,跳过JWT校验 if (requestURI.contains("swagger") || requestURI.contains("webjars") || requestURI.contains("v2/api-docs")) { filterChain.doFilter(req, res); return; } String token = getBearerToken((HttpServletRequest) req); // 原有token校验逻辑全部保留,不要修改 if (token != null && !requestURI.contains("/signin/otp")) { TokenParams params = null; try { params = this.jwtTokenProvider.validateToken(token); } catch (JwtException | IllegalArgumentException e) { log.warn("Invalid Token: {}, Error: {}", params, e.getMessage()); throw new UnauthorizedException(); } if (!params.getRoles().contains(WebSecurityConfig.ADMIN) && params.isForOtp() == true) { log.warn("Invalid Token: {}, it is for OTP!", params); throw new UnauthorizedException(); } Authentication auth = this.jwtTokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(auth); HeaderMapRequestWrapper wrappedRequest = new HeaderMapRequestWrapper(request); wrappedRequest.addHeader("companyId", params.getCompanyId()); wrappedRequest.addHeader("user", params.getEmail()); filterChain.doFilter(wrappedRequest, res); } else { filterChain.doFilter(req, res); } }
3. 版本兼容性处理(仅SpringBoot 2.6.x及以上版本需要配置)
如果项目使用SpringBoot 2.6.0及以上版本,需要在application配置文件中添加如下配置,解决Springfox与SpringMVC默认路径匹配规则不兼容的问题:
spring: mvc: pathmatch: matching-strategy: ant_path_matcher
4. 静态资源映射配置(可选,上述步骤完成后仍无法加载UI时添加)
新增Spring MVC资源映射配置,确保Swagger静态资源可被正常解析:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); } }
验证标准
- 重启服务后访问
http://localhost:8000/swagger-ui.html可正常加载Swagger UI界面,无静态资源404错误 - 点击Swagger页面内的任意接口(包括
/signin接口)不会返回403错误,可正常发起接口调试 - 原有业务接口的权限校验逻辑不受影响
内容的提问来源于stack exchange,提问作者Defne Sabancı
相关产品推荐
相关产品推荐

