- 一、前言
- 二、swagger公共模块抽取
- 1、引入依赖
- 2、Knife4j配置类
- 3、application-swagger.yml
- 三、各微服务接口文档
- 1、引入swagger公共模块
- 2、服务模块`bootstrap.yml`
- 3、公共基础配置`bootstrap.yaml`
- 四、Gateway聚合接口文档
- 1、引入依赖
- 2、动态聚合各个服务的swagger接口
- 五、swagger集成oauth2密码模式进行授权认证
- 六、源码
本文将集成knife4j-spring-boot-starter3.0.3接口文档 & gateway聚合各服务接口文档 & 集成oauth2的密码模式进行授权认证登录
2.6.7 2021.0.1 2021.0.1.0
这里将swagger文档单独抽取了一个公共模块统一管理
2、Knife4j配置类com.github.xiaoymin knife4j-spring-boot-starter 3.0.3
tips: 这里使用的是swagger2.0的api文档方式,因为在3里面集成oauth2认证发现没有表单认证登录了,不太友好,也没看见官方文档有说明,如果之后有发现,再更新到小编的demo源码中吧^_^
@Slf4j
@Configuration
@EnableSwagger2WebMvc
// 对JSR303提供支持
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jConfig {
@Value("${spring.application.name}")
private String applicationName;
@Value("${small-tools.ip}")
private String ip;
@Value("${server.port}")
private String port;
@Value("${knife4j.passwordTokenUrl}")
private String passwordTokenUrl;
@Resource
private OpenApiExtensionResolver openApiExtensionResolver;
@Bean
public Docket defaultApi() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(this.apiInfo())
.groupName(this.applicationName)
.select()
// 添加@Api注解才显示
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
// .apis(RequestHandlerSelectors.basePackage("com.zhengqing"))
.paths(PathSelectors.any())
.build()
// 插件扩展 -- ex:自定义md文档
.extensions(this.openApiExtensionResolver.buildExtensions(this.applicationName))
// 默认全局参数
.globalRequestParameters(
Lists.newArrayList(
new RequestParameterBuilder()
.name(SwaggerConstant.TENANT_ID)
.description("租户ID")
.in(ParameterType.HEADER)
.required(true)
.build()
)
);
// context
List securityContexts = Lists.newArrayList(
SecurityContext.builder()
.securityReferences(
CollectionUtil.newArrayList(
new SecurityReference("oauth2",
Lists.newArrayList(
new AuthorizationScope("read", "read resources"),
new AuthorizationScope("write", "write resources"),
new AuthorizationScope("reads", "read all resources"),
new AuthorizationScope("writes", "write all resources")
).toArray(new AuthorizationScope[]{})
)
)
)
.forPaths(PathSelectors.ant("
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("API文档")
.description("API文档")
.termsOfServiceUrl(String.format("%s:%s/", this.ip, this.port))
.contact(
new Contact(
"zhengqingya",
"https://gitee.com/zhengqingya",
"960869719@qq.com"
)
)
.version("1.0.0")
.build();
}
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier,
ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes,
CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties,
Environment environment) {
List> allEndpoints = new ArrayList();
Collection webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
String basePath = webEndpointProperties.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}
private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
}
3、application-swagger.yml
spring:
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER # 解决springboot高版本Knife4j报错问题
# https://doc.xiaominfo.com/knife4j
knife4j:
# 开启增强配置
enable: true
# 是否开启生产环境屏蔽 true:关闭swagger,false:开启swagger
production: false
# 自定义文档
documents:
- group: demo
name: 测试自定义标题分组
# 某一个文件夹下所有的.md文件
locations: classpath:markdown
private static final String SWAGGER_URL = "/v2/api-docs?group=";
@Resource
private DiscoveryClient discoveryClient;
@Value("${spring.application.name}")
private String gatewayServiceName;
@Override
public List get() {
// swagger下拉列表服务资源
List resourceList = new ArrayList<>();
// 动态获取nacos注册中心上的服务实例名
List nacosServiceNameList = this.discoveryClient.getServices();
for (String instance : nacosServiceNameList) {
String instanceName = instance.toLowerCase();
if (this.gatewayServiceName.equals(instanceName)) {
continue;
}
// 拼接swagger访问url,样式为`/demo/v2/api-info?group=demo`,当网关调用这个接口时,会自动通过负载均衡寻找对应的主机
String url = "/" + instanceName + SWAGGER_URL + instanceName;
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setUrl(url);
swaggerResource.setName(instance);
resourceList.add(swaggerResource);
}
return resourceList;
}
}
swagger-ui.html需要访问的接口
@RestController
@RequiredArgsConstructor
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
private final MySwaggerResourcesProvider mySwaggerResourcesProvider;
@GetMapping("/configuration/security")
public ResponseEntity securityConfiguration() {
return new ResponseEntity<>(SecurityConfigurationBuilder.builder().build(), HttpStatus.OK);
}
@GetMapping("/configuration/ui")
public ResponseEntity uiConfiguration() {
return new ResponseEntity<>(UiConfigurationBuilder.builder().build(), HttpStatus.OK);
}
@GetMapping("")
public ResponseEntity swaggerResources() {
return new ResponseEntity<>(mySwaggerResourcesProvider.get(), HttpStatus.OK);
}
}
此方式会动态拉取我们nacos上面已注册的服务名进行拼接访问我们各微服务的接口文档
ex:/demo/v2/api-info?group=demo
在swagger公共模块中已经引入了oauth2的认证
一旦认证成功后,刷新页面会自动刷新Authorization的值方便我们去测试api
这里需要说明的是认证接口授权成功后Authorization取值问题
一般后端都会有统一响应体
{
"code": 200,
"data": [],
"msg": "OK"
}
当我们有了统一响应体之后Authorization值便会出现undefined undefined的问题
我们可以打开浏览器查看前端UI缓存值发现取值tokenType &accessToken
再去Knife4j的前端UI源码中搜索相关值,可以看见取值逻辑为
$.post(url,params,function(data){
if(data!=null&&data!=undefined) {
that.cacheValue.accessToken=data.token_type+" "+data.access_token;
that.cacheValue.tokenType=data.token_type;
that.cacheValue.granted=true;
window.localStorage.setItem(that.state,JSON.stringify(that.cacheValue))
window.close();
}
})
于是乎在我们的授权认证成功后修改返回所需字段
再次认证后Authorization取值恢复正常^_^
tips: 最近项目在重构,最新代码放在dev分支上了
https://gitee.com/zhengqingya/small-tools
今日分享语句:
无论何时,不管怎样,我也绝不允许自己有一点点心丧气。



