SpringDoc的使用

2,862 阅读19分钟

Swagger 官网地址:swagger.io/

SpringDoc官网地址:springdoc.org/

1. Swagger发展史

Swagger最初由Tony Tam在2011年创建,并在之后被SmartBear Software公司收购。Swagger的发展史大概可以分为以下三个阶段:

  • ①:Swagger 1.x 阶段(2011-2014年) Swagger最初是一个简单的API文档生成工具,通过对JAX-RS和Jersey注解的支持自动生成API文档,使得API文档的维护变得更加容易。 在这个阶段,Swagger还没有完全成熟,只能支持基本的API描述和文档生成。

  • ②:Swagger 2.x 阶段(2014-2017年) 在Swagger 2.x阶段,Swagger发生了重大变化。它不再仅仅是一个文档生成工具,而是一个完整的API开发和管理平台。Swagger 2.x加 入了强大的注解支持,可以描述API的细节信息,如请求参数、返回类型等,以及定义RESTful API的元数据,如API描述、标签等。 此外,Swagger 2.x还引入了OpenAPI规范,在API定义方面有了更严格的标准和规则。

  • ③:OpenAPI 阶段(2017-至今)(也被称为Swagger 3.x)。在2017年,Swagger 2.x的规范成为了Linux基金会旗下的OpenAPI规范。这标志着Swagger从一款工具演变成为了一个开放且标准的API 定义框架。OpenAPI规范不仅继承了Swagger 2.x的特性,还提供了更加全面和严格的API定义规范,并且扩展了对非RESTful API的支持。随着OpenAPI规范的普及,越来越多的API开发者开始使用Swagger/OpenAPI来开发、测试和文档化他们的RESTful API。

随着Linux基金会旗下的OpenAPI 收购了Swagger2.x后对其进行了更严格的规范,又进行了各种优化,所以我们也可以称OpenAPI是一个全新的Swagger3.x,因为OpenAPI对其作了更多的优化和规范。

除了上述几个主要阶段之外,还有一些其他重要的事件和版本,如Swagger UI、Swagger Codegen、SwaggerHub等等。 这些工具和服务进一步扩展了Swagger的功能,使其成为了一个更加完整、强大和易于使用的API定义和管理平台。

2. OpenAPI 规范

OpenAPI规范(简称OAS)最初由开发Swagger的团队在2010年推出,从Swagger 2.0开始,Swagger规范被正式更名为OpenAPI规范,并得到了许多社区的支持和贡献。

OpenAPI规范(也称为 Swagger 3.x 规范) 是一种用于描述RESTful API的标准化格式,它定义了如何描述API的基本信息、结构、参数、响应等方面的规范。

OpenAPI规范以机器可读的方式定义了RESTful API的结构和特征,支持自动生成文档、客户端与服务端代码、Mock Server和测试工具等。

OpenAPI规范采用JSON或YAML格式编写,并支持多种数据类型,可以描述API的基本信息、路径、HTTP方法、参数、响应等各种细节。

通过遵循OpenAPI规范,开发者可以快速定义和构建RESTful API,并且可以生成相应的文档和代码来帮助他们更快地开发与测试API。同时,OpenAPI规范还可以促进不同系统之间的交互和集成,因为根据规范定义的API可以被多个客户端程序和服务端程序所理解和使用。

3. Spring 生态的API文档工具

3.1 SpringFox工具

Springfox是一套可以帮助Java开发者自动生成API文档的工具,它是基于Swagger 2.x基础上开发的。 Springfox是一个用于集成Swagger2.x到Spring应用程序中的库。Springfox 提供了一些注解来描述API接口、参数和返回值,并根据这些信息生成Swagger UI界面 ,从而方便其他开发人员查看和使用您的API接口。

Springfox还支持自动生成API文档和代码片段,简化了开发人员的工作量。除了集成Swagger 2.x,Springfox还提供了一些额外功能,例如自定义Swagger文档、API版本控制、请求验证等等

但随着Swagger2.x终究成为历史,springfox-boot-starter的坐标从3.0.0版本(2020年7月14日)开始就一直没有更新;所以不再推荐使用。

3.2 SpringDoc工具

