Swagger开启账号验证访问

news/2025/11/16 20:11:26/文章来源:https://www.cnblogs.com/nuccch/p/19228853

背景概述

在一些小型的Java后端开发项目工程中集成Swagger生成接口文档是一个比较普遍的做法,默认情况下访问Swagger文档是没有限制的,任何人都可以访问并进行调试。这在某些场合下可能并不合适,特别是对于一些具备写数据的接口,随意暴露可能会被人恶意利用。因此,需要对访问接口的人进行一道认证拦截,只允许特定账户的人可以访问。

认证实现

只需要通过简单的配置即可实现对Swagger文档进行访问认证,如下阐述。

依赖配置:

<!-- 集成swagger -->
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version>
</dependency><!-- swagger UI主题:swagger-ui-layer -->
<!-- 访问路径:/docs.html -->
<dependency><groupId>com.github.caspar-chen</groupId><artifactId>swagger-ui-layer</artifactId><version>1.1.3</version>
</dependency>

如下是一个未开启访问认证的Swagger配置示例(Spring Boot版本为2.6.13):

@Configuration
@EnableSwagger2
public class SwaggerConfig {@Beanpublic Docket docket() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).enable(true).select().apis(RequestHandlerSelectors.basePackage("com.xx.yy.controller")).paths(PathSelectors.any()).build();}/*** 用于定义API主界面的信息,比如可以声明所有的API的总标题、描述、版本*/private ApiInfo apiInfo() {Contact contact = new Contact("我是作者姓名", // 作者姓名"https://blog.csdn.net/", // 作者网址"123456789@163.com"); // 作者邮箱return new ApiInfoBuilder().title("XX项目") //  可以用来自定义API的主标题//.description("XX项目接口") // 可以用来描述整体的API//.termsOfServiceUrl("https://www.baidu.com") // 用于定义服务的域名(跳转链接).version("1.0") // 可以用来定义版本//.license("Swagger-的使用教程")//.licenseUrl("https://blog.csdn.net")//.contact(contact).build(); //}// fix error: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException@Beanpublic static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {return new BeanPostProcessor() {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof WebMvcRequestHandlerProvider) {customizeSpringfoxHandlerMappings(getHandlerMappings(bean));}return bean;}private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());mappings.clear();mappings.addAll(copy);}@SuppressWarnings("unchecked")private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {try {Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");field.setAccessible(true);return (List<RequestMappingInfoHandlerMapping>) field.get(bean);} catch (IllegalArgumentException | IllegalAccessException e) {throw new IllegalStateException(e);}}};}
}

如果要开启对Swagger文档的访问认证,需要在配置类SwaggerConfig上应用注解@EnableSwaggerBootstrapUI,如下:

@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
public class SwaggerConfig {
}

同时,在项目配置文件中添加Swagger认证信息:

# 启用Swagger文档
springfox:documentation:enabled: true# 开启swagger认证
swagger:production: false ## 在生产环境不开启Swagger文档basic: ## 配置Swagger访问认证信息enable: trueusername: adminpassword: 123456

完成如上配置后即可开启Swagger访问认证,再次访问接口文档时会要求输入用户名和密码信息,如下图:
开启Swagger认证

支持多个认证账户

在Swagger的默认认证实现中,只支持配置单个用户。如果需要给不同的人分配不同的认证账户,就需要自定义实现。
另外,通过自定义实现还可以在服务端日志中记录相应账户访问接口的信息。

要自定义Swagger访问认证,核心就是自定义一个新的@EnableSwaggerBootstrapUI注解,在如下示例中自定义一个名为MyEnableSwaggerBootstrapUI的注解。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({SwaggerBootstrapUIConfiguration.class, SwaggerSecurityConfiguration.class, MarkdownFileConfiguration.class})
public @interface MyEnableSwaggerBootstrapUI {
}

引入的配置类SwaggerSecurityConfiguration定义如下:

@Configuration
public class SwaggerSecurityConfiguration {private Logger logger= LoggerFactory.getLogger(SwaggerSecurityConfiguration.class);@Autowiredprivate Environment environment;@Beanpublic ProductionSecurityFilter productionSecurityFilter(){boolean prod=false;if (environment!=null){String prodStr=environment.getProperty("swagger.production");if (logger.isDebugEnabled()){logger.debug("swagger.production:{}",prodStr);}prod=Boolean.valueOf(prodStr);}ProductionSecurityFilter p=new ProductionSecurityFilter(prod);return p;}@Beanpublic SwaggerSecurityBasicAuthFilter securityBasicAuthFilter(){boolean enableSwaggerBasicAuth=false;Map<String, String> basicInfoMap = new HashMap<String, String>();if (environment!=null){String enableAuth=environment.getProperty("swagger.basic.enable");enableSwaggerBasicAuth=Boolean.valueOf(enableAuth);if (enableSwaggerBasicAuth){// 如果开启basic验证,从配置文件中获取用户名和密码// 在这里实现从配置文件中读取多个账户信息String pUser=environment.getProperty("swagger.basic.username");String pPass=environment.getProperty("swagger.basic.password");String[] pUserArr = pUser.split(",");String[] pPassArr = pPass.split(",");for (int i = 0; i < pUserArr.length; i++) {basicInfoMap.put(pUserArr[i], pPassArr[i]);}}}SwaggerSecurityBasicAuthFilter securityBasicAuthFilter= new SwaggerSecurityBasicAuthFilter(enableSwaggerBasicAuth, basicInfoMap);return securityBasicAuthFilter;}
}

核心的SwaggerSecurityBasicAuthFilter定义如下:

public class SwaggerSecurityBasicAuthFilter extends SwaggerBasicFilter implements Filter {private static final Logger logger = LoggerFactory.getLogger(SwaggerSecurityBasicAuthFilter.class);/**** 是否开启basic验证,默认不开启*/private boolean enableBasicAuth=false;/** 认证信息集合 */private Map<String, String> basicInfoMap = new HashMap<String, String>();@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Enumeration<String> enumeration=filterConfig.getInitParameterNames();//SpringMVC环境中,由此init方法初始化此Filter,SpringBoot环境中则不同if (enumeration.hasMoreElements()){setEnableBasicAuth(Boolean.valueOf(filterConfig.getInitParameter("enableBasicAuth")));this.basicInfoMap.put(filterConfig.getInitParameter("userName"), filterConfig.getInitParameter("password"));}}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest servletRequest=(HttpServletRequest)request;HttpServletResponse httpServletResponse=(HttpServletResponse)response;//针对swagger资源请求过滤if (enableBasicAuth){if (match(servletRequest.getRequestURI())){//判断Session中是否存在Object swaggerSessionValue=servletRequest.getSession().getAttribute(SwaggerBootstrapUiBasicAuthSession);if (swaggerSessionValue!=null){chain.doFilter(request,response);}else{//匹配到,判断auth//获取请求头AuthorizationString auth=servletRequest.getHeader("Authorization");if (auth==null||"".equals(auth)){writeForbiddenCode(httpServletResponse);return;}String userAndPass=decodeBase64(auth.substring(6));String[] upArr=userAndPass.split(":");if (upArr.length!=2){writeForbiddenCode(httpServletResponse);}else{String iptUser=upArr[0];String iptPass=upArr[1];String basicPass = this.basicInfoMap.get(iptUser);if (basicPass == null){logger.error("basicUser not found:{}", iptUser);writeForbiddenCode(httpServletResponse);return;}//匹配服务端用户名及密码if (iptPass.equals(basicPass)){// 将认证信息保存到会话中servletRequest.getSession().setAttribute(SwaggerBootstrapUiBasicAuthSession,iptUser);chain.doFilter(request,response);}else{writeForbiddenCode(httpServletResponse);return;}}}}else{chain.doFilter(request,response);}}else{chain.doFilter(request,response);}}@Overridepublic void destroy() {}private void writeForbiddenCode(HttpServletResponse httpServletResponse) throws IOException {httpServletResponse.setStatus(401);httpServletResponse.setHeader("WWW-Authenticate","Basic realm=\"input Swagger Basic userName & password \"");httpServletResponse.getWriter().write("You do not have permission to access this resource");}// 通过参数basicInfoMap设置多个认证账户信息public SwaggerSecurityBasicAuthFilter(boolean enableBasicAuth, Map<String, String> basicInfoMap) {this.enableBasicAuth = enableBasicAuth;if (basicInfoMap != null){this.basicInfoMap.putAll(basicInfoMap);}}public SwaggerSecurityBasicAuthFilter(boolean enableBasicAuth) {this.enableBasicAuth = enableBasicAuth;}public SwaggerSecurityBasicAuthFilter() {}public boolean isEnableBasicAuth() {return enableBasicAuth;}public void setEnableBasicAuth(boolean enableBasicAuth) {this.enableBasicAuth = enableBasicAuth;}
}

SwaggerBasicFilter定义如下:

public class SwaggerBasicFilter implements Consts {private Logger logger= LoggerFactory.getLogger(BasicFilter.class);protected List<Pattern> urlFilters=null;public SwaggerBasicFilter(){urlFilters=new ArrayList<>();// 添加对swagger UI主题(swagger-ui-layer)的路径:/docs.html 拦截urlFilters.add(Pattern.compile(".*?/docs\\.html.*",Pattern.CASE_INSENSITIVE));urlFilters.add(Pattern.compile(".*?/doc\\.html.*",Pattern.CASE_INSENSITIVE));urlFilters.add(Pattern.compile(".*?/v2/api-docs.*",Pattern.CASE_INSENSITIVE));urlFilters.add(Pattern.compile(".*?/v2/api-docs-ext.*",Pattern.CASE_INSENSITIVE));urlFilters.add(Pattern.compile(".*?/swagger-resources.*",Pattern.CASE_INSENSITIVE));urlFilters.add(Pattern.compile(".*?/swagger-ui\\.html.*",Pattern.CASE_INSENSITIVE));urlFilters.add(Pattern.compile(".*?/swagger-resources/configuration/ui.*",Pattern.CASE_INSENSITIVE));urlFilters.add(Pattern.compile(".*?/swagger-resources/configuration/security.*",Pattern.CASE_INSENSITIVE));}protected boolean match(String uri){boolean match=false;if (uri!=null){for (Pattern pattern:getUrlFilters()){if (pattern.matcher(uri).matches()){match=true;break;}}}return match;}protected String decodeBase64(String source){String decodeStr = null;if (source!=null){//BASE64Decoder decoder=new BASE64Decoder();try {//byte[] bytes=decoder.decodeBuffer(source);byte[] bytes= Base64.getDecoder().decode(source);decodeStr=new String(bytes);} catch (Exception e) {logger.error(e.getMessage(),e);}}return decodeStr;}public List<Pattern> getUrlFilters() {return urlFilters;}
}

完成上述自定义组件准备后,将SwaggerConfig配置类中的EnableSwaggerBootstrapUI注解换成MyEnableSwaggerBootstrapUI即可,即:

@Configuration
@EnableSwagger2
@MyEnableSwaggerBootstrapUI
public class SwaggerConfig {
}

同时,还可以在项目配置文件中添加多个账户信息:

# 开启swagger认证
swagger:production: false ## 在生产环境不开启Swagger文档basic: ## 配置Swagger访问认证信息enable: trueusername: zhangsan,lisi # 多个账户用户名,用英文逗号分隔password: 123456,524163 # 多个账户密码,用英文逗号分隔

这样,就可以给不同访问Swagger接口文档的人分配相应的账户信息,也便于在服务端记录不同账户访问接口的情况。

【参考】
Springboot整合swagger,以及开启环境、账号权限验证访问

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/967313.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

标准解读——GB/T 46353—2025《信息技术 大数据 资料资产价值评估》国家标准

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

noip7

11.1311.13 挂分场。 t1 神秘淀粉质。 不想改了,反正noip不考(题解说的)。 赛时写了2h发现想假了,当场😡。 t2 简单构造。 但是挂分了。 每个球跳的次数求假了(不是这也能假)。 然后就挂了。 codewww #include…

代码背后的故事:docker容器名生成算法

让我们一探每次docker run背后自动生成的容器名是怎么生成的,以及生成算法背后的故事吧。我们知道容器化最大的好处是软件交付形成了一种标准化,其带来的好处是巨大且深远的,让开发者从解决各种环境差异的痛苦中解放…

在Windows系统置顶窗口不被Win+D快捷键影响

需求背景 Windows10系统,工作需要开多个应用程序,在使用双屏幕时(多显示器设置为:扩展这些显示器),希望使用另外一个屏幕做监控用,总时只要打开一个浏览器窗口盯着监控信息。但是经常需要通过快捷键Win+D快速返…

dns服务详解

dns服务 1、dns服务是什么叫做名称服务,作用就是将域名解析成ip地址,这个叫做正向解析将ip地址解析成域名叫做反向解析还提供邮件服务器的交换记录在互联网中有很重要的作用是一个c/s架构,每一个电脑就是一个dns客户…

一乐人物志

LEWISAK,真名 Woiler,CSP-S 2025 rk721,尽管进了高新一中博雅班,但是在二班和一四班的学生拉开了一年的 whk 差距,回家后每天推旮旯隔膜,被骂是虾头油渍处,ZR 模拟赛送了 1500 分后 rating 1479。初二时染上了不…

详细介绍:基于Spring Boot的高校实习实践管理系统(源码+论文+部署+安装)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

点分树

事实上比较朴素。 P6329 【模板】点分树 | 震波 大致思路是将点分治的那个过程建成一棵树。每一层的重心和下一层的中心连边。 这棵树有两个重要性质:树高保证为 \(\log n\),任意两点的 lca 一定在这两个点的路径上。…

HTTP请求走私漏洞介绍 - 实践

HTTP请求走私漏洞介绍 - 实践2025-11-16 19:46 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important…

20232428 2025-2026-1 《网络与系统攻防技术》实验五实验报告

1.实验内容 (1)从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取信息 (2)尝试获取BBS、论坛、QQ、MSN中某一好友的IP地址,并查询获取该好友所在的具体地理位置 (3)使用nmap开源软件…

xml.etree.ElementTree 完全支持嵌套查找子元素,且有多种简洁实用的方式。

xml.etree.ElementTree 完全支持嵌套查找子元素,且有多种简洁实用的方式。 核心实现方式 1. XPath 路径直接嵌套查找(最常用) 通过 / 分隔层级,直接定位深层子元素,支持精确匹配标签名、属性等。 import xml.etre…

深入解析:Spring MVC 拦截器interceptor

深入解析:Spring MVC 拦截器interceptorpre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

HarmonyOS 5 鸿蒙Context上下文机制与资源管理详解 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

《重生之我成为世界顶级黑客》第八章:未来野望

《重生之我成为世界顶级黑客》第八章:未来野望词穷,暂时没灵感了。龙傲天盯着桌上那个用40积分换来的企鹅玩偶,嘴角泛起一丝苦涩的笑意。这次的经历像一盆冷水,浇醒了他这些天因微小进步而产生的自满。企业的安全防…

打开工作空间时,但未在 DTD/架构中声明

OpenFromFile时,如果路径不存在或者不合法, 将报错,请先检查路径! {"未指定的错误\r\n\r\n使用了元素 {http://schemas.microsoft.com/windows/2005/02/color/WcsCommonProfileTypes}Text,但未在 DTD/架构中…

开源软件的崛起:技术共享与协作创新的新时代 - 详解

开源软件的崛起:技术共享与协作创新的新时代 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&quo…

从 LLM 到 Agentic AI:构建下一代智能平台的全栈路径

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

11.16组会

R语言 应用:统计分析、绘图功能、扩展生态 列表、数组、矩阵、数据框 平均值、最大最小、中位数等 作各种图,质量好

20232418 2025-2026-1 《网络与系统攻防技术》实验五实验报告

20232418 2025-2026-1 《网络与系统攻防技术》实验五实验报告 1.实验内容 1.1通过whois、dig、nslookup等命令查询DNS域名、IP地址的详细信息。 1.2通过资源监视器和在线工具得到IP地址并确定其地理位置。 1.3nmap开源…

Claude Code教程:从零构建AutoPost GPT自动内容生成系统

Claude Code教程:从零构建AutoPost GPT自动内容生成系统AutoPost GPT是一个非常适合Claude Code 的全流程实战项目,它涵盖了从架构设计到模块拆分、从 API 调用到任务调度、从内容生成到自动发布的完整工程链路。如果…