我有一个像这样的 Swagger 配置
@EnableSwagger2
@Configuration
public class SwaggerConfig {
@Bean
public Docket api() {
List<SecurityScheme> schemeList = new ArrayList<>();
schemeList.add(new ApiKey(HttpHeaders.AUTHORIZATION, "JWT", "header"));
return new Docket(DocumentationType.SWAGGER_2)
.produces(Collections.singleton("application/json"))
.consumes(Collections.singleton("application/json"))
.ignoredParameterTypes(Authentication.class)
.securitySchemes(schemeList)
.useDefaultResponseMessages(false)
.select()
.apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot")))
.paths(PathSelectors.any())
.build();
}
}
在 Swagger UI 中,当我单击“授权”按钮时,我在值字段中输入我的 JWT 令牌
eyJhbGc..nN84qrBg
。现在,我希望通过 Swagger UI 执行的任何请求都将在标头中包含 JWT。然而,事实并非如此。
没有请求包含授权标头。
我错过了什么?
Authorization: Bearer [JWT_TOKEN]
标头的支持从版本 2.9.2 开始工作compile("io.springfox:springfox-swagger2:2.9.2") {
exclude module: 'mapstruct' // necessary in my case to not end up with multiple mapstruct versions
}
compile "io.springfox:springfox-bean-validators:2.9.2"
compile "io.springfox:springfox-swagger-ui:2.9.2"
@Configuration
@EnableSwagger2
@Import(springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration.class)
public class SwaggerConfiguration {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String DEFAULT_INCLUDE_PATTERN = "/api/.*";
private final Logger log = LoggerFactory.getLogger(SwaggerConfiguration.class);
@Bean
public Docket swaggerSpringfoxDocket() {
log.debug("Starting Swagger");
Contact contact = new Contact(
"Matyas Albert-Nagy",
"https://justrocket.de",
"[email protected]");
List<VendorExtension> vext = new ArrayList<>();
ApiInfo apiInfo = new ApiInfo(
"Backend API",
"This is the best stuff since sliced bread - API",
"6.6.6",
"https://justrocket.de",
contact,
"MIT",
"https://justrocket.de",
vext);
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.pathMapping("/")
.apiInfo(ApiInfo.DEFAULT)
.forCodeGeneration(true)
.genericModelSubstitutes(ResponseEntity.class)
.ignoredParameterTypes(Pageable.class)
.ignoredParameterTypes(java.sql.Date.class)
.directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
.directModelSubstitute(java.time.ZonedDateTime.class, Date.class)
.directModelSubstitute(java.time.LocalDateTime.class, Date.class)
.securityContexts(Lists.newArrayList(securityContext()))
.securitySchemes(Lists.newArrayList(apiKey()))
.useDefaultResponseMessages(false);
docket = docket.select()
.paths(regex(DEFAULT_INCLUDE_PATTERN))
.build();
watch.stop();
log.debug("Started Swagger in {} ms", watch.getTotalTimeMillis());
return docket;
}
private ApiKey apiKey() {
return new ApiKey("JWT", AUTHORIZATION_HEADER, "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(DEFAULT_INCLUDE_PATTERN))
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Lists.newArrayList(
new SecurityReference("JWT", authorizationScopes));
}
}
http://host:port/<context-root>/swagger-ui.html
在一系列较新的项目之后,我开始使用
springdoc-openapi
来生成基于 javadoc
的文档,从而消除了额外注释的需要。
写这篇文章给任何愿意尝试这个库的人。我会推荐它/我是这个库的快乐用户。
build.gradle
[...]
// swagger ui
implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
implementation 'org.springdoc:springdoc-openapi-javadoc:1.6.9'
annotationProcessor 'com.github.therapi:therapi-runtime-javadoc-scribe:0.13.0'
implementation 'com.github.therapi:therapi-runtime-javadoc:0.13.0'
[...]
使用项目特定的
SecurityConfiguration.java
- 定义OpenAPI授权的模式。本例:HTTP 中的 Bearer
中的 Authorization
。header
REST 控制器中的使用
import static io.swagger.v3.oas.annotations.enums.SecuritySchemeIn.HEADER;
import static io.swagger.v3.oas.annotations.enums.SecuritySchemeType.HTTP;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
@Component
@SecurityScheme(name = SecurityConfiguration.SECURITY_CONFIG_NAME, in = HEADER, type = HTTP, scheme = "bearer", bearerFormat = "JWT")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
[...]
public static final String SECURITY_CONFIG_NAME = "App Bearer token";
[...]
中的使用应参考安全配置
SomeController.java
配置可达性
import static com.x.common.security.SecurityConfiguration.SECURITY_CONFIG_NAME;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@RestController
@RequestMapping("/api/v1/resources")
@SecurityRequirement(name = SECURITY_CONFIG_NAME)
public class ConnectionSyncController {
/**
* Documentation that will be rendered
*
* supports
*
* 1. markdown
* 1. list
*/
@PostMapping("/{id}/sync")
@DomainAuthorize(permissionType = BasePermissions.PERM_ADMIN_OPERATIONS)
public void syncConnection(@PathVariable("id") Long id) {
/v3/api-docs
加载配置配置
swagger-ui
swagger-ui
/src/main/resources/application.yml
server:
port: 80
# needed for swagger-ui to detect correct proxied paths correctly.
# Configuration needed for the [Try out] buttons to work
# this works in combination with the proxied headers
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Prefix /services/impower-facilioo;
forward-headers-strategy: FRAMEWORK
springdoc:
swagger-ui:
# where the UI configuration is located at
configUrl: /[some/public/path]/v3/api-docs/swagger-config
filter: true
deepLinking: true
# where the server API yml/json files are at (dropdown in top right corner)
urls[0]:
url: /[some/public/path]/v3/api-docs
name: backend
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
.securitySchemes(Arrays.asList(apiKey()));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Sig-Predict REST API Document")
.description("work in progress")
.termsOfServiceUrl("localhost")
.version("1.0")
.build();
}
private ApiKey apiKey() {
return new ApiKey("jwtToken", "Authorization", "header");
}
在
springfox-swagger-ui/springfox-swagger2 版本 2.8.0 中有一个 bug,似乎 2.9.2 也是如此。我怀疑您正在使用受此错误影响的版本。 我只是降级到
2.7.0 @Configuration
@EnableSwagger2
public class SwaggerConfig {
private static final Set<String> DEFAULT_PRODUCES_CONSUMES = new HashSet<String>(Arrays.asList("application/json"));
@Bean
public Docket api() {
ParameterBuilder parameterBuilder = new ParameterBuilder();
parameterBuilder.name("Authorization")
.modelRef(new ModelRef("string"))
.parameterType("header")
.description("JWT token")
.required(true)
.build();
List<Parameter> parameters = new ArrayList<>();
parameters.add(parameterBuilder.build());
return new Docket(DocumentationType.SWAGGER_2).apiInfo(DEFAULT_API_INFO)
.produces(DEFAULT_PRODUCES_CONSUMES)
.consumes(DEFAULT_PRODUCES_CONSUMES)
.select()
.build()
// Setting globalOperationParameters ensures that authentication header is applied to all APIs
.globalOperationParameters(parameters);
}
}
做了一些研究来改进这一点,并使用 OpenApi 来实现这一点,而不需要那个微小的令人讨厌的添加。
来源我曾经继续这样做pom.xml 我有以下内容:
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any()).paths(PathSelectors.regex("/api/v1/.*"))
.build().groupName("API")
.globalOperationParameters(newArrayList(
new ParameterBuilder().name(HttpHeaders.AUTHORIZATION).description("Authorization token").required(true)
.modelRef(new ModelRef("string")).parameterType("header").required(true).build()))
.apiInfo(apiInfo());
在 application.properties 中我添加了几行(可选):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.5</version>
<relativePath />
</parent>
<properties>
<java.version>16</java.version>
<swagger.version>2.9.2</swagger.version>
<open.api.version>1.6.9</open.api.version>
</properties>
<dependencies>
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>${open.api.version}</version>
</dependency>
</dependencies>
swagger 需要有一些安全设置例外:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
springdoc.swagger-ui.path=swagger-ui.html
springdoc.paths-to-exclude=/swagger-resources/**
最后是使用 OpenApi 的 swagger 的实际配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/* Specify the urls not requiring authentication. */
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", "/configuration/ui", "/swagger-resources/**", "/configuration/**", "/webjars/**");
}
}
Spring Boot 3 + JWT + Swagger 示例
要为 Spring Boot 3
实现 JWT 令牌的 swagger,必须遵循以下步骤 -
package com.fujitsu.emom.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
/**
* Configuration for swagger using OpenApi.<br/>
* Notice the spring security must allow to access to the swagger ui at 'SecurityConfiguration.java'.<br/>
* There are also configuration at 'application.properties' for defining the URL to swagger page.
*/
@Configuration
public class SwaggerConfig {
public static final String SCHEME_NAME = "BearerScheme";
public static final String SCHEME = "Bearer";
@Bean
public OpenAPI customOpenAPI() {
var openApi = new OpenAPI().info(this.apiInfo());
this.addSecurity(openApi);
return openApi;
}
private Info apiInfo() {
var contact = new Contact();
contact.setEmail("[email protected]");
contact.setName("product_admin");
contact.setUrl("http://product.com");
return new Info()
.title("Product API")
.description("Product description")
.termsOfService("http://product.com/terms_of_service")
.contact(contact)
// TODO: Version should be dynamically
.version("0.5.1");
}
private void addSecurity(OpenAPI openApi) {
var components = this.createComponents();
var securityItem = new SecurityRequirement().addList(SCHEME_NAME);
openApi.components(components).addSecurityItem(securityItem);
}
private Components createComponents() {
var components = new Components();
components.addSecuritySchemes(SCHEME_NAME, this.createSecurityScheme());
return components;
}
private SecurityScheme createSecurityScheme() {
return new SecurityScheme().name(SCHEME_NAME).type(SecurityScheme.Type.HTTP).scheme(SCHEME);
}
}
}