1、概览 {#1概览}
本文将带你了解如何在 Spring MVC Web 应用中使用 springdoc-openapi 配置默认的全局 Security Scheme,并将其应用为 API 的默认安全配置,以及如何覆盖这些默认的安全配置。
OpenAPI 规范 允许为 API 定义一套 Security Scheme。可以配置 API 全局的安全配置,也可以按端点应用/删除安全配置。
2、设置 {#2设置}
构建一个 Spring Boot Web 项目,使用 Maven。
2.1、依赖 {#21依赖}
该示例有两个依赖。第一个是 spring-boot-starter-web
,用于构建 Web 应用。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.1</version>
</dependency>
另一个依赖是 springdoc-openapi-ui
,它用于输出 HTML、JSON 或 YAML 格式的 API 文档:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.9</version>
</dependency>
2.2、启动类 {#22启动类}
使用 @SpringBootApplication
注解来定义启动类,通过 SpringApplication
类来启动应用:
@SpringBootApplication
public class DefaultGlobalSecuritySchemeApplication {
public static void main(String[] args) {
SpringApplication.run(DefaultGlobalSecuritySchemeApplication.class, args);
}
}
3、springdoc-openapi
基础配置 {#3springdoc-openapi-基础配置}
配置好 Spring MVC 后,来看看 API 语义信息。
通过向 DefaultGlobalSecuritySchemeApplication
类添加 springdoc-openapi
注解来定义默认的全局 Security Scheme 和 API 元数据。
使用 @SecurityScheme
注解定义全局 Security Scheme:
@SecurityScheme(type = SecuritySchemeType.APIKEY, name = "api_key", in = SecuritySchemeIn.HEADER)
这里选择了 APIKEY
Security Scheme 类型,也可以配置其他 Security Scheme,例如 JWT
。在定义 Security Scheme 之后,使用 @OpenApiDefinition
注解来添加元数据并为 API 建立默认的安全配置(SecurityRequirement)。
@OpenAPIDefinition(info = @Info(title = "Apply Default Global SecurityScheme in springdoc-openapi", version = "1.0.0"), security = { @SecurityRequirement(name = "api_key") })
如上,info
属性定义了 API 元数据。security
属性定义了默认的全局安全配置。
使用了这些注解后的 HTML 文档如下。可以看到元数据以及适用于整个 API 的 "Authorize" 按钮。
4、Controller {#4controller}
配置好了 springdoc-openapi
后,添加一个 Rest Controller 如下:
@RestController
@RequestMapping("/")
public class DefaultGlobalSecuritySchemeOpenApiController {
...
}
然后,定义两个端点或 路径(path)。
第一个端点是 /login
端点。它接收用户凭证并对用户进行身份认证。如果验证成功,端点返回一个 Token。
另一个端点是 /ping
端点,需要使用 /login
方法生成的 Token。在执行请求之前,该方法会验证 Token 并检查用户是否获得授权。
总之,/login
端点验证用户身份并提供一个 Token。/ping
端点接收 /login
端点返回的 Token,并检查其是否有效,用户是否可以执行操作。
4.1、login()
方法 {#41login-方法}
该方法没有任何安全配置,需要对默认的安全配置进行覆盖。
首先,添加注解 @RequestMapping
表示这是一个 API 端点:
@RequestMapping(method = RequestMethod.POST, value = "/login", produces = { "application/json" }, consumes = { "application/json" })
之后,需要为端点添加语义信息。使用 @Operation
和 @SecurityRequirements
注解。@Operation
定义端点,@SecurityRequirements
定义适用于端点的特定安全配置集:
@Operation(operationId = "login", responses = {
@ApiResponse(responseCode = "200", description = "api_key to be used in the secured-ping endpoint", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = TokenDto.class)) }),
@ApiResponse(responseCode = "401", description = "Unauthorized request", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ApplicationExceptionDto.class)) }) })
@SecurityRequirements()
例如,以下是状态代码为 200 响应的 HTML 文档:
最后,看看 login()
方法的签名:
public ResponseEntity login(@Parameter(name = "LoginDto", description = "Login") @Valid @RequestBody(required = true) LoginDto loginDto) {
...
}
如你所见,API 会接收一个 LoginDto
实例。还必须用语义信息装饰 DTO,以便在文档中显示信息:
public class LoginDto {
private String user;
private String pass;
...
@Schema(name = "user", required = true)
public String getUser() {
return user;
}
@Schema(name = "pass", required = true)
public String getPass() {
return pass;
}
}
此时,/login
端点的 HTML 文档如下:
4.2、ping()
方法 {#42ping-方法}
定义 ping()
方法,使用默认的全局 Security Scheme:
@Operation(operationId = "ping", responses = {
@ApiResponse(responseCode = "200", description = "Ping that needs an api_key attribute in the header", content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = PingResponseDto.class), examples = { @ExampleObject(value = "{ pong: '2022-06-17T18:30:33.465+02:00' }") }) }),
@ApiResponse(responseCode = "401", description = "Unauthorized request", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ApplicationExceptionDto.class)) }),
@ApiResponse(responseCode = "403", description = "Forbidden request", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ApplicationExceptionDto.class)) }) })
@RequestMapping(method = RequestMethod.GET, value = "/ping", produces = { "application/json" })
public ResponseEntity ping(@RequestHeader(name = "api_key", required = false) String api_key) {
...
}
login()
和 ping()
方法的主要区别在于应用的安全配置。login()
方法根本没有任何安全配置,而 ping()
方法的安全配置则是在 API 层定义的。因此,HTML 文档中的 /ping
端点显示了一把小锁:
- REST API 文档 URL {#5-rest-api-文档-url}
至此,Spring MVC Web 应用已经准备就绪,可以启动服务器了:
mvn spring-boot:run -Dstart-class="com.baeldung.defaultglobalsecurityscheme.DefaultGlobalSecuritySchemeApplication"
服务器启动成功后,可以在 http://localhost:8080/swagger-ui-custom.html
URL 上看到 HTML 文档,如前面的示例所示。
JSON 版本的 API 定义在 http://localhost:8080/api-docs
端点,YAML 版本的在 http://localhost:8080/api-docs.yaml
端点。
可以使用 swagger-codegen-maven-plugin
将这些输出用于不同语言的 API 客户端或服务器的构建。
6、总结 {#6总结}
本文介绍了如何使用 springdoc-openapi 定义默认全局 Security Scheme,以及如何将其用作 API 的默认安全配置,最后了解了如何更改特定端点的默认安全配置。
参考:https://www.baeldung.com/spring-openapi-global-securityscheme