我正在从 Spring boot 2 应用程序迁移到 Spring boot 3, 我在 Spring Security 中做了所有更改,并且应用程序启动正常。当我调用端点时,我收到 PatternParseException,我认为这是因为我的 Spring Security 配置。
您可以在下面找到我的 Spring 安全配置和 Web 配置。
org.springframework.web.util.pattern.PatternParseException: No more pattern data allowed after {*...} or ** pattern element
at org.springframework.web.util.pattern.InternalPathPatternParser.peekDoubleWildcard(InternalPathPatternParser.java:250) ~[spring-web-6.0.11.jar!/:6.0.11]
at org.springframework.web.util.pattern.InternalPathPatternParser.parse(InternalPathPatternParser.java:113) ~[spring-web-6.0.11.jar!/:6.0.11]
at org.springframework.web.util.pattern.PathPatternParser.parse(PathPatternParser.java:129) ~[spring-web-6.0.11.jar!/:6.0.11]
at org.springframework.web.servlet.handler.PathPatternMatchableHandlerMapping.lambda$match$0(PathPatternMatchableHandlerMapping.java:64) ~[spring-webmvc-6.0.11.jar!/:6.0.11]
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) ~[na:na]
安全配置
package com.omb.restcore.security;
import com.omb.core.security.utils.JasyptEncryptorDecryptor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private static final Logger LOG = Logger.getLogger(SecurityConfig.class.getName());
private static final String HEALTHCHECK_URL = "/**/healthCheck";
private static final String[] ALLOW_LIST = {
HEALTHCHECK_URL, "/**/v3/api-docs", "/**/swagger-resources/**",
"/**/swagger-ui.html", "/**/webjars/**", "/**/csrf", "/", "/**/css/**"
};
@Value("${enableHttpAuthentication:true}")
private boolean enableHttpAuthentication;
@Value("${enableLoginEncryption}")
private boolean enableLoginEncryption;
@Value("${allowAnonymousAccessToSwaggerDoc}")
private boolean allowAnonymousAccessToSwaggerDoc;
@Autowired
private Environment environment;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
if (enableHttpAuthentication) {
// Accept only BASIC authenticated requests except for the given ressources, which are accessible without
// authentication
if (allowAnonymousAccessToSwaggerDoc) {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth.requestMatchers(ALLOW_LIST).permitAll()
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
} else {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth.requestMatchers(HEALTHCHECK_URL).permitAll()
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
}
LOG.info("BASIC HTTP authentication enabled");
} else {
// Disable authentication on API usage
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth.anyRequest().anonymous());
LOG.info("BASIC HTTP authentication disabled");
}
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(List.of("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Requestor-Type"));
configuration.setExposedHeaders(List.of("X-Get-Header"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuth = auth.inMemoryAuthentication();
for (PropertySource<?> propertySource : ((AbstractEnvironment) environment).getPropertySources()) {
if (propertySource instanceof MapPropertySource) {
Map<String, Object> properties = ((MapPropertySource) propertySource).getSource();
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("user\\.(.*)\\.password");
for (String k : properties.keySet()) {
java.util.regex.Matcher matcher = pattern.matcher(k);
if (matcher.find()) {
String user = matcher.group(1);
String pwd = properties.get(k).toString();
if (enableLoginEncryption) {
user = JasyptEncryptorDecryptor.decrypt(user);
}
pwd = JasyptEncryptorDecryptor.decrypt(pwd);
if (!StringUtils.isEmpty(pwd)) {
LOG.finest("Configured HTTP BASIC access rights for user " + user);
inMemoryAuth.withUser(user).password("{noop}" + pwd).roles("USER");
} else {
LOG.finest("Disabled HTTP BASIC access rights for user " + user);
}
}
}
}
}
LOG.info("BASIC HTTP configuration done.");
}
}
网络配置
package com.omb.gateway.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedHeaders("Requestor-Type")
.exposedHeaders("X-Get-Header")
.allowCredentials(true);
}
};
}
}
我通过更改这些行来解决我的问题
错误的路径模式定义:
private static final String HEALTHCHECK_URL = "/**/healthCheck";
private static final String[] ALLOW_LIST = {
HEALTHCHECK_URL, "/**/v3/api-docs", "/**/swagger-resources/**",
"/**/swagger-ui.html", "/**/webjars/**", "/**/csrf", "/", "/**/css/**"
};
良好的路径模式定义:
private static final String HEALTHCHECK_URL = "/healthCheck/**";
private static final String[] ALLOW_LIST = {
HEALTHCHECK_URL, "/v3/api-docs/**", "/swagger-resources/**",
"/swagger-ui.html/**", "/webjars/**", "/csrf/**", "/**", "/css/**"
};