springcloud-gateway集成knife4j
创始人
2024-06-02 08:03:50
0

springcloud-gateway集成knife4j

  • springcloud-gateway集成knife4j
    • 环境信息
    • 准备工作
    • 微服务集成knife4j
      • 第一步:编写Knife4jApiInfoProperties
      • 第二步:编写配置类Knife4jConfig
      • 第三步:放行相关静态资源
    • 网关集成knife4j
      • 编写配置类Knife4jGatewayConfig
    • 测试验证
    • 相关资料

环境信息

  • spring-boot:2.6.3
  • spring-cloud-alibaba:2021.0.1.0
  • knife4j-openapi2-spring-boot-starter:4.0.0

准备工作

各微服务&网关引入依赖

com.github.xiaoyminknife4j-openapi2-spring-boot-starter4.0.0

微服务集成knife4j

第一步:编写Knife4jApiInfoProperties

import com.ideaaedi.springcloud.jd.commonspring.config.Knife4jConfig;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** api 基础信息配置。更多配置信息项见{@link Knife4jConfig}** @author JustryDeng * @since 2021.0.1.D*/
@Data
@Component
public class Knife4jApiInfoProperties {/*** 要扫描api的base包*/@Value("${api-info.base-package:com}")private String basePackage;/*** 是否启用登录认证*/@Value("${api-info.enable-security:true}")private boolean enableSecurity;/*** 文档标题*/@Value("${api-info.title:}")private String title;/*** 文档描述*/@Value("${api-info.description:api info}")private String description;/*** 文档版本*/@Value("${api-info.version:1.0.0}")private String version;/*** 联系人姓名*/@Value("${api-info.contact-name:JustryDeng}")private String contactName;/*** 联系人网址*/@Value("${api-info.contact-url:https://gitee.com/JustryDeng/projects}")private String contactUrl;/*** 联系人邮箱*/@Value("${api-info.contact-email:13548417409@163.com}")private String contactEmail;
}

第二步:编写配置类Knife4jConfig

