SAML2.0+Keycloak+SpringBoot集成报client_not_found错误排查
环境信息
- Keycloak 18.0.0(Docker本地部署)
- Spring Boot 2.6.3
- 集成目标:实现基于SAML 2.0的应用单点登录
故障现象
访问应用端点可正常跳转至Keycloak,但Keycloak控制台持续输出如下报错,登录流程中断:
WARN [org.keycloak.events] (executor-thread-15) type=LOGIN_ERROR, realmId=my-app, clientId=null, userId=null, ipAddress=172.18.0.1, error=client_not_found, reason=Cannot_match_source_hash
现有配置
WebSecurityConfig.java
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter; import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter; import static org.springframework.security.config.Customizer.withDefaults; @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final RelyingPartyRegistrationRepository relyingPartyRegistrationRepository; public WebSecurityConfig(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) { this.relyingPartyRegistrationRepository = relyingPartyRegistrationRepository; } @Override protected void configure(HttpSecurity http) throws Exception { RelyingPartyRegistrationResolver defaultRelyingPartyRegistrationResolver = new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository); Saml2MetadataFilter filter = new Saml2MetadataFilter(defaultRelyingPartyRegistrationResolver, new OpenSamlMetadataResolver()); http .saml2Login(withDefaults()) .addFilterBefore(filter, Saml2WebSsoAuthenticationFilter.class) .antMatcher("/**") .authorizeRequests() .antMatchers("/**").authenticated(); } }
pom.xml 核心依赖
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-saml2-service-provider</artifactId> </dependency>
application.yml SAML配置片段
spring: security: saml2: relyingparty: registration: saml: identityprovider: entity-id: http://localhost:8080/realms/my-app verification: - certificate-location: "classpath:saml-certificate/keycloak.cert" singlesignon: url: http://localhost:8080/auth/realms/my-app/protocol/saml sign-request: false
问题根因与修复方案
报错Cannot_match_source_hash且clientId=null的核心原因是Keycloak收到SAML认证请求后,无法通过请求携带的信息匹配到对应realm下的配置客户端,结合现有配置,具体问题和修复步骤如下:
修正Keycloak路径配置不一致问题
Keycloak 18.0.0基于Quarkus重构后,默认移除了旧版本WildFly发行版自带的/auth上下文根路径,只有启动时手动添加--http-relative-path=/auth参数才会保留该前缀。
你当前配置中IdP的entity-id使用不带/auth的路径,SSO地址却带/auth前缀,二者不匹配会导致请求路由异常。如果启动Keycloak时未指定上述参数,请将singlesignon.url修改为http://localhost:8080/realms/my-app/protocol/saml,和entity-id路径保持一致;如果需要保留/auth前缀,启动Keycloak时添加对应参数,同时将entity-id也修改为带/auth的格式即可。补全Spring SAML2 SP侧缺失配置
你当前的配置仅填写了IdP(Keycloak)侧参数,未配置SP(当前Spring应用)自身的实体ID、ACS地址参数。Spring Security SAML2默认会按规则生成SP参数:- 默认SP Entity ID(对应SAML请求中的Issuer字段):
{应用访问根地址}/saml2/service-provider-metadata/{registrationId},当前registrationId为saml - 默认ACS(断言消费服务)地址:
{应用访问根地址}/login/saml2/sso/{registrationId}
你可以选择直接使用默认值,也可以在application.yml中手动指定固定值避免环境变化导致匹配失败,补全后的配置示例如下:
spring: security: saml2: relyingparty: registration: saml: # SP侧配置,需和Keycloak侧客户端参数完全一致 entity-id: my-spring-boot-app # 可自定义,作为Keycloak侧SAML客户端的Client ID acs: location: http://localhost:8081/login/saml2/sso/saml # 替换为Spring应用实际访问地址+默认ACS路径 identityprovider: entity-id: http://localhost:8080/realms/my-app verification: - certificate-location: "classpath:saml-certificate/keycloak.cert" singlesignon: url: http://localhost:8080/realms/my-app/protocol/saml sign-request: false- 默认SP Entity ID(对应SAML请求中的Issuer字段):
修正Keycloak侧SAML客户端配置
进入Keycloak的my-apprealm,按如下要求配置SAML客户端:- 客户端类型选择SAML
- Client ID字段必须和Spring配置中
entity-id的值完全一致,不能存在拼写、斜杠、前后缀差异 - 有效重定向URI配置为Spring应用的ACS地址,格式为
http://localhost:8081/login/saml2/sso/saml/*(末尾加通配符适配请求参数) - 为避免手动填写参数出错,可以启动Spring应用后访问
http://localhost:8081/saml2/metadata/saml获取SP元数据文件,创建客户端时直接选择导入元数据,即可自动填充所有匹配参数。
你当前的WebSecurityConfig代码本身没有逻辑问题,不需要额外调整,完成以上配置后重启应用即可正常走通SAML登录流程。
内容的提问来源于stack exchange,提问作者marco