SpringDoc对应坐标是springdoc-openapi-ui,它是一个集成Swagger UI和ReDoc的接口文档生成工具,在使用上与springfox-boot-starter类似,但提供了更为灵活、功能更加强大的工具。

SpringDoc支持全新的OpenAPI 3.0规范,可以生成Swagger UI风格的接口文档,还提供了ReDoc的文档渲染方式,可以自动注入OpenAPI规范的JSON描述文件,支持OAuth2、JWT等认证机制。

SpringDoc是基于OpenAPI 3.0规范构建的,因此推荐在Spring Boot 2.4及以上版本中使用。

因为SpringDoc有更先进的技术架构和更好的扩展性,使其基本取代了springfox-boot-starter工具包,成为了当前Spring Boot生态中最受欢迎的API文档工具之一。

4. SpringBoot 中集成 SpringDoc

4.1 Springdoc OpenAPI的依赖项

引入springdoc openapi依赖时,需要先确认一下SpringBoot所使用的Web框架:

  • Spring MVC ,基于 Servlet 的框架,是 Spring Framework 的一部分,用于构建 web 应用程序。
  • Spring WebFlux ,是 Spring 5 引入的一个新模块,用于构建响应式 web 应用程序。

根据项目所使用的Web框架,选择相应的starter依赖项:

  • Spring MVC:使用 springdoc-openapi-starter-webmvc-ui
  • Spring WebFlux:使用springdoc-openapi-starter-webflux-ui
  • 如需要同时支持 Spring MVC 和 Spring WebFlux:则使用 springdoc-openapi-starter-ui

以下是被starter所包含的依赖项,不需要直接引入:

  • springdoc-openapi-common:提供了 Springdoc OpenAPI 的一些通用功能。
  • springdoc-openapi-core:是 Springdoc OpenAPI 的核心,提供生成 OpenAPI 文档的核心功能。
  • springdoc-openapi-ui:提供了一个 web 界面,用于查看和测试 OpenAPI 文档。

如果 API 使用了安全机制(如 OAuth2 或 JWT),则需要引入单独的 springdoc-openapi-security依赖项,它提供了与安全相关的功能,例如处理 OpenAPI 的 securityScheme 和 securityRequirement。

项目中最常用的,是springdoc-openapi-starter-webmvc-ui

org.springdoc springdoc-openapi-starter-webmvc-ui 2.5.0

⚠️注意:

当引入Mockserver时,其内置的swagger模块会和swagger3产生版本冲突,导致报以下错误:

Failed to instantiate [org.springdoc.core.converters.ModelConverterRegistrar]: 
Factory method 'modelConverterRegistrar' threw exception with message: 
'io.swagger.v3.core.converter.ModelConverters 
io.swagger.v3.core.converter.ModelConverters.getInstance(boolean)'

解决方法是将MockServer中的swagger模块排除掉:

<dependency>
    <groupId>org.mock-server</groupId>
    <artifactId>mockserver-netty</artifactId>
    <version>${mockserver.version}</version>
    <exclusions>
        <exclusion>
            <groupId>io.swagger.parser.v3</groupId>
            <artifactId>swagger-parser</artifactId>
        </exclusion>
        <exclusion>
            <groupId>io.swagger.parser.v3</groupId>
            <artifactId>swagger-models</artifactId>
        </exclusion>
    </exclusions>
</dependency>

4.2 配置OpenAPI

OpenAPI有两种配置方式:

  • 注解的配置方式,对应的包是:io.swagger.v3.oas.annotations
  • 类对象的配置方式,对应的包是:io.swagger.v3.oas.models

4.2.1 注解配置方式

@SpringBootConfiguration
@OpenAPIDefinition(
    // API的基本信息,包括标题、版本号、描述、联系人等
    info = @Info(
        title = "Swagger3.0 (Open API) 示例文档",      // Api接口文档标题(必填)
        description = "Swagger框架测试接口",            // Api接口文档描述
        version = "1.0.0",                            // Api接口版本
        termsOfService = "https://example.com/",      // Api接口的服务条款地址
        contact = @Contact(
            name = "FirstName LastName",              // 作者姓名
            email = "contact@example.com",            // 作者邮箱
            url = "https://url/"                      // 作者的URL地址
        ),
        license = @License(                           // 设置Lisence信息
            name = "Apache 2.0",                      
            url = "https://www.apache.org/licenses/LICENSE-2.0.html" 
        )
    ),
    security = @SecurityRequirement(name = "JWT-test"),
    
    // 服务器地址列表,用于在页面上执行测试时切换
    servers = {
        @Server(url = "http://devIp:port", description = "测试环境服务器"),
        @Server(url = "http://prodIp:port", description = "生产环境服务器"),
    },
    externalDocs = @ExternalDocumentation(description = "更多内容", url = "xxx"))
