Compare commits

...

9 Commits
v2.14.0 ... dev

Author SHA1 Message Date
liquor
730b39d18e fix(api-docs): 修复 OpenAPI 安全配置未正确引用 securityScheme 名称的问题 2025-11-06 03:36:50 +00:00
liquor
deb330b4cd fix(api-doc): 修复全局 OpenAPI 鉴权配置错误,改为使用 securitySchemes 的 key 2025-10-21 02:46:00 +00:00
吴泽威
58e234a929 perf(log): 扩展静态资源路径匹配规则
- 新增 AntPathMatcher 支持更灵活的路径匹配
- 更新 RESOURCE_PATH 列表以包含更多静态资源路径模式
- 实现 isMatchAnt 方法用于 Ant 风格路径匹配
- 添加多个新的静态资源排除路径,如 /actuator/**、/favicon.ico 等
- 修改访问日志工具类使用新的 Ant 路径匹配方法
2025-10-20 16:07:31 +08:00
吴泽威
f1937d3968 refactor(web): 移除 SpringDoc 全局响应处理器及相关依赖
- 删除 ApiDocGlobalResponseHandler 类及其相关配置
- 移除对 continew-starter-api-doc 模块的依赖
- 替换为 swagger-annotations-jakarta 依赖
- 清理 GlobalResponseAutoConfiguration 中的条件加载逻辑
- 更新 pom.xml 中的注释说明
2025-10-20 16:06:45 +08:00
吴泽威
a60d452aee refactor(api-doc): 将默认API文档UI从Knife4j替换为NextDoc4j
- 移除对Knife4j相关依赖和资源配置
- 新增NextDoc4j依赖及版本管理
- 重构BaseEnumParameterHandler以继承ModelResolver
- 添加ObjectMapper注入支持枚举参数解析
- 调整资源处理器配置移除旧版静态资源配置
- 删除自定义OpenApiHandler及相关构建器配置
- 更新YAML配置文件启用NextDoc4j并移除Knife4j设置
2025-10-20 16:05:41 +08:00
33748f7cec feat(core): 添加 ContiNew Starter 版本信息类 2025-10-19 20:34:46 +08:00
e8d2bfddcf build(dependencies): 更新项目依赖版本
- spring-boot 3.3.12 => 3.4.10
- spring-cloud 2023.0.5 => 2024.0.2
- redisson 3.49.0 => 3.52.0
- mybatis-plus 3.5.12 => 3.5.14
- mybatis-flex 1.10.9 => 1.11.3
- cosid 2.13.0 => 2.13.3
- snail-job 1.5.0 => 1.8.0
- fastexcel 1.2.0 => 1.3.0
- aws-sdk-v1 1.12.783 => 1.12.792
- aws-sdk 2.31.63 => 2.35.10
- aws-crt 0.38.5 => 0.39.3
- thumbnails 0.4.20 => 0.4.21
- spel-validator 0.5.2-beta => 0.6.0-beta
- ip2region 3.3.6 => 3.4.7
- hutool 5.8.38 => 5.8.41
- snakeyaml 2.4 => 2.5
- nashorn 15.6 => 15.7
- commons-io 2.17.0 => 2.20.0
- commons-compress 1.26.0 => 1.28.0
- flatten 1.7.0 => 1.7.3
- spotless 2.44.3 => 3.0.0
- sonar 3.11.0.3922 => 5.2.0.4988
2025-10-18 22:17:04 +08:00
jasmine
a4fe07bc63 perf(extension/datapermission): DataPermission 注解增加缓存处理
* 缓存Mapper接口方法上携带@DataPermission的值。
2025-10-18 10:18:01 +00:00
14eed577d6 build: 更新项目版本号至2.15.0-SNAPSHOT 2025-10-18 18:16:19 +08:00
14 changed files with 191 additions and 486 deletions

View File

@@ -22,10 +22,11 @@
<artifactId>continew-starter-core</artifactId>
</dependency>
<!-- Knife4j前身是 swagger-bootstrap-ui集 Swagger2 和 OpenAPI3 为一体的增强解决方案) -->
<!--NextDoc4j (现代化 API 文档 UI 工具 全面替代 Swagger UI) -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<groupId>top.nextdoc4j</groupId>
<artifactId>nextdoc4j-springboot3-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -17,6 +17,7 @@
package top.continew.starter.apidoc.autoconfigure;
import cn.hutool.core.map.MapUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
@@ -29,32 +30,19 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.configuration.SpringDocConfiguration;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.providers.JavadocProvider;
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import top.continew.starter.apidoc.handler.BaseEnumParameterHandler;
import top.continew.starter.apidoc.handler.OpenApiHandler;
import top.continew.starter.core.autoconfigure.application.ApplicationProperties;
import top.continew.starter.core.util.CollUtils;
import top.continew.starter.core.util.GeneralPropertySourceFactory;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* API 文档自动配置
@@ -73,10 +61,6 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/");
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
}
/**
@@ -101,12 +85,10 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
Components components = properties.getComponents();
if (components != null) {
openApi.components(components);
// 鉴权配置
Map<String, SecurityScheme> securitySchemeMap = components.getSecuritySchemes();
if (MapUtil.isNotEmpty(securitySchemeMap)) {
SecurityRequirement securityRequirement = new SecurityRequirement();
List<String> list = CollUtils.mapToList(securitySchemeMap.values(), SecurityScheme::getName);
list.forEach(securityRequirement::addList);
securitySchemeMap.keySet().forEach(securityRequirement::addList);
openApi.addSecurityItem(securityRequirement);
}
}
@@ -121,16 +103,14 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
public GlobalOpenApiCustomizer globalOpenApiCustomizer(SpringDocExtensionProperties properties) {
return openApi -> {
if (openApi.getPaths() != null) {
openApi.getPaths().forEach((s, pathItem) -> {
openApi.getPaths().forEach((path, pathItem) -> {
// 为所有接口添加鉴权
Components components = properties.getComponents();
if (components != null && MapUtil.isNotEmpty(components.getSecuritySchemes())) {
Map<String, SecurityScheme> securitySchemeMap = components.getSecuritySchemes();
pathItem.readOperations().forEach(operation -> {
SecurityRequirement securityRequirement = new SecurityRequirement();
List<String> list = CollUtils.mapToList(securitySchemeMap
.values(), SecurityScheme::getName);
list.forEach(securityRequirement::addList);
securitySchemeMap.keySet().forEach(securityRequirement::addList);
operation.addSecurityItem(securityRequirement);
});
}
@@ -139,20 +119,6 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
};
}
/**
* 自定义 OpenApi 处理器
*/
@Bean
public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties,
PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers,
Optional<JavadocProvider> javadocProvider) {
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
}
/**
* 自定义 BaseEnum 枚举参数配置(针对实现了 BaseEnum 的枚举,优化其枚举值和描述展示)
*
@@ -160,8 +126,8 @@ public class SpringDocAutoConfiguration implements WebMvcConfigurer {
* @since 2.4.0
*/
@Bean
public BaseEnumParameterHandler customParameterCustomizer() {
return new BaseEnumParameterHandler();
public BaseEnumParameterHandler customParameterCustomizer(ObjectMapper mapper) {
return new BaseEnumParameterHandler(mapper);
}
@PostConstruct

View File

@@ -18,19 +18,23 @@ package top.continew.starter.apidoc.handler;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ClassUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.SimpleType;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverterContext;
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.springdoc.core.customizers.ParameterCustomizer;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springframework.core.MethodParameter;
import top.continew.starter.apidoc.util.ApiDocUtils;
import top.continew.starter.core.enums.BaseEnum;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
@@ -42,7 +46,11 @@ import java.util.List;
* @author echo
* @since 2.5.2
*/
public class BaseEnumParameterHandler implements ParameterCustomizer, PropertyCustomizer {
public class BaseEnumParameterHandler extends ModelResolver implements ParameterCustomizer {
public BaseEnumParameterHandler(ObjectMapper mapper) {
super(mapper);
}
@Override
public Parameter customize(Parameter parameterModel, MethodParameter methodParameter) {
@@ -62,16 +70,18 @@ public class BaseEnumParameterHandler implements ParameterCustomizer, PropertyCu
}
@Override
public Schema customize(Schema schema, AnnotatedType type) {
public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
Schema resolve = super.resolve(type, context, chain);
Class<?> rawClass = resolveRawClass(type.getType());
// 判断是否为 BaseEnum 的子类型
if (!ClassUtil.isAssignable(BaseEnum.class, rawClass)) {
return schema;
return resolve;
}
// 自定义参数描述并封装参数配置
configureSchema(schema, rawClass);
schema.setDescription(appendEnumDescription(schema.getDescription(), rawClass));
return schema;
configureSchema(resolve, rawClass);
resolve.setDescription(appendEnumDescription(resolve.getDescription(), rawClass));
return resolve;
}
/**

View File

@@ -1,275 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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 top.continew.starter.apidoc.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import io.swagger.v3.core.jackson.TypeNameResolver;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.annotations.tags.Tags;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.providers.JavadocProvider;
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.method.HandlerMethod;
import top.continew.starter.core.util.CollUtils;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 自定义 OpenApi 处理器(对源码功能进行修改,增强使用)
*
* @author echo
* @since 2.4.0
*/
@SuppressWarnings("all")
public class OpenApiHandler extends OpenAPIService {
/**
* The Basic error controller.
*/
private static Class<?> basicErrorController;
/**
* The Security parser.
*/
private final SecurityService securityParser;
/**
* The Mappings map.
*/
private final Map<String, Object> mappingsMap = new HashMap<>();
/**
* The Springdoc tags.
*/
private final Map<HandlerMethod, Tag> springdocTags = new HashMap<>();
/**
* The Open api builder customisers.
*/
private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;
/**
* The server base URL customisers.
*/
private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;
/**
* The Spring doc config properties.
*/
private final SpringDocConfigProperties springDocConfigProperties;
/**
* The Cached open api map.
*/
private final Map<String, OpenAPI> cachedOpenAPI = new HashMap<>();
/**
* The Property resolver utils.
*/
private final PropertyResolverUtils propertyResolverUtils;
/**
* The javadoc provider.
*/
private final Optional<JavadocProvider> javadocProvider;
/**
* The Context.
*/
private ApplicationContext context;
/**
* The Open api.
*/
private OpenAPI openAPI;
/**
* The Is servers present.
*/
private boolean isServersPresent;
/**
* The Server base url.
*/
private String serverBaseUrl;
/**
* Instantiates a new Open api builder.
*
* @param openAPI the open api
* @param securityParser the security parser
* @param springDocConfigProperties the spring doc config properties
* @param propertyResolverUtils the property resolver utils
* @param openApiBuilderCustomizers the open api builder customisers
* @param serverBaseUrlCustomizers the server base url customizers
* @param javadocProvider the javadoc provider
*/
public OpenApiHandler(Optional<OpenAPI> openAPI,
SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties,
PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
Optional<JavadocProvider> javadocProvider) {
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
if (openAPI.isPresent()) {
this.openAPI = openAPI.get();
if (this.openAPI.getComponents() == null) {
this.openAPI.setComponents(new Components());
}
if (this.openAPI.getPaths() == null) {
this.openAPI.setPaths(new Paths());
}
if (CollUtil.isNotEmpty(this.openAPI.getServers())) {
this.isServersPresent = true;
}
}
this.propertyResolverUtils = propertyResolverUtils;
this.securityParser = securityParser;
this.springDocConfigProperties = springDocConfigProperties;
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
this.javadocProvider = javadocProvider;
if (springDocConfigProperties.isUseFqn()) {
TypeNameResolver.std.setUseFqn(true);
}
}
@Override
public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {
Set<Tag> tags = new HashSet<>();
Set<String> tagsStr = new HashSet<>();
buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);
buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);
if (CollUtil.isNotEmpty(tagsStr)) {
tagsStr = CollUtils.mapToSet(tagsStr, str -> propertyResolverUtils.resolve(str, locale));
}
if (springdocTags.containsKey(handlerMethod)) {
Tag tag = springdocTags.get(handlerMethod);
tagsStr.add(tag.getName());
if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
openAPI.addTagsItem(tag);
}
}
if (CollUtil.isNotEmpty(tagsStr)) {
if (CollUtil.isEmpty(operation.getTags())) {
operation.setTags(new ArrayList<>(tagsStr));
} else {
Set<String> operationTagsSet = new HashSet<>(operation.getTags());
operationTagsSet.addAll(tagsStr);
operation.getTags().clear();
operation.getTags().addAll(operationTagsSet);
}
}
if (isAutoTagClasses(operation)) {
if (javadocProvider.isPresent()) {
String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType());
if (StringUtils.isNotBlank(description)) {
Tag tag = new Tag();
// 自定义部分 修改使用java注释当tag名
List<String> list = IoUtil.readLines(new StringReader(description), new ArrayList<>());
// tag.setName(tagAutoName);
tag.setName(list.get(0));
operation.addTagsItem(list.get(0));
tag.setDescription(description);
if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
openAPI.addTagsItem(tag);
}
}
} else {
String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName());
operation.addTagsItem(tagAutoName);
}
}
if (CollUtil.isNotEmpty(tags)) {
// Existing tags
List<Tag> openApiTags = openAPI.getTags();
if (CollUtil.isNotEmpty(openApiTags)) {
tags.addAll(openApiTags);
}
openAPI.setTags(new ArrayList<>(tags));
}
// Handle SecurityRequirement at operation level
io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser
.getSecurityRequirements(handlerMethod);
if (securityRequirements != null) {
if (securityRequirements.length == 0) {
operation.setSecurity(Collections.emptyList());
} else {
securityParser.buildSecurityRequirement(securityRequirements, operation);
}
}
return operation;
}
private void buildTagsFromMethod(Method method, Set<Tag> tags, Set<String> tagsStr, Locale locale) {
// method tags
Set<Tags> tagsSet = AnnotatedElementUtils.findAllMergedAnnotations(method, Tags.class);
Set<io.swagger.v3.oas.annotations.tags.Tag> methodTags = tagsSet.stream()
.flatMap(x -> Stream.of(x.value()))
.collect(Collectors.toSet());
methodTags.addAll(AnnotatedElementUtils
.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
if (CollUtil.isNotEmpty(methodTags)) {
tagsStr.addAll(CollUtils.mapToSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale)));
List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
addTags(allTags, tags, locale);
}
}
private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<Tag> tags, Locale locale) {
Optional<Set<Tag>> optionalTagSet = AnnotationsUtils.getTags(sourceTags
.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);
optionalTagSet.ifPresent(tagsSet -> {
tagsSet.forEach(tag -> {
tag.name(propertyResolverUtils.resolve(tag.getName(), locale));
tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));
if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName()))) {
tags.add(tag);
}
});
});
}
}

