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

Spring Boot3+Spring Security6集成Keycloak配置问题求助

Spring Boot 3 + Spring Security 6 + Keycloak 配置修正方案

你的配置存在几个关键问题,直接导致了重定向循环、403错误和登出失效:

核心问题分析

  1. @KeycloakConfiguration 注解冗余冲突:这个注解属于旧版Keycloak专属适配器,Spring Security 6已经通过标准OAuth2/OIDC协议整合Keycloak,保留该注解会干扰正常的OAuth2流程。
  2. STATELESS 会话策略不兼容:OAuth2授权码模式需要通过会话存储授权请求的临时state参数(用于防CSRF),设置为STATELESS会导致状态丢失,触发无限制重定向。
  3. Logout 逻辑完全缺失:空的logout { }块既不会清理本地会话,也不会调用Keycloak的全局登出端点,导致登出操作无效。
  4. 静态资源未放行:Thymeleaf应用依赖/css/**、/js/**等静态资源,你的配置未对这些路径开放权限,可能引发页面加载时的403错误。

修正后的完整配置代码

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
class SecurityConfig {

    @Value("\${spring.security.oauth2.client.provider.keycloak.issuer-uri}")
    private lateinit var issuerUri: String

    @Bean
    fun configure(http: HttpSecurity): SecurityFilterChain {
        http
            .cors { it.disable() }
            .csrf { 
                // 传统MVC应用建议开启,Thymeleaf会自动处理CSRF令牌
                it.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            }
            .oauth2Login { oauth2 ->
                // 自定义登录成功后默认跳转页面,第二个参数设为true强制跳转,忽略原始请求
                oauth2.defaultSuccessUrl("/portal/dashboard", true)
            }
            .logout { logout ->
                logout
                    .logoutSuccessUrl("/")
                    .invalidateHttpSession(true)
                    .clearAuthentication(true)
                    // 配置Keycloak全局登出逻辑
                    .addLogoutHandler(OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository()))
            }
            .authorizeHttpRequests { authorize ->
                authorize
                    // 放行首页和静态资源
                    .requestMatchers("/", "/css/**", "/js/**", "/images/**").permitAll()
                    // 保护portal下的所有路径
                    .requestMatchers("/portal/**").authenticated()
                    // 其余请求全部放行
                    .anyRequest().permitAll()
            }
            .sessionManagement { session ->
                // OAuth2授权码流程必须使用STATEFUL会话存储临时状态
                session.sessionCreationPolicy(SessionCreationPolicy.STATEFUL)
            }
        return http.build()
    }

    @Bean
    fun clientRegistrationRepository(): ClientRegistrationRepository {
        return InMemoryClientRegistrationRepository(keycloakClientRegistration())
    }

    private fun keycloakClientRegistration(): ClientRegistration {
        return ClientRegistration.withRegistrationId("keycloak")
            .clientId("your-client-id")
            .clientSecret("your-client-secret")
            .redirectUri("{baseUrl}/login/oauth2/code/keycloak")
            .authorizationUri("$issuerUri/protocol/openid-connect/auth")
            .tokenUri("$issuerUri/protocol/openid-connect/token")
            .userInfoUri("$issuerUri/protocol/openid-connect/userinfo")
            .userNameAttributeName(IdTokenClaimNames.SUB)
            .jwkSetUri("$issuerUri/protocol/openid-connect/certs")
            .clientName("Keycloak")
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .scope("openid", "profile", "email")
            .build()
    }
}

配套配置说明

1. application.yml 基础配置

确保配置文件中填入Keycloak的实例信息:

spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            issuer-uri: http://你的Keycloak地址/auth/realms/你的领域名
        registration:
          keycloak:
            client-id: 你的客户端ID
            client-secret: 你的客户端密钥
            scope: openid,profile,email

2. 依赖调整(移除旧适配器)

使用Spring官方OAuth2依赖替代Keycloak专属适配器,以gradle Kotlin DSL为例:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
    implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
    implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6")
}

3. Thymeleaf模板权限控制

在页面中通过Spring Security标签判断登录状态:

<div sec:authorize="isAuthenticated()">
  <a href="/logout" th:href="@{/logout}">登出</a>
</div>
<div sec:authorize="!isAuthenticated()">
  <a href="/oauth2/authorization/keycloak" th:href="@{/oauth2/authorization/keycloak}">登录</a>
</div>

内容的提问来源于stack exchange,提问作者Mateusz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:45:07