import com.ideaaedi.springcloud.jd.commonds.constant.BaseConstant;
import com.ideaaedi.springcloud.jd.commonspring.config.properties.Knife4jApiInfoProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;import java.util.ArrayList;
import java.util.List;/*** knife4j配置类** @author JustryDeng * @since 2021.0.1.D*/
@Slf4j
@Configuration
public class Knife4jConfig implements WebMvcConfigurer {/** 文档相关资源的链接(需保证这些资源不需要鉴权即可访问) */public static String[] RESOURCE_URLS = new String[]{"/webjars/**", "/swagger**", "/v2/api-docs", "/doc.html"};@Value("${spring.application.name:}")private String applicationName;@Beanpublic Docket docket(Knife4jApiInfoProperties knife4jApiInfoProperties) {String apiBasePackage = knife4jApiInfoProperties.getBasePackage();Docket docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo(knife4jApiInfoProperties)).select().apis(RequestHandlerSelectors.basePackage(apiBasePackage)).paths(PathSelectors.any()).build();if (knife4jApiInfoProperties.isEnableSecurity()) {docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());}return docket;}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");}private ApiInfo apiInfo(Knife4jApiInfoProperties knife4jApiInfoProperties) {return new ApiInfoBuilder().title(knife4jApiInfoProperties.getTitle()).description(knife4jApiInfoProperties.getDescription()).termsOfServiceUrl(StringUtils.isBlank(applicationName) ? "" : "/" + applicationName).contact(new Contact(knife4jApiInfoProperties.getContactName(), knife4jApiInfoProperties.getContactUrl(), knife4jApiInfoProperties.getContactEmail())).version(knife4jApiInfoProperties.getVersion()).build();}private List securitySchemes() {// 设置请求头信息List result = new ArrayList<>();// 第一个参数,自定义即可。 如:BaseConstant.JWT_TOKEN_KEY=Auth-Token,然后在代码里request.getHeader(BaseConstant.JWT_TOKEN_KEY)取值ApiKey apiKey = new ApiKey(BaseConstant.JWT_TOKEN_KEY, "Authorization", "header");result.add(apiKey);return result;}private List securityContexts() {// 设置需要登录认证的路径List result = new ArrayList<>();result.add(getContextByPath("/*/.*"));return result;}private SecurityContext getContextByPath(String pathRegex) {return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.regex(pathRegex)).build();}private List defaultAuth() {List result = new ArrayList<>();AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];authorizationScopes[0] = authorizationScope;result.add(new SecurityReference("Authorization", authorizationScopes));return result;}}

第三步:放行相关静态资源

对于管控了权限的应用,应放行以下资源

# 需要放行的资源已经定义进上面编写的Knife4jConfig中
public static String[] RESOURCE_URLS = new String[]{"/webjars/**", "/swagger**", "/v2/api-docs", "/doc.html"};

网关集成knife4j

编写配置类Knife4jGatewayConfig

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;/*** 网关knife4j配置** @author JustryDeng * @since 2021.0.1.D*/
@RestController
public class Knife4jGatewayConfig {private final SecurityConfiguration securityConfiguration;private final UiConfiguration uiConfiguration;private final SwaggerResourceAdapter swaggerResourceAdapter;public Knife4jGatewayConfig(@Autowired(required = false) SecurityConfiguration securityConfiguration,@Autowired(required = false) UiConfiguration uiConfiguration,SwaggerResourceAdapter swaggerResourceAdapter) {this.securityConfiguration = securityConfiguration;this.uiConfiguration = uiConfiguration;this.swaggerResourceAdapter = swaggerResourceAdapter;}/*** 安全配置*/@GetMapping("/swagger-resources/configuration/security")public Mono> securityConfiguration() {return Mono.just(new ResponseEntity<>(Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));}/*** ui配置*/@GetMapping("/swagger-resources/configuration/ui")public Mono> uiConfiguration() {return Mono.just(new ResponseEntity<>(Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));}/*** 资源配置,自动路由到微服务中的各个服务的api-docs信息*/@GetMapping("/swagger-resources")public Mono>> swaggerResources() {return Mono.just(new ResponseEntity<>(swaggerResourceAdapter.get(), HttpStatus.OK));}/*** favicon.ico*/@GetMapping("/favicon.ico")public Mono> favicon() {return Mono.just(new ResponseEntity<>(null, HttpStatus.OK));}/*** swagger资源适配器** @author JustryDeng * @since 2021.0.1.D*/@Slf4j@Componentpublic static class SwaggerResourceAdapter implements SwaggerResourcesProvider {/*** spring-cloud-gateway是否开启了根据服务发现自动为服务创建router*/@Value("${spring.cloud.gateway.discovery.locator.enabled:false}")private boolean autoCreateRouter;@Value("${spring.application.name:}")private String applicationName;@Resourceprivate RouteLocator routeLocator;@Resourceprivate GatewayProperties gatewayProperties;/*** 根据当前所有的微服务路由信息,创建对应的SwaggerResource*/@Overridepublic List get() {List finalResources;Set routes = new LinkedHashSet<>(16);// 获取所有路由的idrouteLocator.getRoutes().subscribe(route -> {String routeId = route.getId();routeId = routeId.replace("ReactiveCompositeDiscoveryClient_", "");routes.add(routeId);});// 没有开启自动创建路由,那么走配置文件中配置的路由if (!autoCreateRouter) {finalResources = new ArrayList<>(16);gatewayProperties.getRoutes().stream()// 过滤出配置文件中定义的路由.filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {route.getPredicates().stream()// 过滤出设置有Path Predicate的路由.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))// 根据路径拼接成api-docs路径,生成SwaggerResource.forEach(predicateDefinition -> finalResources.add(swaggerResource(route.getId(),predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("**", "v2/api-docs"))));});} else {finalResources = routes.stream().map(routeId -> swaggerResource(routeId, routeId + "/v2/api-docs")).collect(Collectors.toList());}List resources = new ArrayList<>(finalResources);// resources过滤掉网关的SwaggerResource, 我们一般也不会在网关中编写业务controllerif (StringUtils.isNotBlank(applicationName)) {resources = resources.stream().filter(x -> !applicationName.equalsIgnoreCase(x.getName())).collect(Collectors.toList());}// 排序resources.sort(Comparator.comparing(x -> x.getName().length()));return resources;}/*** 创建swagger资源** @param name*            swagger资源名(注:一般对应 {路由id})* @param location*            swagger资源路径(注:一般对应 {路由id}/v2/api-docs)* @return  swager资源*/private SwaggerResource swaggerResource(String name, String location) {log.info("name:{},location:{}", name, location);SwaggerResource swaggerResource = new SwaggerResource();swaggerResource.setName(name);swaggerResource.setLocation(location);swaggerResource.setSwaggerVersion("2.0");return swaggerResource;}}
}

测试验证

启动微服务后,访问{网关}/doc.html完成验证

在这里插入图片描述

相关资料