View File

@@ -10,8 +10,5 @@ springdoc:
enabled: ${springdoc.swagger-ui.enabled}
path: /v3/api-docs
## 接口文档增强配置
knife4j:
enable: true
setting:
language: zh_cn
swagger-model-name: 实体类列表
nextdoc4j:
enabled: true

View File

@@ -13,7 +13,7 @@
<description>ContiNew Starter BOM</description>
<properties>
<revision>2.14.0</revision>
<revision>2.15.0-SNAPSHOT</revision>
</properties>
<dependencyManagement>

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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 top.continew.starter.core;
/**
* ContiNew Starter 版本
*
* @author Charles7c
* @since 2.15.0
*/
public final class ContiNewStarterVersion {
private ContiNewStarterVersion() {
}
/**
* Return the full version string of the present ContiNew Starter codebase.
*
* @return the version of ContiNew Starter
*/
public static String getVersion() {
return "2.15.0";
}
}

View File

@@ -23,6 +23,7 @@ import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.http.server.PathContainer;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
@@ -50,6 +51,8 @@ public class SpringWebUtils {
private SpringWebUtils() {
}
private static final AntPathMatcher matcher = new AntPathMatcher();
/**
* 路径是否匹配
*
@@ -88,6 +91,30 @@ public class SpringWebUtils {
return pathPattern.matches(pathContainer);
}
/**
* 路径是否匹配 - Ant 风格
*
* @param path 路径
* @param pattern 匹配模式
* @return 是否匹配
* @since 2.4.0
*/
public static boolean isMatchAnt(String path, String pattern) {
return matcher.match(pattern, path);
}
/**
* 路径是否匹配 - Ant 风格
*
* @param path 路径
* @param patterns 匹配模式列表
* @return 是否匹配
* @since 2.6.0
*/
public static boolean isMatchAnt(String path, List<String> patterns) {
return patterns.stream().anyMatch(pattern -> isMatchAnt(path, pattern));
}
/**
* 取消注册静态资源映射
*

View File

@@ -14,29 +14,29 @@
<properties>
<!-- Project Version -->
<revision>2.14.0</revision>
<revision>2.15.0-SNAPSHOT</revision>
<!-- Core Framework Versions -->
<spring-boot.version>3.3.12</spring-boot.version>
<spring-cloud.version>2023.0.5</spring-cloud.version>
<spring-boot.version>3.4.10</spring-boot.version>
<spring-cloud.version>2024.0.2</spring-cloud.version>
<!-- Cache and Storage Versions -->
<redisson.version>3.49.0</redisson.version>
<redisson.version>3.52.0</redisson.version>
<jetcache.version>2.7.8</jetcache.version>
<!-- Security and Authentication Versions -->
<sa-token.version>1.44.0</sa-token.version>
<just-auth.version>1.16.7</just-auth.version>
<justauth.version>1.16.7</justauth.version>
<!-- Database and ORM Versions -->
<mybatis-plus.version>3.5.12</mybatis-plus.version>
<mybatis-flex.version>1.10.9</mybatis-flex.version>
<mybatis-plus.version>3.5.14</mybatis-plus.version>
<mybatis-flex.version>1.11.3</mybatis-flex.version>
<dynamic-datasource.version>4.3.1</dynamic-datasource.version>
<p6spy.version>3.9.1</p6spy.version>
<!-- ID Generator and Job Scheduler Versions -->
<cosid.version>2.13.0</cosid.version>
<snail-job.version>1.5.0</snail-job.version>
<cosid.version>2.13.3</cosid.version>
<snail-job.version>1.8.0</snail-job.version>
<!-- Messaging and Notification Versions -->
<sms4j.version>3.3.5</sms4j.version>
@@ -46,23 +46,24 @@
<easy-captcha.version>1.6.2</easy-captcha.version>
<!-- Excel Processing Versions -->
<fastexcel.version>1.2.0</fastexcel.version>
<fastexcel.version>1.3.0</fastexcel.version>
<poi.version>5.4.1</poi.version>
<!-- File Storage Versions -->
<x-file-storage.version>2.2.1</x-file-storage.version>
<aws-s3-v1.version>1.12.783</aws-s3-v1.version>
<aws-sdk.version>2.31.63</aws-sdk.version>
<aws-crt.version>0.38.5</aws-crt.version>
<thumbnails.version>0.4.20</thumbnails.version>
<aws-sdk-v1.version>1.12.792</aws-sdk-v1.version>
<aws-sdk.version>2.35.10</aws-sdk.version>
<aws-crt.version>0.39.3</aws-crt.version>
<thumbnails.version>0.4.21</thumbnails.version>
<!-- Validation and Response Processing Versions -->
<graceful-response.version>5.0.5-boot3</graceful-response.version>
<spel-validator.version>0.5.2-beta</spel-validator.version>
<spel-validator.version>0.6.0-beta</spel-validator.version>
<crane4j.version>2.9.0</crane4j.version>
<!-- API Documentation Versions -->
<knife4j.version>4.5.0</knife4j.version>
<nextdoc4j.version>1.1.0</nextdoc4j.version>
<swagger-annotations.version>2.2.36</swagger-annotations.version>
<!-- Tracing and Logging Versions -->
<tlog.version>1.5.2</tlog.version>
@@ -74,20 +75,20 @@
<!-- HTTP Client and Utilities Versions -->
<okhttp.version>4.12.0</okhttp.version>
<ttl.version>2.14.5</ttl.version>
<ip2region.version>3.3.6</ip2region.version>
<hutool.version>5.8.38</hutool.version>
<snakeyaml.version>2.4</snakeyaml.version>
<nashorn.version>15.6</nashorn.version>
<ip2region.version>3.4.7</ip2region.version>
<hutool.version>5.8.41</hutool.version>
<snakeyaml.version>2.5</snakeyaml.version>
<nashorn.version>15.7</nashorn.version>
<!-- Security Vulnerability Fix Versions -->
<commons-beanutils.version>1.11.0</commons-beanutils.version>
<commons-io.version>2.17.0</commons-io.version>
<commons-compress.version>1.26.0</commons-compress.version>
<commons-io.version>2.20.0</commons-io.version>
<commons-compress.version>1.28.0</commons-compress.version>
<!-- Maven Plugin Versions -->
<flatten.version>1.7.0</flatten.version>
<spotless.version>2.44.3</spotless.version>
<sonar.version>3.11.0.3922</sonar.version>
<flatten.version>1.7.3</flatten.version>
<spotless.version>3.0.0</spotless.version>
<sonar.version>5.2.0.4988</sonar.version>
</properties>
<dependencyManagement>
@@ -126,15 +127,6 @@
<scope>import</scope>
</dependency>
<!-- CosId通用、灵活、高性能的分布式 ID 生成器) -->
<dependency>
<groupId>me.ahoo.cosid</groupId>
<artifactId>cosid-bom</artifactId>
<version>${cosid.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Sa-Token轻量级 Java 权限认证框架,让鉴权变得简单、优雅) -->
<dependency>
<groupId>cn.dev33</groupId>
@@ -148,7 +140,7 @@
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>${just-auth.version}</version>
<version>${justauth.version}</version>
</dependency>
<dependency>
<groupId>com.xkcoding.justauth</groupId>
@@ -194,6 +186,15 @@
<version>${p6spy.version}</version>
</dependency>
<!-- CosId通用、灵活、高性能的分布式 ID 生成器) -->
<dependency>
<groupId>me.ahoo.cosid</groupId>
<artifactId>cosid-bom</artifactId>
<version>${cosid.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SnailJob灵活可靠和快速的分布式任务重试和分布式任务调度平台 -->
<dependency>
<groupId>com.aizuda</groupId>
@@ -232,13 +233,6 @@
<version>${easy-captcha.version}</version>
</dependency>
<!-- JS 引擎(纯编译的 JavaScript 引擎) -->
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>${nashorn.version}</version>
</dependency>
<!-- FastExcel基于 Java 的快速、简洁、解决大文件内存溢出的 Excel 处理工具) -->
<dependency>
<groupId>cn.idev.excel</groupId>
@@ -285,7 +279,7 @@
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>${aws-s3-v1.version}</version>
<version>${aws-sdk-v1.version}</version>
</dependency>
<!-- Amazon SDK -->
@@ -332,11 +326,11 @@
<version>${crane4j.version}</version>
</dependency>
<!-- Knife4j前身是 swagger-bootstrap-ui集 Swagger2 和 OpenAPI3 为一体的增强解决方案) -->
<!--NextDoc4j (现代化 API 文档 UI 工具 全面替代 Swagger UI) -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-dependencies</artifactId>
<version>${knife4j.version}</version>
<groupId>top.nextdoc4j</groupId>
<artifactId>nextdoc4j-bom</artifactId>
<version>${nextdoc4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -403,6 +397,13 @@
<version>${snakeyaml.version}</version>
</dependency>
<!-- JS 引擎(纯编译的 JavaScript 引擎) -->
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>${nashorn.version}</version>
</dependency>
<!-- 解决部分传递依赖漏洞问题 -->
<dependency>
<groupId>commons-beanutils</groupId>
@@ -422,6 +423,13 @@
<version>${commons-compress.version}</version>
</dependency>
<!-- Swagger 注解 -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>${swagger-annotations.version}</version>
</dependency>
<!-- ContiNew Starter 依赖 -->
<dependency>
<groupId>top.continew.starter</groupId>

View File

@@ -16,20 +16,9 @@
package top.continew.starter.extension.datapermission.handler;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.spring.SpringUtil;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
@@ -42,6 +31,8 @@ import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.data.enums.DatabaseType;
import top.continew.starter.data.util.MetaUtils;
@@ -49,9 +40,16 @@ import top.continew.starter.extension.datapermission.annotation.DataPermission;
import top.continew.starter.extension.datapermission.constant.DataPermissionConstants;
import top.continew.starter.extension.datapermission.enums.DataScope;
import top.continew.starter.extension.datapermission.exception.DataPermissionException;
import top.continew.starter.extension.datapermission.provider.DataPermissionUserDataProvider;
import top.continew.starter.extension.datapermission.model.RoleData;
import top.continew.starter.extension.datapermission.model.UserData;
import top.continew.starter.extension.datapermission.provider.DataPermissionUserDataProvider;
import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 默认数据权限处理器
@@ -64,6 +62,10 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
private static final Logger log = LoggerFactory.getLogger(DefaultDataPermissionHandler.class);
private final DataPermissionUserDataProvider dataPermissionUserDataProvider;
/**
* Mapper类中所有方法数据权限注解缓存
*/
private final Map<String, Map<String, DataPermission>> annotationCache = new ConcurrentHashMap<>();
public DefaultDataPermissionHandler(DataPermissionUserDataProvider dataPermissionUserDataProvider) {
this.dataPermissionUserDataProvider = dataPermissionUserDataProvider;
@@ -98,19 +100,36 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
String className = mappedStatementId.substring(0, lastDotIndex);
String methodName = mappedStatementId.substring(lastDotIndex + 1);
// 先根据类名从缓存获取如果methodAnnotations不为空则说明该类中的所有方法都已缓存 只是值为null。
Map<String, DataPermission> methodAnnotations = annotationCache.get(className);
if (methodAnnotations != null) {
// methodName 可能是 ** 或者 **_COUNT
return methodAnnotations.getOrDefault(methodName, methodAnnotations
.get(methodName + DataPermissionConstants.COUNT_METHOD_SUFFIX));
}
// 缓存未命中,执行反射操作
Class<?> clazz = Class.forName(className);
Method[] methods = clazz.getMethods();
// 创建新的缓存映射
Map<String, DataPermission> newMethodAnnotations = new ConcurrentHashMap<>();
// 缓存所有带@DataPermission注解的方法
for (Method method : methods) {
String name = method.getName();
if (CharSequenceUtil.equalsAny(methodName, name, name + DataPermissionConstants.COUNT_METHOD_SUFFIX)) {
return method.getAnnotation(DataPermission.class);
DataPermission annotation = method.getAnnotation(DataPermission.class);
if (annotation != null) {
newMethodAnnotations.put(name, annotation);
}
}
// 存入缓存
annotationCache.put(className, newMethodAnnotations);
return newMethodAnnotations.get(methodName);
} catch (ClassNotFoundException e) {
throw DataPermissionException.methodNotFound(mappedStatementId);
}
return null;
}
/**

View File

@@ -40,10 +40,10 @@ public class AccessLogUtils {
}
/**
* 资源路径 - doc 路径
* 静态资源路径模式
*/
private static final List<String> RESOURCE_PATH = List
.of("/doc/**", "/v2/api-docs/**", "/v3/api-docs/**", "/webjars/**", "/swagger-resources/**", "/swagger-ui.html");
.of("/**/doc/**", "/**/doc.html", "/**/nextdoc/**", "/**/v*/api-docs/**", "/**/api-docs/**", "/**/swagger-ui/**", "/**/swagger-ui.html", "/**/swagger-resources/**", "/**/webjars/**", "/**/favicon.ico", "/**/static/**", "/**/assets/**", "/**/actuator/**", "/error", "/health");
/**
* 获取参数信息
@@ -91,7 +91,7 @@ public class AccessLogUtils {
public static boolean exclusionPath(LogProperties properties, String path) {
// 放行路由配置的排除检查
return properties.isMatch(path) || RESOURCE_PATH.stream()
.anyMatch(resourcePath -> SpringWebUtils.isMatch(path, resourcePath));
.anyMatch(resourcePath -> SpringWebUtils.isMatchAnt(path, resourcePath));
}
/**

View File

@@ -22,11 +22,10 @@
<artifactId>continew-starter-json-jackson</artifactId>
</dependency>
<!-- API 文档模块 -->
<!-- Swagger 注解 -->
<dependency>
<groupId>top.continew.starter</groupId>
<artifactId>continew-starter-api-doc</artifactId>
<optional>true</optional>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
</dependency>
<!-- Spring Boot Web提供 Spring MVC Web 开发能力,默认内置 Tomcat 服务器) -->

View File

@@ -1,71 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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 top.continew.starter.web.autoconfigure.response;
import cn.hutool.core.util.ClassUtil;
import org.apache.commons.lang3.reflect.TypeUtils;
import org.springdoc.core.parsers.ReturnTypeParser;
import org.springframework.core.MethodParameter;
import top.continew.starter.apidoc.util.ApiDocUtils;
import java.lang.reflect.Type;
/**
* SpringDoc 全局响应处理器
* <p>
* 接口文档全局添加响应格式 {@link com.feiniaojin.gracefulresponse.data.Response}
* </p>
*
* @author echo
* @since 2.5.2
*/
public class ApiDocGlobalResponseHandler implements ReturnTypeParser {
private final GlobalResponseProperties globalResponseProperties;
private final Class<Object> responseClass;
public ApiDocGlobalResponseHandler(GlobalResponseProperties globalResponseProperties) {
this.globalResponseProperties = globalResponseProperties;
this.responseClass = ClassUtil.loadClass(globalResponseProperties.getResponseClassFullName());
}
/**
* 获取返回类型
*
* @param methodParameter 方法参数
* @return {@link Type }
*/
@Override
public Type getReturnType(MethodParameter methodParameter) {
// 获取返回类型
Type returnType = ReturnTypeParser.super.getReturnType(methodParameter);
// 判断是否具有 RestController 注解
if (!ApiDocUtils.hasRestControllerAnnotation(methodParameter.getContainingClass())) {
return returnType;
}
// 如果为响应类型,则直接返回
if (returnType.getTypeName().contains(globalResponseProperties.getResponseClassFullName())) {
return returnType;
}
// 如果是 void类型则返回 R<Void>
if (returnType == void.class || returnType == Void.class) {
return TypeUtils.parameterize(responseClass, Void.class);
}
// 返回 R<T>
return TypeUtils.parameterize(responseClass, returnType);
}
}

View File

@@ -28,8 +28,6 @@ import com.feiniaojin.gracefulresponse.defaults.DefaultResponseStatusFactoryImpl
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.parsers.ReturnTypeParser;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -223,18 +221,6 @@ public class GlobalResponseAutoConfiguration {
return new AdviceSupport();
}
/**
* SpringDoc 全局响应处理器
*
* @return {@link ApiDocGlobalResponseHandler }
*/
@Bean
@ConditionalOnClass(ReturnTypeParser.class)
@ConditionalOnMissingBean
public ApiDocGlobalResponseHandler apiDocGlobalResponseHandler() {
return new ApiDocGlobalResponseHandler(globalResponseProperties);
}
@PostConstruct
public void postConstruct() {
log.debug("[ContiNew Starter] - Auto Configuration 'Web-Global Response' completed initialization.");