@SecurityScheme(
    name = "JWT-test",                   // 认证方案名称
    type = SecuritySchemeType.HTTP,      // 认证类型,当前为http认证
    description = "这是一个认证的描述详细",  // 描述信息
    in = SecuritySchemeIn.HEADER,        // 代表在http请求头部
    scheme = "bearer",                   // 认证方案,如:Authorization: bearer token信息
    bearerFormat = "JWT")                // 表示使用 JWT 格式作为 Bearer Token 的格式
@SecurityScheme(
    name = "X-API-KEY",
    type = SecuritySchemeType.APIKEY,
    description = "这是一个认证的描述详细",
    in = SecuritySchemeIn.HEADER,
    scheme = "bearer")
public class SwaggerOpenApiConfig {}

4.2.2 类对象配置方式

类对象的配置方式是创建一个OpenAPI的Bean对象。

OpenAPI对象用于描述整个API的结构和元数据。可以理解为一个API文档对象,其中包含了许多属性元素,

包括:

  • ①:openapi属性:表示使用的 OpenAPI 规范版本(例如 3.0.1)。
  • ②:info属性:表示API的基本信息,包括标题、版本号、描述、联系人等。使用Info类来创建这个对象。
  • ③:servers属性: 表示服务器地址或者URL模板列表。每个URL模板可以包含占位符,这些占位符可以被路径参数或者查询参数替换。 使用Server类来创建这个对象。
  • ④:paths属性:表示API的所有路径和操作信息,使用PathItem类来描述每一个路径,使用Operation类来描述操作。
  • ⑤:components属性:表示API的组件信息,比如响应模板、请求模板和安全方案等。 使用Schema、Response、Parameter、SecurityScheme等类来创建这些对象。
  • ⑥:tags属性:表示API的标签信息,用于对相似的操作进行分组。

示例代码如下:

@Configuration
public class OpenAPIConfig {

    @Bean
    public OpenAPI myOpenAPI() {

        // 定义测试环境服务器URL
        String devUrl = "http://localhost:8999";
        Server devServer = new Server();
        devServer.setUrl(devUrl);
        devServer.setDescription("测试环境的服务器URL");

        // 定义生产环境服务器URL
        String prodUrl = "http://localhost:8999";
        Server prodServer = new Server();
        prodServer.setUrl(prodUrl);
        prodServer.setDescription("正式环境的服务器URL");

        // 定义联系人对象
        Contact contact = new Contact();
        contact.setEmail("contact@example.com");
        contact.setName("FirtName LastName");
        contact.setUrl("https://www.example.com");

        License mitLicense = new License().name("MIT License")
                .url("https://choosealicense.com/licenses/mit/");

        // 定义API信息对象
        Info info = new Info()
            .title("Spring Doc 演示 API")
            .version("1.0")
            .contact(contact)
            .description("对外API接口说明.")
            .termsOfService("https://www.sanfate.com/terms")
            .license(mitLicense);

        // 返回OpenAPI对象,包含API信息和服务器信息
        return new OpenAPI().info(info)
                            .servers(List.of(devServer, prodServer));    
    }
    
}

5. SpringDoc扫描的内容

SpringDoc扫描项目源码中的以下内容:

  • Controller 和接口方法:扫描所有标记为 @RestController 或 @Controller 的类。
  • DTO/POJO 类(模型):扫描作为请求和响应对象的自定义类(DTO/POJO),并生成相应的 Schema。 SpringDoc的页面中,只显示这两种内容:接口,Schema,如下图所示:

image.png

注意⚠️:

