fix: 修订、修正 Spring Boot 3.X 无法编译启动的问题

pull/70/head
oc 2023-11-28 10:54:41 +08:00
parent e1faae14cc
commit f4fada5ad6
35 changed files with 420 additions and 337 deletions

View File

@ -6,5 +6,5 @@ charset = utf-8
[*.{groovy,java,kt,kts,xml,xsd}]
indent_style = space
indent_size = 4
continuation_indent_size = 8
continuation_indent_size = 4
end_of_line = lf

View File

@ -1,8 +1,13 @@
package cn.iocoder.yudao.framework.common.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({
ElementType.METHOD,

View File

@ -2,22 +2,18 @@ package cn.iocoder.yudao.framework.common.validation;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.PhoneUtil;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class TelephoneValidator implements ConstraintValidator<Telephone, String> {
@Override
public void initialize(Telephone annotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// 如果手机号为空,默认不校验,即校验通过
if (CharSequenceUtil.isEmpty(value)) {
return true;
}
// 校验手机
return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value);
}

View File

@ -12,9 +12,12 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
@ -35,11 +38,12 @@ import java.util.Set;
* @author
*/
@AutoConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableMethodSecurity(securedEnabled = true)
public class YudaoWebSecurityConfigurerAdapter {
@Resource
private WebProperties webProperties;
@Resource
private SecurityProperties securityProperties;
@ -48,11 +52,13 @@ public class YudaoWebSecurityConfigurerAdapter {
*/
@Resource
private AuthenticationEntryPoint authenticationEntryPoint;
/**
* Bean
*/
@Resource
private AccessDeniedHandler accessDeniedHandler;
/**
* Token Bean
*/
@ -81,7 +87,7 @@ public class YudaoWebSecurityConfigurerAdapter {
/**
* URL
*
* <p>
* anyRequest |
* access | SpringEltrue访
* anonymous | 访
@ -101,44 +107,65 @@ public class YudaoWebSecurityConfigurerAdapter {
// 登出
httpSecurity
// 开启跨域
.cors().and()
.cors(Customizer.withDefaults())
// CSRF 禁用,因为不使用 Session
.csrf().disable()
.csrf(AbstractHttpConfigurer::disable)
// 基于 token 机制,所以不需要 Session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.headers().frameOptions().disable().and()
.sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.headers(c -> c.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
// 一堆自定义的 Spring Security 处理器
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
.exceptionHandling(c ->
c
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
);
// 登录、登录暂时不使用 Spring Security 的拓展点,主要考虑一方面拓展多用户、多种登录方式相对复杂,一方面用户的学习成本较高
// 获得 @PermitAll 带来的 URL 列表,免登录
Multimap<HttpMethod, String> permitAllUrls = getPermitAllUrlsFromAnnotations();
// 设置每个请求的权限
httpSecurity
// ①:全局共享规则
.authorizeHttpRequests()
.authorizeHttpRequests(c ->
c
// 1.1 静态资源,可匿名访问
.requestMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
// 1.2 设置 @PermitAll 无需认证
.requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.HEAD, permitAllUrls.get(HttpMethod.HEAD).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.PATCH, permitAllUrls.get(HttpMethod.PATCH).toArray(new String[0])).permitAll()
// 1.3 基于 yudao.security.permit-all-urls 无需认证
.requestMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()
// 1.4 设置 App API 无需认证
.requestMatchers(buildAppApi("/**")).permitAll()
)
// ②:每个项目的自定义规则
.and().authorizeHttpRequests(registry -> // 下面,循环设置自定义规则
authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(registry)))
.authorizeHttpRequests(c ->
// 下面,循环设置自定义规则
authorizeRequestsCustomizers.forEach(customizer ->
customizer.customize(c)
)
)
// ③:兜底规则,必须认证
.authorizeHttpRequests()
.anyRequest().authenticated()
.authorizeHttpRequests(c -> c.anyRequest().authenticated())
;
// 添加 Token Filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
@ -148,29 +175,40 @@ public class YudaoWebSecurityConfigurerAdapter {
private Multimap<HttpMethod, String> getPermitAllUrlsFromAnnotations() {
Multimap<HttpMethod, String> result = HashMultimap.create();
// 获得接口对应的 HandlerMethod 集合
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
applicationContext.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
// 获得有 @PermitAll 注解的接口
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = entry.getValue();
if (!handlerMethod.hasMethodAnnotation(PermitAll.class)) {
continue;
}
if (entry.getKey().getPatternsCondition() == null) {
continue;
}
Set<String> urls = entry.getKey().getPatternsCondition().getPatterns();
// 特殊:使用 @RequestMapping 注解,并且未写 method 属性,此时认为都需要免登录
Set<RequestMethod> methods = entry.getKey().getMethodsCondition().getMethods();
if (CollUtil.isEmpty(methods)) { //
result.putAll(HttpMethod.GET, urls);
result.putAll(HttpMethod.POST, urls);
result.putAll(HttpMethod.PUT, urls);
result.putAll(HttpMethod.DELETE, urls);
result.putAll(HttpMethod.HEAD, urls);
result.putAll(HttpMethod.PATCH, urls);
continue;
}
// 根据请求方法,添加到 result 结果
methods.forEach(requestMethod -> {
switch (requestMethod) {
@ -186,9 +224,16 @@ public class YudaoWebSecurityConfigurerAdapter {
case DELETE:
result.putAll(HttpMethod.DELETE, urls);
break;
case HEAD:
result.putAll(HttpMethod.HEAD, urls);
break;
case PATCH:
result.putAll(HttpMethod.PATCH, urls);
break;
}
});
}
return result;
}

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -63,6 +63,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -67,6 +67,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -21,10 +21,12 @@ spring:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
mybatis-plus:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -63,6 +63,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -21,10 +21,12 @@ spring:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
mybatis-plus:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -63,6 +63,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -21,10 +21,12 @@ spring:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -63,6 +63,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -63,6 +63,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -21,10 +21,12 @@ spring:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试

View File

@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.member.enums.ApiConstants;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
/**
* Member Security
@ -18,17 +18,17 @@ public class SecurityConfiguration {
return new AuthorizeRequestsCustomizer() {
@Override
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
// Swagger 接口文档
registry.antMatchers("/v3/api-docs/**").permitAll() // 元数据
.antMatchers("/swagger-ui.html").permitAll(); // Swagger UI
registry.requestMatchers("/v3/api-docs/**").permitAll() // 元数据
.requestMatchers("/swagger-ui.html").permitAll(); // Swagger UI
// Spring Boot Actuator 的安全配置
registry.antMatchers("/actuator").anonymous()
.antMatchers("/actuator/**").anonymous();
registry.requestMatchers("/actuator").anonymous()
.requestMatchers("/actuator/**").anonymous();
// Druid 监控
registry.antMatchers("/druid/**").anonymous();
registry.requestMatchers("/druid/**").anonymous();
// RPC 服务的安全配置
registry.antMatchers(ApiConstants.PREFIX + "/**").permitAll();
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
}
};

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -63,6 +63,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -21,10 +21,12 @@ spring:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -63,6 +63,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -63,6 +63,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -21,10 +21,12 @@ spring:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -64,6 +64,7 @@ spring:
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口

View File

@ -21,10 +21,12 @@ spring:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
mybatis-plus:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试

View File

@ -52,6 +52,7 @@ spring:
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口

View File

@ -21,10 +21,12 @@ spring:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试