Spring Boot集成Keycloak OAuth2时Bean创建错误求助
问题描述
尝试用Spring Boot和Keycloak实现登录表单授权认证,启动时出现Bean创建错误,具体错误:
创建名为'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration'的Bean时出错:方法'setFilterChains'参数0存在不满足的依赖;创建类路径资源[com/login/java/config/SecurityConfig.class]中定义的'clientFilterChain'Bean时出错:工厂方法'clientFilterChain'抛出异常,提示无法判断这些模式是否为Spring MVC模式,若为Spring MVC端点请使用requestMatchers(MvcRequestMatcher),否则请使用requestMatchers(AntPathRequestMatcher)。
Keycloak运行在8080端口的master realm,查阅Spring Security 6迁移文档未找到解决方案,还存在antmatching相关问题。
错误原因
Spring Security 6对请求匹配器的检测机制更严格,直接使用字符串路径(如"/"、"/home*")时,无法自动识别是Spring MVC端点还是普通路径,必须显式指定使用MvcRequestMatcher或AntPathRequestMatcher。此外,项目还存在依赖重复、配置冲突等潜在问题。
修复方案
1. 显式指定请求匹配器
修改SecurityConfig.java,通过以下两种方式解决匹配器识别问题:
方案一:使用MvcRequestMatcher(适配Spring MVC端点,推荐)
注入MvcRequestMatcher.Builder构建匹配器,适配Spring MVC的路径规则:
package com.login.java.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.util.matcher.MvcRequestMatcher; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import static org.springframework.security.config.Customizer.withDefaults; import org.springframework.beans.factory.annotation.Autowired; @Configuration @EnableWebSecurity class SecurityConfig{ private final KeycloakLogoutHandler keycloakLogoutHandler; private final MvcRequestMatcher.Builder mvc; @Autowired SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler, HandlerMappingIntrospector introspector) { this.keycloakLogoutHandler = keycloakLogoutHandler; this.mvc = new MvcRequestMatcher.Builder(introspector); } @Bean SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Order(1) @Bean SecurityFilterChain clientFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((authorize) -> authorize .requestMatchers(mvc.pattern(HttpMethod.GET, "/")).permitAll() .anyRequest().authenticated()) .oauth2Login(withDefaults()) .logout(logout -> logout .addLogoutHandler(keycloakLogoutHandler) .logoutSuccessUrl("/") ); return http.build(); } @Order(2) @Bean SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((authorize) -> authorize .requestMatchers(mvc.pattern(HttpMethod.GET, "/home*")) .hasRole("user") .anyRequest() .authenticated() ); http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); return http.build(); } @Bean AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { return http.getSharedObject(AuthenticationManagerBuilder.class) .build(); } }
方案二:使用AntPathRequestMatcher
如果不需要适配Spring MVC路径规则,可直接使用Ant模式匹配器:
// 修改clientFilterChain中的匹配规则 .requestMatchers(new AntPathRequestMatcher("/", HttpMethod.GET.name())).permitAll() // 修改resourceServerFilterChain中的匹配规则 .requestMatchers(new AntPathRequestMatcher("/home*", HttpMethod.GET.name())) .hasRole("user")
2. 清理依赖冲突
修改pom.xml,移除重复和过时的依赖:
- 移除重复的
spring-boot-starter-oauth2-client依赖(保留一个即可) - 移除旧版
spring-security-oauth2依赖(Spring Boot 3的OAuth2 Starter已包含所需功能)
修改后的依赖部分:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> </dependencies>
3. 调整配置文件
移除application.properties中不必要的自动配置排除项,自定义SecurityConfig会自动覆盖默认配置:
# 删除此行配置 # spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
4. 解决登录路径冲突
当前LoginController自定义的/login路径,与Spring Security OAuth2 Login的默认登录处理路径冲突,需二选一:
- 若使用Keycloak的OAuth2登录:删除
LoginController中@RequestMapping(path = "/login")的方法,由Spring Security自动跳转到Keycloak登录页。 - 若使用自定义表单登录:修改SecurityConfig,替换
oauth2Login(withDefaults())为表单登录配置,同时调整/login路径的处理逻辑。
额外优化建议
- 确认Keycloak客户端配置:
client-id需与application.properties一致,客户端授权类型包含authorization_code,重定向URI配置为http://localhost:8081/login/oauth2/code/keycloak。 - 检查角色映射:确保Keycloak用户已分配
user角色,可通过spring.security.oauth2.resourceserver.jwt.authorities-mapper配置自定义角色映射规则。
内容的提问来源于stack exchange,提问作者sdidd