如果源码中接口的注解方式,使用的注解为 @ReqeustMapping ,而不是具体的 @GetMapping,@PostMapping等,由于SpirngDoc无法获知具体的接口类型,就会为其生成全部七个类型的RESTFUL接口文档:GET, POST, PUT, PATCH, DELETE, HEAD, OPTION,再加上多个接口,会使得页面非常杂乱。因此建议要明确指定接口的类型,不要使用通用的@ReqeustMapping。

6. SpringDoc的注解

SpringDoc注解的核心目的,是为了获得更详细的接口或Schema的描述信息。

各种注解的详细信息,参考官网说明:spec.openapis.org/oas/latest.…

6.1 @Tag 注解

@Tag 可以用于对接口进行分类和归类,便于开发人员组织和管理 API 文档。其属性包括:

  • ①:name:表示标签的名称,必填属性,如果多个Controller上的name写一样的,就会把它们归类在一起。
  • ②:description:表示标签的描述信息,非必填属性。
  • ③:externalDocs:用于指定URL地址文档信息来追加描述接口的信息。非必填属性。
@Tag(
    name = "StudentControllerAPI",
    description = "学生控制器接口",
    externalDocs = @ExternalDocumentation(
            description = "这是一个接口文档介绍",
            url = "https://www.cnblogs.com/antLaddie/")
)

6.2 @Operation注解

@Operation 注解用于对API操作(即方法)进行描述和标记。具体属性如下:

  • ①:summary:用于简要描述API接口的概要。
  • ②:description:用于详细描述API接口的描述信息。
  • ③:parameters:用于指定API接口的参数列表,包括路径参数、请求参数、请求头部等。可以使用@Parameter注解进一步定义参数。
  • ④:operationId:用于指定API接口的唯一标识符,可以用于生成客户端代码或文档等。 说明:第三方工具使用operationId来唯一标识此接口。
  • ⑤:requestBody:用于定义API操作的请求体,可以使用@RequestBody注解进一步定义请求体。 说明:这里的@RequestBody注解是@io.swagger.v3.oas.annotations.parameters.RequestBody包里的
  • ⑥:responses:用于定义 API接口的响应列表,包括成功响应和错误响应。可以使用@ApiResponse注解进一步定义响应。
  • ⑦:security:用于对API接口进行安全控制,可以使用@SecurityRequirement注解进一步定义安全需求
  • ⑧:deprecated:表示该API接口已经过时或不推荐使用。
@Operation(
    summary = "根据Id查询学生信息",
    description = "根据ID查询学生信息,并返回响应结果信息",
    parameters = {
        @Parameter(
            name = "id",
            description = "学生ID",
            required = true,
            example = "1")
    },
    responses = {
        @ApiResponse(
            responseCode = "200",
            description = "响应成功",
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(
                    title = "AjaxResul和StudentVO组合模型",
                    description = "返回实体,AjaxResult内data为StudentVO模型",
                    anyOf = {AjaxResult.class, StudentVO.class})
            )
        )
    }
 )

6.3 @Parameter注解

@Parameter用于描述HTTP请求的参数信息,它是一个Parameter[]类型的数组,每个元素表示一个请求参数

具体属性如下;

  • ①:name:参数名称。
  • ②:in:参数位置,可以是 query、header、path、cookie 等。
  • ③:description:参数描述。
  • ④:required:参数是否必须,默认为 false。
  • ⑤:deprecated:参数是否已过时,默认为 false。
  • ⑥:allowEmptyValue:是否允许空值,默认为false。
  • ⑦:style:参数的序列化风格,可以是 “matrix”、“label”、“form”、“simple”、 “spaceDelimited”、“pipeDelimited”、“deepObject”;
  • ⑧:explode:当参数值是对象或数组时,是否将其展开成多个参数,默认为 false。
  • ⑨:schema:参数类型和格式的定义,通常使用@Schema注解。(下面介绍)
  • ⑩:example:参数值的示例;

6.4 @Schema注解

@Schema 是用于描述数据模型的基本信息和属性

