Gateway 聚合swagger文檔
?
在微服務架構下,通常每個微服務 都會使用 Swagger 來管理我們的接口文檔,當微服務越來越多,接口查找管理無形中要浪費我們不少時間,因此,我們需要把其它系統的 Swagger 文檔聚合到 Gateway ,方便我們統一查看接口文檔。
1. 核心實現
1.1 OMS 端實現
1.1.1 swagger配置文件
@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {
/**
* 是否開啟swagger
*/
private Boolean enabled;
/**
* swagger會解析的包路徑
**/
private String basePackage = "";
/**
* swagger會解析的url規則
**/
private List<String> basePath = new ArrayList<>();
/**
* 在basePath基礎上需要排除的url規則
**/
private List<String> excludePath = new ArrayList<>();
/**
* 標題
**/
private String title = "";
/**
* 描述
**/
private String description = "";
/**
* 版本
**/
private String version = "";
/**
* 許可證
**/
private String license = "";
/**
* 許可證URL
**/
private String licenseUrl = "";
/**
* 服務條款URL
**/
private String termsOfServiceUrl = "";
/**
* host信息
**/
private String host = "";
/**
* 聯系人信息
*/
private Contact contact = new Contact();
/**
* 全局統一鑒權配置
**/
private Authorization authorization = new Authorization();
@Data
@NoArgsConstructor
public static class Contact {
/**
* 聯系人
**/
private String name = "";
/**
* 聯系人url
**/
private String url = "";
/**
* 聯系人email
**/
private String email = "";
}
@Data
@NoArgsConstructor
public static class Authorization {
/**
* 鑒權策略ID,需要和SecurityReferences ID保持一致
*/
private String name = "";
/**
* 需要開啟鑒權URL的正則
*/
private String authRegex = "^.*$";
/**
* 鑒權作用域列表
*/
private List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
private List<String> tokenUrlList = new ArrayList<>();
}
@Data
@NoArgsConstructor
public static class AuthorizationScope {
/**
* 作用域名稱
*/
private String scope = "";
/**
* 作用域描述
*/
private String description = "";
}
}
1.1.2 swagger 自動配置類
@Configuration
@EnableSwagger2
@EnableAutoConfiguration
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerAutoConfiguration {
/**
* 默認的排除路徑,排除Spring Boot默認的錯誤處理路徑和端點
*/
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error","/actuator/**");
private static final String BASE_PATH = "/**";
@Bean
@ConditionalOnMissingBean
public SwaggerProperties swaggerProperties() {
return new SwaggerProperties();
}
@Bean
public Docket api(SwaggerProperties swaggerProperties) {
// base-path處理
if (swaggerProperties.getBasePath().isEmpty()) {
swaggerProperties.getBasePath().add(BASE_PATH);
}
//noinspection unchecked
List<Predicate<String>> basePath = new ArrayList();
swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));
// exclude-path處理
if (swaggerProperties.getExcludePath().isEmpty()) {
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
}
List<Predicate<String>> excludePath = new ArrayList<>();
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
//noinspection Guava
return new Docket(DocumentationType.SWAGGER_2)
.host(swaggerProperties.getHost())
.apiInfo(apiInfo(swaggerProperties)).select()
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
.paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
.build()
.securitySchemes(Collections.singletonList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext()))
.pathMapping("/");
}
/**
* 配置默認的全局鑒權策略的開關,通過正則表達式進行匹配;默認匹配所有URL
*
* @return
*/
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex()))
.build();
}
/**
* 默認的全局鑒權策略
*
* @return
*/
private List<SecurityReference> defaultAuth() {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
return Collections.singletonList(SecurityReference.builder()
.reference(swaggerProperties().getAuthorization().getName())
.scopes(authorizationScopeList.toArray(authorizationScopes))
.build());
}
private OAuth securitySchema() {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
ArrayList<GrantType> grantTypes = new ArrayList<>();
swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes);
}
private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.license(swaggerProperties.getLicense())
.licenseUrl(swaggerProperties.getLicenseUrl())
.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
.version(swaggerProperties.getVersion())
.build();
}
}
1.1.3 設置允許跨域
如果不設置允許跨域,則 Gateway 無法跨域調用 OMS 的 Swagger 文檔,因此,需要設置 swagger 接口允許跨域。
package com.ftl.oms.config;
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否發送Cookie信息
config.setAllowCredentials(true);
//放行哪些原始域(請求方式)
config.addAllowedMethod("*");
//放行哪些原始域(頭部信息)
config.addAllowedHeader("*");
//暴露哪些頭部信息(因為跨域訪問默認不能獲取全部頭部信息)
config.addExposedHeader("Access-Control-Allow-*");
//2.添加映射路徑
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/api/**", config);
configSource.registerCorsConfiguration("/v2/api-docs", config);
//3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
1.2 GATEWAY 端實現
1.2.1 swagger配置文件
@Data
@Component
@ConfigurationProperties("swagger")
public class SwaggerProperties {
/**
* 資源名稱
*/
private List<Resource> resources;
/**
* 是否開啟swagger
*/
private Boolean enabled;
/**
* swagger會解析的包路徑
**/
private String basePackage = "";
/**
* swagger會解析的url規則
**/
private List<String> basePath = new ArrayList<>();
/**
* 在basePath基礎上需要排除的url規則
**/
private List<String> excludePath = new ArrayList<>();
/**
* 標題
**/
private String title = "";
/**
* 描述
**/
private String description = "";
/**
* 版本
**/
private String version = "";
/**
* 許可證
**/
private String license = "";
/**
* 許可證URL
**/
private String licenseUrl = "";
/**
* 服務條款URL
**/
private String termsOfServiceUrl = "";
/**
* host信息
**/
private String host = "";
/**
* 聯系人信息
*/
private Contact contact = new Contact();
/**
* 全局統一鑒權配置
**/
private Authorization authorization = new Authorization();
@Data
@NoArgsConstructor
public static class Contact {
/**
* 聯系人
**/
private String name = "";
/**
* 聯系人url
**/
private String url = "";
/**
* 聯系人email
**/
private String email = "";
}
@Data
@NoArgsConstructor
public static class Authorization {
/**
* 鑒權策略ID,需要和SecurityReferences ID保持一致
*/
private String name = "";
/**
* 需要開啟鑒權URL的正則
*/
private String authRegex = "^.*$";
/**
* 鑒權作用域列表
*/
private List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
private List<String> tokenUrlList = new ArrayList<>();
}
@Data
@NoArgsConstructor
public static class AuthorizationScope {
/**
* 作用域名稱
*/
private String scope = "";
/**
* 作用域描述
*/
private String description = "";
}
@Data
@NoArgsConstructor
public static class Resource {
/**
* 資源名稱
*/
private String name;
/**
* 轉發路徑
*/
private String url;
/**
* 文檔版本
*/
private String swaggerVersion;
}
}
注:GATEWAY 端比 OMS 端的配置文件多出了
private List<Resource> resources;,這個主要功能是從配置文件中得到 OMS 的 swagger 接口文檔接口, 從而聚合 其它服務的文檔。
1.2.2 swagger 自動配置類
@Configuration
@EnableSwagger2
@EnableAutoConfiguration
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerAutoConfiguration {
/**
* 默認的排除路徑,排除Spring Boot默認的錯誤處理路徑和端點
*/
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error","/actuator/**");
private static final String BASE_PATH = "/**";
@Bean
@ConditionalOnMissingBean
public SwaggerProperties swaggerProperties() {
return new SwaggerProperties();
}
@Bean
public Docket api(SwaggerProperties swaggerProperties) {
// base-path處理
if (swaggerProperties.getBasePath().isEmpty()) {
swaggerProperties.getBasePath().add(BASE_PATH);
}
//noinspection unchecked
List<Predicate<String>> basePath = new ArrayList();
swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));
// exclude-path處理
if (swaggerProperties.getExcludePath().isEmpty()) {
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
}
List<Predicate<String>> excludePath = new ArrayList<>();
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
//noinspection Guava
return new Docket(DocumentationType.SWAGGER_2)
.host(swaggerProperties.getHost())
.apiInfo(apiInfo(swaggerProperties)).select()
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
.paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
.build()
.securitySchemes(Collections.singletonList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext()))
.pathMapping("/");
}
/**
* 配置默認的全局鑒權策略的開關,通過正則表達式進行匹配;默認匹配所有URL
*
* @return
*/
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex()))
.build();
}
/**
* 默認的全局鑒權策略
*
* @return
*/
private List<SecurityReference> defaultAuth() {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
return Collections.singletonList(SecurityReference.builder()
.reference(swaggerProperties().getAuthorization().getName())
.scopes(authorizationScopeList.toArray(authorizationScopes))
.build());
}
private OAuth securitySchema() {
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
ArrayList<GrantType> grantTypes = new ArrayList<>();
swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes);
}
private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.license(swaggerProperties.getLicense())
.licenseUrl(swaggerProperties.getLicenseUrl())
.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
.version(swaggerProperties.getVersion())
.build();
}
}
1.2.3 swagger 資源配置信息
這個類主要是用來暴露 swagger 相關資源的接口,允許瀏覽器加載所對應的資源文件
@RestController
@RequestMapping("/swagger-resources")
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;
}
@GetMapping("/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
1.2.4 swagger 聚合配置
這個類主要負責的功能是將其它系統的文檔聚合到 gateway,重寫 SwaggerResourcesProvider#get() 方法,從 yml 配置文件中,得到其它系統的 swagger 文檔信息,然后gateway 轉發到不同的系統的 swagger。
@Component
@Primary
@AllArgsConstructor
public class SwaggerProviderConfiguration implements SwaggerResourcesProvider {
private static final String API_URI = "/v2/api-docs";
@Autowired
private SwaggerProperties swaggerProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerProperties.Resource> resourcesPropertes = swaggerProperties.getResources();
if (resourcesPropertes.isEmpty()) {
return Lists.newArrayList();
}
// 從yml配置文件中加載其它系統的swagger資源
List<SwaggerResource> resources = new ArrayList<>();
resourcesPropertes.forEach(resource -> {
resources.add(this.swaggerResource(resource.getName(), resource.getUrl() + API_URI, resource.getSwaggerVersion()));
});
return resources;
}
private SwaggerResource swaggerResource(String name, String url, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(url);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
2. 展示效果
聚合其它系統 swagger 文檔展示

3. 參考文檔
SpringCloud Gateway聚合各個服務的Swagger
Spring Cloud Gateway 聚合swagger文檔

浙公網安備 33010602011771號