This commit is contained in:
kk
2026-07-07 18:01:21 +08:00
commit b259f94d4b
1088 changed files with 121778 additions and 0 deletions
@@ -0,0 +1,160 @@
/*
* Copyright © 2017-2023 Knife4j(xiaoymin@foxmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.xiaoymin.knife4j.spring.extension;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.github.xiaoymin.knife4j.core.conf.ExtensionsConstants;
import com.github.xiaoymin.knife4j.core.conf.GlobalConstants;
import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties;
import com.github.xiaoymin.knife4j.spring.configuration.Knife4jSetting;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.models.OpenAPI;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RestController;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Collectors;
/**
* 增强扩展属性支持
* 升级支持springboot3.5+
*
* @author xiaoymin@foxmail.comBladeX
* 2022/12/11 22:40
* @since 4.1.0
*/
@Slf4j
@AllArgsConstructor
public class Knife4jOpenApiCustomizer implements GlobalOpenApiCustomizer {
final Knife4jProperties knife4jProperties;
final SpringDocConfigProperties properties;
@Override
public void customise(OpenAPI openApi) {
log.debug("Knife4j OpenApiCustomizer");
if (knife4jProperties.isEnable()) {
Knife4jSetting setting = knife4jProperties.getSetting();
OpenApiExtensionResolver openApiExtensionResolver = new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments());
// 解析初始化
openApiExtensionResolver.start();
Map<String, Object> objectMap = new HashMap<>();
objectMap.put(GlobalConstants.EXTENSION_OPEN_SETTING_NAME, setting);
objectMap.put(GlobalConstants.EXTENSION_OPEN_MARKDOWN_NAME, openApiExtensionResolver.getMarkdownFiles());
openApi.addExtension(GlobalConstants.EXTENSION_OPEN_API_NAME, objectMap);
addOrderExtension(openApi);
}
}
/**
* 往OpenAPI内tags字段添加x-order属性
*
* @param openApi openApi
*/
private void addOrderExtension(OpenAPI openApi) {
if (CollectionUtils.isEmpty(properties.getGroupConfigs())) {
return;
}
// 获取包扫描路径
Set<String> packagesToScan =
properties.getGroupConfigs().stream()
.map(SpringDocConfigProperties.GroupConfig::getPackagesToScan)
.filter(toScan -> !CollectionUtils.isEmpty(toScan))
.flatMap(List::stream)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(packagesToScan)) {
return;
}
// 扫描包下被ApiSupport注解的RestController Class
Set<Class<?>> classes =
packagesToScan.stream()
.map(packageToScan -> scanPackageByAnnotation(packageToScan, RestController.class))
.flatMap(Set::stream)
.filter(clazz -> clazz.isAnnotationPresent(ApiSupport.class))
.collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(classes)) {
// ApiSupport oder值存入tagSortMap<Tag.name,ApiSupport.order>
Map<String, Integer> tagOrderMap = new HashMap<>();
classes.forEach(
clazz -> {
Tag tag = getTag(clazz);
if (Objects.nonNull(tag)) {
ApiSupport apiSupport = clazz.getAnnotation(ApiSupport.class);
tagOrderMap.putIfAbsent(tag.name(), apiSupport.order());
}
});
// 往openApi tags字段添加x-order增强属性
if (openApi.getTags() != null) {
openApi
.getTags()
.forEach(
tag -> {
if (tagOrderMap.containsKey(tag.getName())) {
tag.addExtension(
ExtensionsConstants.EXTENSION_ORDER, tagOrderMap.get(tag.getName()));
}
});
}
}
}
private Tag getTag(Class<?> clazz) {
// 从类上获取
Tag tag = clazz.getAnnotation(Tag.class);
if (Objects.isNull(tag)) {
// 从接口上获取
Class<?>[] interfaces = clazz.getInterfaces();
if (ArrayUtils.isNotEmpty(interfaces)) {
for (Class<?> interfaceClazz : interfaces) {
Tag anno = interfaceClazz.getAnnotation(Tag.class);
if (Objects.nonNull(anno)) {
tag = anno;
break;
}
}
}
}
return tag;
}
private Set<Class<?>> scanPackageByAnnotation(
String packageName, final Class<? extends Annotation> annotationClass) {
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationClass));
Set<Class<?>> classes = new HashSet<>();
for (BeanDefinition beanDefinition : scanner.findCandidateComponents(packageName)) {
try {
Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
classes.add(clazz);
} catch (ClassNotFoundException ignore) {
}
}
return classes;
}
}
@@ -0,0 +1,40 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.swagger;
import java.lang.annotation.*;
/**
* Swagger配置开关
*
* @author Chill
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableSwagger {
}
@@ -0,0 +1,184 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.swagger;
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.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.NumberUtil;
import org.springdoc.core.configuration.SpringDocConfiguration;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* swagger配置
*
* @author Chill
*/
@Slf4j
@EnableSwagger
@Configuration
@AllArgsConstructor
@AutoConfigureBefore(SpringDocConfiguration.class)
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(value = "swagger.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerAutoConfiguration {
private static final String DEFAULT_BASE_PATH = "/**";
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String TOKEN_HEADER = TokenConstant.AUTH_HEADER;
private static final String TENANT_HEADER = "Tenant-Id";
/**
* 引入Swagger配置类
*/
private final SwaggerProperties swaggerProperties;
/**
* 初始化OpenAPI对象
*/
@Bean
public OpenAPI openApi() {
// 初始化OpenAPI对象,并设置API的基本信息、安全策略、联系人信息、许可信息以及外部文档链接
return new OpenAPI()
.components(new Components()
// 添加安全策略,配置API密钥(Token)和鉴权机制
.addSecuritySchemes(TOKEN_HEADER,
new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER)
.scheme("bearer")
.bearerFormat("JWT")
.name(TOKEN_HEADER)
)
// 添加安全策略,配置API密钥(Authorization)和鉴权机制
.addSecuritySchemes(AUTHORIZATION_HEADER,
new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER)
.name(AUTHORIZATION_HEADER)
)
// 添加安全策略,配置租户ID(Tenant-Id)和鉴权机制
.addSecuritySchemes(TENANT_HEADER,
new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER)
.name(TENANT_HEADER)
)
)
// 设置API文档的基本信息,包括标题、描述、联系方式和许可信息
.info(new Info()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.termsOfService(swaggerProperties.getTermsOfServiceUrl())
.contact(new Contact()
.name(swaggerProperties.getContact().getName())
.email(swaggerProperties.getContact().getEmail())
.url(swaggerProperties.getContact().getUrl())
)
.license(new License()
.name(swaggerProperties.getLicense())
.url(swaggerProperties.getLicenseUrl())
)
.version(swaggerProperties.getVersion())
);
}
/**
* 初始化GlobalOpenApiCustomizer对象
*/
@Bean
@ConditionalOnMissingBean
public GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() {
return openApi -> {
if (openApi.getPaths() != null) {
openApi.getPaths().forEach((s, pathItem) -> pathItem.readOperations().forEach(operation ->
operation.addSecurityItem(new SecurityRequirement()
.addList(AUTHORIZATION_HEADER)
.addList(TOKEN_HEADER)
.addList(TENANT_HEADER))));
}
if (openApi.getTags() != null) {
openApi.getTags().forEach(tag -> {
Map<String, Object> map = new HashMap<>();
map.put("x-order", NumberUtil.parseFirstInt(tag.getDescription()));
tag.setExtensions(map);
});
}
};
}
/**
* 初始化GroupedOpenApi对象
*/
@Bean
@ConditionalOnMissingBean
public GroupedOpenApi defaultApi() {
// 如果Swagger配置中的基本路径和排除路径为空,则设置默认的基本路径和排除路径
if (CollectionUtil.isEmpty(swaggerProperties.getBasePath())) {
swaggerProperties.getBasePath().add(DEFAULT_BASE_PATH);
}
if (CollectionUtil.isEmpty(swaggerProperties.getExcludePath())) {
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
}
// 获取Swagger配置中的基本路径、排除路径、基本包路径和排除包路径
List<String> basePath = swaggerProperties.getBasePath();
List<String> excludePath = swaggerProperties.getExcludePath();
List<String> basePackages = swaggerProperties.getBasePackages();
List<String> excludePackages = swaggerProperties.getExcludePackages();
// 创建并返回GroupedOpenApi对象
return GroupedOpenApi.builder()
.group("default")
.pathsToMatch(basePath.toArray(new String[0]))
.pathsToExclude(excludePath.toArray(new String[0]))
.packagesToScan(basePackages.toArray(new String[0]))
.packagesToExclude(excludePackages.toArray(new String[0]))
.build();
}
}
@@ -0,0 +1,70 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.swagger;
import org.springblade.core.auto.service.AutoService;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.launch.service.LauncherService;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.core.Ordered;
import java.util.Properties;
/**
* 初始化Swagger配置
*
* @author Chill
*/
@AutoService(LauncherService.class)
public class SwaggerLauncherServiceImpl implements LauncherService {
@Override
public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
Properties props = System.getProperties();
if (profile.equals(AppConstant.PROD_CODE)) {
props.setProperty("swagger.enabled", "false");
props.setProperty("knife4j.enable", "false");
props.setProperty("knife4j.production", "true");
props.setProperty("springdoc.api-docs.enabled", "false");
props.setProperty("springdoc.api-usage.enabled", "false");
props.setProperty("springdoc.swagger-ui.enabled", "false");
props.setProperty("springdoc.default-flat-param-object", "false");
} else {
props.setProperty("swagger.enabled", "true");
props.setProperty("knife4j.enable", "true");
props.setProperty("knife4j.production", "false");
props.setProperty("springdoc.api-docs.enabled", "true");
props.setProperty("springdoc.api-usage.enabled", "true");
props.setProperty("springdoc.swagger-ui.enabled", "true");
props.setProperty("springdoc.default-flat-param-object", "true");
props.setProperty("spring.mvc.pathmatch.matching-strategy", "ANT_PATH_MATCHER");
}
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
@@ -0,0 +1,142 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.swagger;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springblade.core.launch.constant.AppConstant;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* SwaggerProperties
*
* @author Chill
*/
@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {
/**
* 是否开启swagger
*/
private boolean enabled = true;
/**
* swagger会解析的包路径
**/
private List<String> basePackages = new ArrayList<>(Collections.singletonList(AppConstant.BASE_PACKAGES));
/**
* swagger会排除解析的包路径
**/
private List<String> excludePackages = new ArrayList<>();
/**
* swagger会解析的url规则
**/
private List<String> basePath = new ArrayList<>();
/**
* 在basePath基础上需要排除的url规则
**/
private List<String> excludePath = new ArrayList<>();
/**
* 标题
**/
private String title = "BladeX 接口文档系统";
/**
* 描述
**/
private String description = "BladeX 接口文档系统";
/**
* 版本
**/
private String version = AppConstant.APPLICATION_VERSION;
/**
* 许可证
**/
private String license = "Powered By BladeX";
/**
* 许可证URL
**/
private String licenseUrl = "https://license.bladex.cn";
/**
* 服务条款URL
**/
private String termsOfServiceUrl = "https://bladex.cn";
/**
* host信息
**/
private String host = "";
/**
* 联系人信息
*/
private Contact contact = new Contact();
/**
* 全局统一鉴权配置
**/
private Authorization authorization = new Authorization();
@Data
@NoArgsConstructor
public static class Contact {
/**
* 联系人
**/
private String name = "翼宿";
/**
* 联系人email
**/
private String email = "bladejava@qq.com";
/**
* 联系人url
**/
private String url = "https://gitee.com/smallc";
}
@Data
@NoArgsConstructor
public static class Authorization {
/**
* 鉴权策略ID,需要和SecurityReferences ID保持一致
*/
private String name = "";
/**
* 需要开启鉴权URL的正则
*/
private String authRegex = "^.*$";
/**
* 接口匹配地址
*/
private List<String> tokenUrlList = new ArrayList<>();
}
}
@@ -0,0 +1,51 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.swagger;
import org.springblade.core.launch.props.BladePropertySource;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* swagger资源配置
*
* @author Chill
*/
@AutoConfiguration
@EnableConfigurationProperties(SwaggerProperties.class)
@BladePropertySource(value = "classpath:/blade-swagger.yml")
public class SwaggerWebConfiguration implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
@@ -0,0 +1,47 @@
#springdoc-openapi项目配置
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: order
operations-sorter: order
persist-authorization: true
api-docs:
enabled: true
path: /v3/api-docs
# 默认是false,需要设置为true
default-flat-param-object: true
#knife4j配置
knife4j:
#启用
enable: true
#基础认证
basic:
enable: false
username: blade
password: blade
#增强配置
setting:
enable-swagger-models: true
enable-document-manage: true
enable-host: false
enable-host-text: http://localhost
enable-request-cache: true
enable-filter-multipart-apis: false
enable-filter-multipart-api-method-type: POST
enable-footer: false
enable-footer-custom: true
language: zh_cn
footer-custom-content: Copyright © 2024 BladeX All Rights Reserved
#swagger公共信息
swagger:
title: BladeX 接口文档系统
description: BladeX 接口文档系统
license: Powered By BladeX
license-url: https://license.bladex.cn
terms-of-service-url: https://bladex.cn
contact:
name: 翼宿
email: bladejava@qq.com
url: https://gitee.com/smallc