6.4.1 常规属性

  • ①:description:用于描述该类或属性的作用。
  • ②:name:指定属性名。该属性只对属性有效,对类无效。
  • ③:title:用于显示在生成的文档中的标题。
  • ④:requiredMode:用于指定该属性是否必填项。枚举Schema.RequiredMode内可选值如下:
    • 默认AUTO:可有可无;
    • REQUIRED:必须存在此字段(会加红色*);
    • NOT_REQUIRED:不需要存在此字段
  • ⑤:accessMode:用于指定该属性的访问方式。 包括AccessMode.READ_ONLY(只读)、AccessMode.WRITE_ONLY(只写)、AccessMode.READ_WRITE(读写)
  • ⑥:format:用于指定该属性的数据格式。例如:日期格式、时间格式、数字格式。
  • ⑦:example:为当前的属性创建一个示例的值,后期测试可以使用此值。
  • ⑧:deprecated:用于指定该属性是否为已过时的属性,默认为false。
  • ⑨:defaultValue:用于指定该属性的默认值。
  • ⑩:implementation:用于显示为该类或属性引入具体的实体路径,这代表当前指定的类或者属性将参考引入的实体。

6.4.2 type属性

type属性用于指定数据类型(Data Type)或者元素类型(Element Type) :

  • 基本类型:取值为相应的 Java 类型名,例如 int、long、float、double、boolean 等。
  • 包装类型:与基本类型相同,取值为相应的Java包装类型名,例如Integer、Long、Float、Double、Boolean等。
  • 字符串类型:取值为string。
  • 数组类型:取值为 array。对于数组类型,还可以使用 schema 属性指定其元素类型的 Schema 信息。
  • 对象类型:不用指定type,可以通过implementation属性引入。
  • 枚举类型:取值为enum。对于枚举类型,还需要使用enumAsRef属性指定是否将其定义为一个独立的引用类型。
  • 其它类型:不用指定type,可以通过implementation属性引入。

6.4.3 复杂类型的描述属性

@Schema注解提供了四个属性来描述复杂类型,分别是allOf、anyOf、oneOf和not。 这四个属性可以用于组合不同的JSON Schema以描述一个复杂类型,具体如下:

  • ①:allOf: 表示当前schema是多个其它schema的并集。 例如,如果一个Java类型同时实现了两个接口,那么可以使用allOf来表示这个Java类型继承了这两个接口的所有属性和方法。
  • ②:anyOf: 表示当前schema可以匹配其中任意一个schema,其本身也是一个组合体,可以嵌套使用。 例如,一个返回类型可能是多个Java类型中的任意一个,可以使用anyOf来描述这种情况。
  • ③:oneOf: 表示当前schema只能匹配其中一个schema,其本身也是一个组合体,可以嵌套使用。 例如,一个Java类型只能是多个子类型中的任意一个,可以使用oneOf来描述这种情况。
  • ④:not: 表示当前Schema不能匹配某个schema。 例如,一个Java类型不能是某个子类型,可以使用not来描述这种情况。

7. API接口注解示例

全局基本信息配置好了之后,就可以开始对API接口添加注解。

7.1 响应对象定义

@Data 
@AllArgsConstructor 
@Schema(description = “响应返回数据对象”) 
public class AjaxResult {
    @Schema(
        title \= "code",
        description \= "响应码",
        format \= "int32",
        requiredMode \= Schema.RequiredMode.REQUIRED)
    private Integer code;
    
    @Schema(
        title \= "msg",
        description \= "响应信息",
        accessMode \= Schema.AccessMode.READ_ONLY,
        example \= "成功或失败",
        requiredMode \= Schema.RequiredMode.REQUIRED)
    private String msg;

    @Schema(title \= "data", 
        description = "响应数据", 
        accessMode = Schema.AccessMode.READ_ONLY)
    private Object data;
}

7.2 实体定义

// 模型定义示例:
@Data
@AllArgsConstructor
@Schema(title = "学生模型VO", description = "响应视图学生模型VO")
public class StudentVO {
    @Schema(name = "学生ID", description = "学生ID属性", format = "int64", example = "1")
    private Long id;            // 学生ID
    @Schema(name = "学生姓名", description = "学生姓名属性", example = "jack")
    private String name;        // 学生姓名
    @Schema(name = "学生年龄", description = "学生年龄属性", format = "int32", example = "24")
    private Integer age;        // 学生年龄
    @Schema(name = "学生地址", description = "学生地址属性", example = "安徽合肥")
    private String address;     // 学生地址
    @Schema(name = "学生分数", description = "学生分数属性", format = "double", example = "55.50")
    private Double fraction;    // 学生分数
    @Schema(name = "学生爱好", description = "学生爱好属性(List类型)",
            type = "array", example = "[\"玩\", \"写字\"]")
    private List<String> likes; // 学生爱好
}

