前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在微服务项目中引入 knife4j

在微服务项目中引入 knife4j

原创
作者头像
Bess Croft
修改2021-11-17 10:56:59
2K0
修改2021-11-17 10:56:59
举报
文章被收录于专栏:贝丝的专栏贝丝的专栏

什么是 Knife4j ?

knife4j 是为 Java MVC 框架集成 Swagger 生成 Api 文档的增强解决方案。说白了,如果项目开发为前后端分离开发的话,这个插件就非常的省事儿,不用再很麻烦的写接口文档了。之前用过 Swagger 来生成文档,但是在某些情况下,Swagger 却并不适合国内的项目,尤其是验收文档。给甲方的验收文档往往包含接口文档,这是 knife4j 的导出就派上用场了。

没有好不好用,只有适不适合!

引入项目

说明

本文章以我自己的开源项目 aurora-mall 为例,详细讲述在 Spring Cloud 2020 & Alibaba 2021 中,应该如何引用。为什么必须加上这个说明呢?因为不同的项目,引入方式还是有一些差别的。

开始

导包
  • 在项目的根 pom.xml 文件中导包。
<properties>
    <knife4j.version>2.0.4</knife4j.version>
</properties>

<dependencyManagement>
    <dependencies>
		<dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-micro-spring-boot-starter</artifactId>
            <version>${knife4j.version}</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>${knife4j.version}</version>
        </dependency>
	</dependencies>
</dependencyManagement>
  • 在网关服务的 pom.xml 中,导包
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
  • 在每一个需要生成文档的服务的 pom.xml 中,导包(一般放在 common 中,然后不需要屏蔽即可)
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-micro-spring-boot-starter</artifactId>
</dependency>

gateway 服务模块的接口,需要多一个包,用来输出文档到前端(包含UI包)它会把我们所有的微服务都聚合到一个文档,统一输出到前端,其它服务只需控制数据,这样便于节约资源!

生产环境屏蔽

目前 Springfox-Swagger 以及 Knife4j 提供的资源接口包括如下:如果你要用的话,记得白名单放行

资源

说明

/doc.html

Knife4j提供的文档访问地址

/v2/api-docs-ext

Knife4j提供的增强接口地址,自2.0.6 版本后删除

/swagger-resources

Springfox-Swagger提供的分组接口

/v2/api-docs

Springfox-Swagger提供的分组实例详情接口

/swagger-ui.html

Springfox-Swagger提供的文档访问地址

/swagger-resources/configuration/ui

Springfox-Swagger提供

/swagger-resources/configuration/security

Springfox-Swagger提供

当我们部署系统到生产系统,为了接口安全,需要屏蔽所有 Swagger 的相关资源

如果使用 SpringBoot 框架,只需在 application.properties 或者 application.yml 配置文件中配置

knife4j:
  # 开启增强配置 
  enable: true
 # 开启生产环境屏蔽
  production: true
Java 化配置
  • 配置基类

咱们一般会放在 common 包里面。

package com.besscroft.aurora.mall.common.config;

import com.besscroft.aurora.mall.common.domain.SwaggerProperties;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
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;

/**
 * swagger 通用配置
 *
 * @Author Bess Croft
 * @Date 2021/4/2 12:28
 */
public abstract class BaseSwaggerConfig {

    @Bean
    public Docket createRestApi() {
        SwaggerProperties swaggerProperties = swaggerProperties();
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo(swaggerProperties))
                .select()
                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getApiBasePackage()))
                .paths(PathSelectors.any())
                .build();
        if (swaggerProperties.isEnableSecurity()) {
            docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());
        }
        return docket;
    }

    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
                .version(swaggerProperties.getVersion())
                .build();
    }

    private List<ApiKey> securitySchemes() {
        // 设置请求头信息
        List<ApiKey> result = new ArrayList<>();
        ApiKey apiKey = new ApiKey("Authorization", "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;
    }

    /**
     * 自定义Swagger配置
     */
    public abstract SwaggerProperties swaggerProperties();

}
  • 新建 SwaggerProperties 类
@Data
@Builder
@EqualsAndHashCode(callSuper = false)
public class SwaggerProperties {

    /**
     * API文档生成基础路径
     */
    private String apiBasePackage;

    /**
     * 是否要启用登录认证
     */
    private boolean enableSecurity;

    /**
     * 文档标题
     */
    private String title;

    /**
     * 文档描述
     */
    private String description;

    /**
     * 文档版本
     */
    private String version;

    /**
     * 文档联系人姓名
     */
    private String contactName;

    /**
     * 文档联系人网址
     */
    private String contactUrl;

    /**
     * 文档联系人邮箱
     */
    private String contactEmail;

    /**
     * 版权信息
     */
    private String license;

    /**
     * 版权协议地址
     */
    private String licenseUrl;

}
  • 在网关服务新增 Swagger 资源配置
package com.besscroft.aurora.mall.gateway.config;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.ArrayList;
import java.util.List;