  • springboot2集成knife4j
  • 在微服务项目中引入 knife4j
  • 本文已被收录进《程序员成长笔记》 ,笔者JustryDeng

相关内容

热门资讯

火烧云小学作文【最新6篇】 火烧云小学作文篇一:火烧云的美丽火烧云是一种非常美丽的自然景观,它们在天空中绽放出绚丽的色彩,给人们...
人间自有温情在作文(精简3篇... 人间自有温情在作文 篇一温情的力量人间自有温情,在日常生活中,我们时常能够感受到这种力量。温情是指人...
小学三年级抗疫情作文(精简5... 小学三年级抗疫情作文 篇一:我们一起抗疫,共克时艰新冠疫情的突然爆发给全世界带来了巨大的挑战,人们的...
有你真好的作文(通用6篇) 有你真好的作文 篇一有你真好每个人的生活中都会有那么一个重要的人,他们的存在让我们的生活变得更加美好...
我上学了(优选3篇) 我上学了 篇一我上学了已经有好几年了,回想起来,这段时光仿佛就在眨眼间过去了。刚开始上学的时候,我还...
有趣的游戏小学作文400字【... 有趣的游戏小学作文400字 篇一标题:童年乐园——躲猫猫游戏在我心中,有一个让我欢乐无比的游戏,那就...
那次玩得真高兴作文(优秀5篇... 那次玩得真高兴作文 篇一那次玩得真高兴上个周末,我和我的朋友们一起去了游乐园玩,那次真是玩得太高兴了...
初一七年级学生作文题目【优选... 初一七年级学生作文题目 篇一我的暑假计划暑假即将来临,我对未来的两个月充满了期待和计划。今年的暑假,...
假如考上了名校,我要回来看看... 假如考上了名校,我要回来看看我的母校小学作文 篇一当我考上了名校,我内心感到无比的兴奋和自豪。这是我...
校园桂花香小学作文【最新3篇... 校园桂花香小学作文 篇一校园桂花香小学作文我所在的学校是一所名为桂花香小学的学校,这个名字来源于学校...
春天小学一年级作文300字(... 春天小学一年级作文300字 篇一春天的花儿春天是一个美丽的季节,大地万物都在春天苏醒,充满了生机和活...
小学中秋节的作文【优选3篇】 小学中秋节的作文 篇一中秋节是中国传统的节日之一,也是我最喜欢的节日。在这一天,我和家人一起庆祝,品...
梦里清江小学作文【优质3篇】 梦里清江小学作文 篇一我爱梦里清江小学梦里清江小学是我上学的地方,它位于美丽的梦里清江边。每当我踏进...
难忘的秋游小学作文450字(... 难忘的秋游小学作文450字 篇一难忘的秋游今天,我们全班去郊游了,这是我度过的一个最难忘的秋天。我们...
可怜的骆驼作文(优质3篇) 可怜的骆驼作文 篇一骆驼是一种生活在沙漠中的动物,它们背上长着一座座驼峰,可以存储大量的水分,帮助它...
倔老头作文【优选3篇】 倔老头作文 篇一倔老头的坚持倔老头是我家附近的一位老先生,他已经七十多岁了,但他的身体和精神状态一直...
小学生一年级的作文400字(... 小学生一年级的作文400字 篇一我的暑假计划暑假就要来了,我有很多计划,让我来给大家说说吧!首先,我...
师爱温暖我的心小学作文【优秀... 师爱温暖我的心小学作文 篇一师爱温暖我的心作为一名小学生,我有幸遇到了一位非常特别的老师,她就是我的...
可爱的金毛小学作文【优秀3篇... 可爱的金毛小学作文 篇一我家的金毛小狗我家有一只可爱的金毛小狗,它的名字叫做小黄。小黄是一只非常聪明...
四年级上册三单元作文【优秀3... 四年级上册三单元作文 篇一我的家乡我是一个四年级的学生,我想给大家介绍一下我的家乡。我的家乡是一个美...