7.3 接口定义

@RestController
@RequestMapping("/student")
@Tag(
    name = "StudentControllerAPI",
    description = "学生控制器接口",
    externalDocs = @ExternalDocumentation(
        description = "这是一个接口文档介绍",
        url = "https://www.cnblogs.com/antLaddie/"))
public class StudentController {

    /***
     * 根据ID查询学生信息(单条)
     * @param id 学生id
     * @return 返回一条数据
     */
    @Operation(
        summary = "根据Id查询学生信息",
        description = "根据ID查询学生信息,并返回响应结果信息",
        parameters = {
            @Parameter(name = "id",
                 description = "学生ID", 
                 required = true, 
                 example = "1")},
        responses = {
            @ApiResponse(
                responseCode = "200",
                description = "响应成功",
                content = @Content(
                    mediaType = "application/json",
                    schema = @Schema(
                        title = "AjaxResul和StudentVO组合模型",
                        description = "返回实体,AjaxResult内data为StudentVO模型",
                        anyOf = {AjaxResult.class, StudentVO.class}))),
            @ApiResponse(
                responseCode = "500",
                description = "响应失败",
                content = @Content(
                    mediaType = "application/json",
                    schema = @Schema(
                        title = "AjaxResul模型",
                        description = "返回实体,AjaxResult内data为空",
                        implementation = AjaxResult.class)))
        })
    @GetMapping("/findOne/{id}")
    public AjaxResult findOneStudent(@PathVariable(value = "id") Long id) {
        //模拟学生数据
        List<String> likes = Arrays.asList("抓鱼", "爬山", "写字");
        StudentVO studentVO = new StudentVO(id, "张三", 22, "安徽六安", 93.5, likes);
        return new AjaxResult(200, "成功", studentVO);
    }

    /***
     * 查询全部学生数据
     * @return 返回d条数据
     */
    @Operation(
        summary = "查询全部学生数据",
        description = "查询学生信息,并返回响应结果信息",
        parameters = {},
        deprecated = true,
        responses = {
            @ApiResponse(
                responseCode = "200",
                description = "响应成功",
                content = @Content(
                    mediaType = "application/json",
                    schema = @Schema(
                        title = "AjaxResul和StudentVO组合模型",
                        description = "返回实体,AjaxResult内data为" +
                                "StudentVO模型(并且StudentVO为集合)",
                        anyOf = {AjaxResult.class, StudentVO.class})
                )
            )
        }
    )
    @GetMapping("/findAll")
    public AjaxResult findAllStudent() {
        //模拟学生数据
        List<String> likes = Arrays.asList("抓鱼", "爬山", "写字");
        StudentVO student1 = new StudentVO(1L, "张三", 22, "安徽六安", 93.5, likes);
        StudentVO student2 = new StudentVO(2L, "李四", 24, "安徽合肥", 99.5, likes);
        return new AjaxResult(200, "成功", Arrays.asList(student1, student2));
    }

    /***
     * 学生添加接口
     * @param studentDTO 学生DTO信息
     * @return 成功信息
     */
    @Operation(
            summary = "学生添加接口",
            description = "学生添加接口",
            parameters = {},
            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                description = "学生信息DTO",
                required = true,
                content = {
                    @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = StudentDTO.class))
                }
            ),
            responses = {
                @ApiResponse(
                    responseCode = "200",
                    description = "响应成功",
                    content = @Content(
                        mediaType = "application/json",
                        schema = @Schema(
                            title = "AjaxResul模型",
                            description = "返回实体AjaxResult,并且Data为null",
                            implementation = AjaxResult.class)
                    )
                )
            }
    )
    @PostMapping("/saveStudent")
    public AjaxResult saveStudent(@RequestBody StudentDTO studentDTO) {
        System.out.println("成功添加数据:" + studentDTO);
        return new AjaxResult(200, "成功", null);
    }  
    
}

8. 权限认证方式:@SecurityScheme注解

接口一半都是有权限校验方式的,每个接口在请求时都是包含校验,如常见的JWT校验,访问每个接口都需要携带Token信息,下面简单介绍校验认证的方式描述。

