微服务中配置多组文档接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class RESTFulWebAPI {
public Docket createControllerApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName(" 接口分组名 1")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.**.**.controller"))
.paths(PathSelectors.any())
.build()
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
public Docket createRpcApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName(" 接口分组名 2")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.**.**.rpc"))
.paths(PathSelectors.any())
.build()
// 设置全局 token, 解决接口需要 token 验证的问题
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
private List<ApiKey> securitySchemes() {
List<ApiKey> apiKeyList = new ArrayList();
apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
return apiKeyList;
}
private List<SecurityContext> securityContexts() {
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build());
return securityContexts;
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(" 文档标题 ")
.version("0.0.0.1")
.description("API 描述 ")
.build();
}
}Gateway 配置网关
pom 文件添加依赖
1
2
3
4
5
6
7
8
9
10
11<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>注入路由到 SwaggerResource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class SwaggerProvider implements SwaggerResourcesProvider {
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
private final RestTemplate restTemplate;
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
// 取出 gateway 的 route
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
// 结合配置的 route- 路径(Path),和 route 过滤,只获取有效的 route 节点
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> {
SwaggerResource[] swaggerResources;
try {
swaggerResources = restTemplate
.getForObject("http://" + routeDefinition.getId() + "/swagger-resources", SwaggerResource[].class);
} catch (HttpClientErrorException | IllegalStateException e) {
return;
}
if (Objects.isNull(swaggerResources)) {
return;
}
for (SwaggerResource resource : swaggerResources) {
resource.setUrl(predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("/**", resource.getUrl()));
resources.add(resource);
}
}));
return resources;
}
}提供 Swagger 对外接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class SwaggerController {
private SecurityConfiguration securityConfiguration;
private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
public SwaggerController(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
}Swagger 的路径转换
Swagger 文档中的路径为:
主机名: 端口: 映射路径
少了一个服务路由前缀,是因为展示 handler 经过了StripPrefixGatewayFilterFactory
这个过滤器的处理,原有的路由前缀被过滤掉了!方案 1:通过 Swagger 的 host 配置手动维护一个前缀
1
2
3
4
5
6
7
8return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.host(" 主机名:端口:服务前缀 ") // 注意这里的主机名:端口是网关的地址和端口
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(parameterList);方案 2:增加 X-Forwarded-Prefix
配置 appction.yml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
private static final String HEADER_NAME = "X-Forwarded-Prefix";
private static final String API_URI = "/v2/api-docs";
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (!StringUtils.endsWithIgnoreCase(path, API_URI)) {
return chain.filter(exchange);
}
String basePath = path.substring(0, path.lastIndexOf(API_URI));
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
};
}
}1
2
3
4
5
6
7- id: test
uri: lb://test
predicates:
- Path=/test/**
filters:
- SwaggerHeaderFilter
- StripPrefix=1
采用方案 2,并且将
spring-cloud-dependencies
依赖从Finchley.RELEASE<
升级到Finchley.SR4
,spring-boot-starter-parent
依赖从2.0.1.RELEASE
升级到2.0.9.RELEASE
。文档中的路径多了一个服务路由前缀,将方案二代码去除,文档中的路径恢复正常。
Nginx 配置
Nginx 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 配置使用用户名和密码登录
location /swagger-ui.html {
auth_basic "test";
# 相对路径:htpasswd 在机器上的位置:/usr/local/nginx/conf/htpasswd
auth_basic_user_file htpasswd;
# 绝对路径:htpasswd 在机器上的位置:/tmp/htpasswd
# auth_basic_user_file /tmp/htpasswd;
proxy_pass http://***/swagger-ui.html;
}
location /swagger-resources {
proxy_pass http://**/swagger-resources;
}
location /webjars {
proxy_pass http://**/webjars;
}创建 htpasswd
1
2# 格式:用户名: 密码,注意密码是使用 crypt 加密过的
admin:PbSRr7orsxaso
参考