You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot集成Keycloak OAuth2时Bean创建错误求助

Spring Boot + Keycloak 登录授权启动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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 10:47:02