Spring OAuth替代issuer-uri后无法配置end_session_endpoint致登出失效
DMZ中部署的Spring Boot客户端接入OIDC认证服务时,初始使用如下配置:
issuer-uri: https://authentication_server/auth/realms/my-realm
Spring Security会抛出issuer不匹配错误:
The Issuer "https://external_url/auth/realms/my-realm" provided in the configuration metadata did not match the requested issuer "https://authentication_server/auth/realms/my-realm"
参考官方issue的解决方案,切换为单独指定三个独立端点替代issuer-uri的配置方式:
authorization-uri: https://external_url/auth/realms/my-realm/protocol/openid-connect/auth token-uri: https://authentication_server/auth/realms/my-realm/protocol/openid-connect/token jwk-set-uri: https://authentication_server/auth/realms/my-realm/protocol/openid-connect/certs
调整后认证流程可正常运行。这里长期存在的疑问是:既然单独配置这些端点可正常运行,为什么Spring Security无法基于issuer-uri自动适配相同的参数?
目前出现的新问题为登出功能失效:原本使用issuer-uri的OAuth自动配置模式下,可从元数据响应中自动获取end_session_endpoint;但改为手动配置各端点后,常规配置项中没有可指定end_session_endpoint的入口。
核心待解答问题:当前登出失效的问题,是Spring Security OAuth尚未解决的已知缺陷,还是需要采用其他配置方式实现登出功能?
关于issuer-uri无法自动适配内外网地址差异的原因
这不是Spring Security的功能缺失,是OIDC协议的强制安全校验要求:OIDC规范明确规定,客户端从元数据接口拿到的issuer声明值,必须和客户端初始请求元数据时使用的issuer-uri做完全字符串匹配,这是防止中间人篡改认证元数据的核心安全机制。
之前的报错本质是对接的Keycloak认证服务自身配置问题:它在元数据里返回的issuer是对外暴露的外网地址,和客户端配置的内网地址字符串不匹配,触发了Spring Security的安全校验。Spring Security不会自动替换issuer字符串里的域名做适配,是因为这类自动适配会直接绕开issuer校验的安全设计,本身不符合协议的安全基线要求。
实际DMZ部署场景下,认证服务通常同时暴露内网和外网两个访问地址,如果希望不做地址字符串替换就通过校验,只需要在认证服务端把对外返回的issuer配置为客户端实际访问的内网地址,再结合网关做外网访问的路径转发,就不会触发issuer不匹配的报错。
关于手动配置下的登出功能实现
这个问题不属于未解决的已知缺陷,只是Spring Security的自动配置没有在常规application.yml配置项中暴露登出端点的配置入口,完全可以通过手动配置Bean的方式实现,和现有手动端点配置的逻辑完全兼容,常用实现方案有两种:
- 方案一:不需要完全弃用
issuer-uri配置,只需调整认证服务的issuer配置,让它在元数据中返回的issuer值和客户端配置的内网地址保持字符串一致,校验通过后Spring Security会自动从元数据中拉取包括end_session_endpoint在内的所有端点地址,无需手动拆分所有配置。 - 方案二:完全保留现有手动拆分端点的配置,自定义
OidcClientInitiatedLogoutSuccessHandlerBean,手动将对应认证服务的登出端点(即https://authentication_server/auth/realms/my-realm/protocol/openid-connect/logout)注入到登出处理器中,同时配置登出成功后的跳转地址即可,不需要依赖OIDC元数据的自动拉取逻辑。
内容的提问来源于stack exchange,提问作者homaxto

