Spring Security FilterChain异常求助:路径匹配器类型错误排查
Spring Security FilterChain配置异常问题排查与修复
问题背景
开发Spring Boot个人项目时,配置Spring Security FilterChain出现异常,尝试升级Spring Security版本至6.1.2无效,项目未使用额外Servlet。
异常信息
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0: Error creating bean with name 'filterChain' defined in class path resource [com/practise/userApp/Security/SecurityConfig.class]: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'filterChain' threw exception with message: This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher). Caused by: java.lang.IllegalArgumentException: This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).
项目配置
Security配置类
@Configuration @EnableWebSecurity public class SecurityConfig { //INMemory userDetails DB ,PasswordEncoder是接口,BCryptPasswordEncoder是其实现类 @Bean public InMemoryUserDetailsManager detailsManager(PasswordEncoder p) { UserDetails user=User.withUsername("springer1") .password(p.encode("secret")) .roles("admin") .build(); UserDetails admin=User.withUsername("springer2") .password(p.encode("secret")) .roles("user") .build(); return new InMemoryUserDetailsManager(user,admin); } @Bean BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // If you are only creating a service that is used by non-browser clients, you will likely want to disable CSRF protection. .authorizeHttpRequests( (authorizeHttpRequests)-> authorizeHttpRequests .requestMatchers("/users").hasRole("admin") .requestMatchers("/users","/users/**").hasAnyRole("admin","user") .requestMatchers("/users/**").hasRole("admin") .requestMatchers("/","/h2-console/**").permitAll() .requestMatchers("/").permitAll() ); return http.build(); } }
POM.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>userApp</artifactId> <version>0.0.1-SNAPSHOT</version> <name>userApp</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-hateoas</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>6.1.2</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-rest-hal-explorer</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
错误原因
Spring Security 6.x版本对请求匹配器的处理做了严格限制:当项目中同时存在Spring MVC环境(引入了spring-boot-starter-web)和Servlet API时,默认的requestMatchers(String...)方法无法自动判断路径是Spring MVC模式还是AntPath模式,因此抛出该异常。
另外原配置中存在规则顺序冲突:比如先定义了/users需要admin角色,接着又定义/users, /users/**允许admin和user角色,最后又定义/users/**需要admin角色。Spring Security的规则是从上到下匹配,先匹配的规则会生效,后面的规则不会被执行,这种重复定义会导致权限逻辑混乱。
修复方法
方案1:使用MvcRequestMatcher(推荐,适用于Spring MVC端点)
注入HandlerMappingIntrospector创建MvcRequestMatcher,明确指定使用Spring MVC的路径匹配规则:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public InMemoryUserDetailsManager detailsManager(PasswordEncoder p) { UserDetails user=User.withUsername("springer1") .password(p.encode("secret")) .roles("admin") .build(); UserDetails admin=User.withUsername("springer2") .password(p.encode("secret")) .roles("user") .build(); return new InMemoryUserDetailsManager(user,admin); } @Bean BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector); http .authorizeHttpRequests(authorize -> authorize // 调整规则顺序:精确路径在前,模糊路径在后 .requestMatchers(mvcMatcherBuilder.pattern("/users")).hasRole("admin") .requestMatchers(mvcMatcherBuilder.pattern("/users/**")).hasRole("admin") .requestMatchers(mvcMatcherBuilder.pattern("/")).permitAll() .requestMatchers(AntPathRequestMatcher.antMatcher("/h2-console/**")).permitAll() // 默认规则:所有未匹配请求需认证 .anyRequest().authenticated() ); // 允许H2控制台访问的额外配置 http.csrf(csrf -> csrf.ignoringRequestMatchers("/h2-console/**")) .headers(headers -> headers.frameOptions(frame -> frame.sameOrigin())); return http.build(); } }
方案2:使用AntPathRequestMatcher
如果不需要Spring MVC的路径匹配特性,直接使用AntPathRequestMatcher:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public InMemoryUserDetailsManager detailsManager(PasswordEncoder p) { UserDetails user=User.withUsername("springer1") .password(p.encode("secret")) .roles("admin") .build(); UserDetails admin=User.withUsername("springer2") .password(p.encode("secret")) .roles("user") .build(); return new InMemoryUserDetailsManager(user,admin); } @Bean BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .requestMatchers(AntPathRequestMatcher.antMatcher("/users")).hasRole("admin") .requestMatchers(AntPathRequestMatcher.antMatcher("/users/**")).hasRole("admin") .requestMatchers(AntPathRequestMatcher.antMatcher("/")).permitAll() .requestMatchers(AntPathRequestMatcher.antMatcher("/h2-console/**")).permitAll() .anyRequest().authenticated() ); http.csrf(csrf -> csrf.ignoringRequestMatchers("/h2-console/**")) .headers(headers -> headers.frameOptions(frame -> frame.sameOrigin())); return http.build(); } }
额外优化点
- 依赖精简:
spring-boot-starter-security已经包含spring-security-core,可移除POM中单独引入的spring-security-core依赖,避免版本冲突。 - 规则合理性:确保权限规则逻辑一致,避免重复定义冲突路径。
内容的提问来源于stack exchange,提问作者mohan
相关产品推荐
相关产品推荐

