软件编程
位置:首页>> 软件编程>> java编程>> springcloud-gateway集成knife4j的示例详解

springcloud-gateway集成knife4j的示例详解

作者:justry_deng  发布时间:2023-11-29 08:56:44 

标签:springcloud,gateway,knife4j

springcloud-gateway集成knife4j

环境信息

环境信息

  • spring-boot:2.6.3

  • spring-cloud-alibaba:2021.0.1.0

  • knife4j-openapi2-spring-boot-starter:4.0.0

准备工作

各微服务&网关引入依赖

<dependency>
   <groupId>com.github.xiaoymin</groupId>
   <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
   <version>4.0.0</version>
</dependency>

微服务集成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 <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
* src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
* @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 <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
* src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
* @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;

@Bean
   public 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;
   }

@Override
   public 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<SecurityScheme> securitySchemes() {
       // 设置请求头信息
       List<SecurityScheme> 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<SecurityContext> securityContexts() {
       // 设置需要登录认证的路径
       List<SecurityContext> 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<SecurityReference> defaultAuth() {
       List<SecurityReference> 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 <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
* @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<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
       return Mono.just(new ResponseEntity<>(
               Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
   }

/**
    * ui配置
    */
   @GetMapping("/swagger-resources/configuration/ui")
   public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
       return Mono.just(new ResponseEntity<>(
               Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
   }

/**
    * 资源配置,自动路由到微服务中的各个服务的api-docs信息
    */
   @GetMapping("/swagger-resources")
   public Mono<ResponseEntity<List<SwaggerResource>>> swaggerResources() {
       return Mono.just(new ResponseEntity<>(swaggerResourceAdapter.get(), HttpStatus.OK));
   }

/**
    * favicon.ico
    */
   @GetMapping("/favicon.ico")
   public Mono<ResponseEntity<?>> favicon() {
       return Mono.just(new ResponseEntity<>(null, HttpStatus.OK));
   }

/**
    * swagger资源适配器
    *
    * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
    * @since 2021.0.1.D
    */
   @Slf4j
   @Component
   public 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;

@Resource
       private RouteLocator routeLocator;

@Resource
       private GatewayProperties gatewayProperties;

/**
        * 根据当前所有的微服务路由信息,创建对应的SwaggerResource
        */
       @Override
       public List<SwaggerResource> get() {
           List<SwaggerResource> finalResources;
           Set<String> routes = new LinkedHashSet<>(16);
           // 获取所有路由的id
           routeLocator.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<SwaggerResource> resources = new ArrayList<>(finalResources);
           // resources过滤掉网关的SwaggerResource, 我们一般也不会在网关中编写业务controller
           if (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完成验证

springcloud-gateway集成knife4j的示例详解

相关资料

springboot2集成knife4j

在微服务项目中引入 knife4j

来源:https://blog.csdn.net/justry_deng/article/details/129474480

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com