/**
 * swagger资源配置
 *
 * @Author Bess Croft
 * @Date 2021/4/2 12:37
 */
@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {

    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;

    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        //获取所有路由的ID
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        //过滤出配置文件中定义的路由->过滤出Path Route Predicate->根据路径拼接成api-docs路径->生成SwaggerResource
        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
            route.getPredicates().stream()
                    .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                    .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
                            predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
                                    .replace("**", "v2/api-docs"))));
        });

        return resources;
    }

    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;
    }

}
  • 配置网关的 Swagger 处理器,自定义 Swagger 的各个配置节点
package com.besscroft.aurora.mall.gateway.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;

import java.util.Optional;

/**
 * 自定义Swagger的各个配置节点
 *
 * @Author Bess Croft
 * @Date 2021/3/30 21:35
 */
@RestController
public class SwaggerHandler {

    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;

    @Autowired(required = false)
    private UiConfiguration uiConfiguration;

    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }

    /**
     * Swagger安全配置,支持oauth和apiKey设置
     */
    @GetMapping("/swagger-resources/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    /**
     * Swagger 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));
    }

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

}
  • 在每个业务系统服务配置 Swagger
package com.besscroft.aurora.mall.admin.config;

import com.besscroft.aurora.mall.common.config.BaseSwaggerConfig;
import com.besscroft.aurora.mall.common.domain.SwaggerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * swagger配置
 *
 * @Author Bess Croft
 * @Date 2021/3/30 13:43
 */
@Configuration
@EnableSwagger2
@Profile(value = {"dev"})
public class SwaggerConfiguration extends BaseSwaggerConfig {

    @Override
    public SwaggerProperties swaggerProperties() {
        return SwaggerProperties.builder()
                .apiBasePackage("com.besscroft.aurora.mall.admin.controller")
                .title("极光商城开发文档")
                .description("后台相关接口文档")
                .contactName("Bess Croft")
                .contactEmail("besscroft@foxmail.com")
                .contactUrl("https://github.com/besscroft/aurora-mall")
                .license("Open Source")
                .licenseUrl("https://github.com/besscroft/aurora-mall/blob/main/LICENSE")
                .version("0.1.0")
                .enableSecurity(true)
                .build();
    }

}

这里注意2点:

  • @Profile 注解可以规定在哪个环境下生效,咱们只控制开发环境就ok了。
  • apiBasePackage 设置生成的接口在哪个包里面。

同时,如果网关配置了白名单机制,记得放行 /v2/api-docs 地址!因为这里咱没做认证功能!(对于线上生产环境来说,白名单可以更好地控制访问权限,能一定程度保证安全性!)

启动

然后咱们启动项目,来查看是否配置成功!

访问分组接口地址:

http://localhost:8000/swagger-resources
[
    {
        "name": "mall-auth",
        "url": "/mall-auth/v2/api-docs",
        "swaggerVersion": "2.0",
        "location": "/mall-auth/v2/api-docs"
    },
    {
        "name": "mall-admin",
        "url": "/mall-admin/v2/api-docs",
        "swaggerVersion": "2.0",
        "location": "/mall-admin/v2/api-docs"
    },
    {
        "name": "mall-elasticsearch",
        "url": "/mall-elasticsearch/v2/api-docs",
        "swaggerVersion": "2.0",
        "location": "/mall-elasticsearch/v2/api-docs"
    },
    {
        "name": "mall-log",
        "url": "/mall-log/v2/api-docs",
        "swaggerVersion": "2.0",
        "location": "/mall-log/v2/api-docs"
    }
]

有正常值,表示接口返回正常!

  • 访问前端地址:
http://localhost:8000/doc.html#/home

注意,实例中的 8000,指的是网关的端口号!

使用注解

实际上就是 Swagger 的注解,比如说:

  • 接口类上面可以加 @Api(tags = "主页接口")
  • 接口方法上可以加 @ApiOperation(value = "首页排行接口")
  • 实体类上可以加 @ApiModel(value = "登录对象")
  • 实体类字段上可以加 @ApiModelProperty(value = "用户名", dataType = "String")
  • 当然,不加也可以默认扫描读取!
白名单

最后,放上一些可能需要放行的白名单地址:

"/doc.html"
"/v2/api-docs-ext"
"/swagger-resources"
"/v2/api-docs"
"/swagger-ui.html"
"/swagger-resources/configuration/ui"
"/swagger-resources/configuration/security"

最后

我觉得 knife4j 最大的好处就是,可以导出离线文档,有:Markdown、Word、Html、OpenAPI 格式!如果它正好命中你的需求,不妨试试引入到项目中呢!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 Knife4j ?
  • 引入项目
    • 说明
      • 开始
        • 导包
        • 生产环境屏蔽
        • Java 化配置
        • 启动
        • 使用注解
        • 白名单
      • 最后
      相关产品与服务
      API 网关
      腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档