feat: 升级springboot3

This commit is contained in:
2025-10-30 16:25:30 +08:00
parent 7321a9e172
commit 2a72c5f783
68 changed files with 242 additions and 436 deletions
@@ -19,7 +19,7 @@ package cn.odboy.base;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotNull;
import jakarta.validation.constraints.NotNull;
/**
* 通用参数
@@ -21,8 +21,8 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
/**
@@ -17,10 +17,9 @@
package cn.odboy.framework.context;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
@@ -16,139 +16,33 @@
package cn.odboy.framework.doc;
import cn.odboy.constant.SystemConst;
import cn.odboy.framework.properties.AppProperties;
import cn.odboy.util.CsAnonTagUtil;
import org.springframework.beans.BeansException;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* api页面 /doc.html
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${server.servlet.context-path:}")
private String apiPath;
@Autowired
private AppProperties properties;
@Autowired
private ApplicationContext applicationContext;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2).enable(properties.getSwagger().getEnabled())
.pathMapping("/")
.apiInfo(apiInfo())
.select()
.paths(PathSelectors.regex("^(?!/error).*"))
.paths(PathSelectors.any())
.build()
//添加登陆认证
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().description("一个简单且易上手的自动化运维平台")
.title("CuteJava 接口文档")
.version("1.4.1")
.build();
}
private List<SecurityScheme> securitySchemes() {
//设置请求头信息
List<SecurityScheme> securitySchemes = new ArrayList<>();
ApiKey apiKey = new ApiKey(SystemConst.HEADER_NAME, SystemConst.HEADER_NAME, "header");
securitySchemes.add(apiKey);
return securitySchemes;
}
private List<SecurityContext> securityContexts() {
//设置需要登录认证的路径
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(getContextByPath());
return securityContexts;
}
private SecurityContext getContextByPath() {
Set<String> urls = CsAnonTagUtil.getAllAnonymousUrl(applicationContext);
urls = urls.stream().filter(url -> !"/".equals(url)).collect(Collectors.toSet());
String regExp = "^(?!" + apiPath + String.join("|" + apiPath, urls) + ").*$";
return SecurityContext.builder().securityReferences(defaultAuth()).operationSelector(o -> o.requestMappingPattern()
// 排除不需要认证的接口
.matches(regExp)).build();
}
private List<SecurityReference> defaultAuth() {
List<SecurityReference> securityReferences = new ArrayList<>();
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
securityReferences.add(new SecurityReference(SystemConst.HEADER_NAME, authorizationScopes));
return securityReferences;
}
/**
* 解决Springfox与SpringBoot集成后, WebMvcRequestHandlerProvider和WebFluxRequestHandlerProvider冲突问题
*
* @return /
*/
@Bean
@SuppressWarnings({"unchecked", "all"})
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> filteredMappings =
mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());
mappings.clear();
mappings.addAll(filteredMappings);
}
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
if (field != null) {
field.setAccessible(true);
try {
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Failed to access handlerMappings field", e);
}
}
return Collections.emptyList();
}
};
public OpenAPI customOpenAPI() {
// 添加开关控制
if (!properties.getSwagger().getEnabled()) {
return new OpenAPI();
}
return new OpenAPI().components(new Components().addSecuritySchemes("Authorization",
new SecurityScheme().name("Authorization").type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER)))
.info(new Info().title("CuteJava 接口文档").version("1.4.1").description("一个简单且易上手的自动化运维平台"))
.addSecurityItem(new SecurityRequirement().addList("Authorization"));
}
}
@@ -16,8 +16,8 @@
package cn.odboy.framework.mybatisplus.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -26,9 +26,9 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Configuration
@@ -27,10 +27,9 @@ import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jakarta.validation.constraints.NotBlank;
import lombok.extern.slf4j.Slf4j;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import jakarta.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
@@ -21,10 +21,10 @@ import com.alibaba.fastjson2.JSONFactory;
import com.alibaba.fastjson2.JSONWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.MurmurHash3;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
@@ -49,7 +49,7 @@ import java.util.Map;
@Configuration
@EnableCaching
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfiguration extends CachingConfigurerSupport {
public class RedisConfiguration {
/**
* 自动识别json对象白名单配置(仅允许解析的包名, 范围越小越安全)<br/> 未配置可能导致, 登录失败, 反复登录等问题
*/
@@ -69,7 +69,8 @@ public class RedisConfiguration extends CachingConfigurerSupport {
public RedisCacheConfiguration redisCacheConfiguration() {
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(2));
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
.entryTtl(Duration.ofHours(2));
return configuration;
}
@@ -109,7 +110,6 @@ public class RedisConfiguration extends CachingConfigurerSupport {
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
Map<String, Object> container = new HashMap<>(8);
@@ -132,7 +132,6 @@ public class RedisConfiguration extends CachingConfigurerSupport {
}
@Bean
@SuppressWarnings({"unchecked", "all"})
public CacheErrorHandler errorHandler() {
return new SimpleCacheErrorHandler() {
@Override
@@ -1,43 +0,0 @@
/*
* Copyright 2021-2025 Odboy
*
* 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 cn.odboy.framework.websocket.config;
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
/**
* 解决启动io.undertow.websockets.jsr UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used的警告
*
* @date 2024-06-07
*/
@Component
public class WsFactory implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
@Override
public void customize(UndertowServletWebServerFactory factory) {
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
deploymentInfo.addServletContextAttribute(
"io.undertow.websockets.jsr.WebSocketDeploymentInfo",
webSocketDeploymentInfo);
});
}
}
@@ -17,13 +17,12 @@
package cn.odboy.framework.websocket.context;
import com.alibaba.fastjson2.JSON;
import jakarta.websocket.server.ServerEndpoint;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
@@ -23,7 +23,7 @@ import lombok.experimental.UtilityClass;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@@ -42,8 +42,7 @@ public class CsAnonTagUtil {
* @return /
*/
public static Map<String, Set<String>> getAnonymousUrl(final ApplicationContext applicationContext) {
RequestMappingHandlerMapping requestMappingHandlerMapping =
(RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
RequestMappingHandlerMapping requestMappingHandlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
Map<String, java.util.Set<String>> anonymousUrls = new HashMap<>(8);
// 获取匿名标记
@@ -58,29 +57,27 @@ public class CsAnonTagUtil {
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess && infoEntry.getKey() != null) {
List<RequestMethod> requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
RequestMethodEnum request = RequestMethodEnum.find(requestMethods.isEmpty() ?
RequestMethodEnum.ALL.getType() :
requestMethods.get(0).name());
PatternsRequestCondition patternsCondition = infoEntry.getKey().getPatternsCondition();
if (patternsCondition != null) {
RequestMethodEnum request = RequestMethodEnum.find(requestMethods.isEmpty() ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name());
PathPatternsRequestCondition pathPatternsCondition = infoEntry.getKey().getPathPatternsCondition();
if (pathPatternsCondition != null) {
switch (Objects.requireNonNull(request)) {
case GET:
get.addAll(patternsCondition.getPatterns());
get.addAll(pathPatternsCondition.getPatternValues());
break;
case POST:
post.addAll(patternsCondition.getPatterns());
post.addAll(pathPatternsCondition.getPatternValues());
break;
case PUT:
put.addAll(patternsCondition.getPatterns());
put.addAll(pathPatternsCondition.getPatternValues());
break;
case PATCH:
patch.addAll(patternsCondition.getPatterns());
patch.addAll(pathPatternsCondition.getPatternValues());
break;
case DELETE:
delete.addAll(patternsCondition.getPatterns());
delete.addAll(pathPatternsCondition.getPatternValues());
break;
default:
all.addAll(patternsCondition.getPatterns());
all.addAll(pathPatternsCondition.getPatternValues());
break;
}
}
@@ -21,7 +21,7 @@ import cn.hutool.http.useragent.UserAgentUtil;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -29,9 +29,9 @@ import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.security.MessageDigest;
@@ -19,11 +19,11 @@ package cn.odboy.util;
import cn.odboy.framework.exception.BadRequestException;
import lombok.experimental.UtilityClass;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.groups.Default;
import java.util.Set;
import java.util.stream.Collectors;