权限设置的注解是:@SecurityScheme ,用于定义API的安全方案。

通过使用@SecurityScheme注解,可以为API定义多种安全方案,并指定每种方案的相关属性, 例如认证类型、授权URL、令牌URL、作用域等。

@SecurityScheme 常用属性:

  • name: 安全方案的名称;
  • type: 认证类型,具体包含如下:SecuritySchemeType.API_KEY(API密钥)、SecuritySchemeType.HTTP(HTTP认证)、 SecuritySchemeType.OAUTH2(OAuth2.0 认证)等;
  • description: 安全方案的描述信息;
  • in: 仅在使用API密钥认证时适用,表示API密钥的位置,包括如下: ApiKeyLocation.HEADER(HTTP 头部)、ApiKeyLocation.COOKIE(Cookie)等
  • scheme: 仅在使用HTTP认证时适用,表示认证方案,例如HTTP Authentication Scheme.Basic(Basic 认证); 防止客户端需要在请求头部中添加一个包含用户名和密码的 Base64 编码字符串,并以 "Basic "开头,如: Authorization: Basic YWRtaW46MTIzNDU2
  • bearerFormat: 仅在使用 Bearer Token 认证时适用,表示 Bearer Token 的格式;
  • flows: 仅在使用OAuth2.0认证时适用,表示OAuth2.0的认证流程,包括@OAuthFlows.authorizationCode、 @OAuthFlows.clientCredentials、@OAuthFlows.password 和 @OAuthFlows.implicit等。

比如添加了一个名称为”JWT-test“的验证方式,使用方式存在2种,一种是设置全部文档使用这种验证,另一种是某个接口使用此种校验方式,可在自定义的SwaggerOpenApiConfig类上面设置了两种权限校验方式,如下:

@SecurityScheme(
    name = "JWT-test",                   // 认证方案名称
    type = SecuritySchemeType.HTTP,      // 认证类型,当前为http认证
    description = "这是一个认证的描述详细",  // 描述信息
    in = SecuritySchemeIn.HEADER,        // 代表在http请求头部
    scheme = "bearer",                   // 认证方案,如:Authorization: bearer token信息
    bearerFormat = "JWT")                // 表示使用 JWT 格式作为 Bearer Token 的格式
@SecurityScheme(
    name = "X-API-KEY",
    type = SecuritySchemeType.APIKEY,
    description = "这是一个认证的描述详细",
    in = SecuritySchemeIn.HEADER,
    scheme = "bearer")
    

设置全部接口描述都有指定的一种校验方式:在@OpenAPIDefinition注解里的security属性设置校验方式

    @OpenAPIDefinition(

    ....
    security = @SecurityRequirement(name = "JWT-test")
  )

设置指定接口描述有指定的一种校验方式:在@Operation注解里的security属性设置校验方式

    @Operation(

            ....
      security = @SecurityRequirement(name = "JWT-test")
    )

9. Swagger-UI查看接口文档

按照上述要求完成OpenApi的使用后,可以基于Swagger-UI的界面,查看本项目的接口文档: 浏览器输入地址:http://ip:port/swagger-ui.html

其中,ip和port是本项目运行的地址和端口;

页面样式:

image.png

10. Knife4j 的使用

Knife4j 是基于 Swagger UI 的增强版本,专为中国开发者社区设计,主要目的是改善Swagger-UI比较丑陋的前端页面。它在 springdoc-openapi 和 Swagger 3 的基础上进行了功能扩展,提供了更强大、更易用的 API 文档管理和展示工具。

可惜的是Knife4j目前的最后更新时间截止到2021年,其增强功能只适用于SpringFox(OpenAPI2.x规范)。

使用springdoc-openapi框架而非springfox时,需要使用Knife4j的3.x版本,该版本没有Knife4j提供的部分增强功能,是一个纯Ui,引入如下依赖即可:

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-springdoc-ui</artifactId>
    <version>3.0.3</version>
</dependency>

在application.yml中添加配置:

knife4j:
  enable: true
  setting:
    language: zh-CN

Knife4j的页面访问地址:http://ip:port/doc.html

页面样式:

image.png

11. 演示项目地址

github.com/zhaojf2022/…