Spring Security Spring安全:发送SAML 2注销请求导致SAML-Server中的会话丢失

k97glaaz  于 2023-03-02  发布在  Spring
关注(0)|答案(1)|浏览(322)

在Spring Boot 4(Spring Security 6)中,我们将从我们自己的SAML实现转移到Spring Security的SAML实现。发送Saml2LogoutRequest会导致SAML2-Server(在我的测试场景中是一个simpleSamlPhp示例)中出现会话丢失错误,并且不会执行注销。重新加载页面会在后台创建一个新会话,因为用户只是从SpringBoot应用程序注销。
我在调试问题时发现了以下内容:
POST到logout-Endpoint导致LogoutFilter.doFilter()被调用,用户被注销系统,如日志所示:

2023-02-21T16:19:24.369+01:00 DEBUG 897328 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : Securing POST /sso/saml/logout
2023-02-21T16:19:24.371+01:00 DEBUG 897328 --- [nio-8080-exec-3] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=AiToolSaml2Authentication [Principal=org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal@669175a9, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=73598BB643535D6256B71F961AFF7266], Granted Authorities=[ROLE_CONSUMER]]]
2023-02-21T16:20:09.065+01:00  WARN 897328 --- [l-1 housekeeper] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=48s315ms980µs301ns).
2023-02-21T16:20:10.414+01:00 DEBUG 897328 --- [nio-8080-exec-3] o.s.s.w.a.logout.LogoutFilter            : Logging out [AiToolSaml2Authentication [Principal=org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal@669175a9, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=73598BB643535D6256B71F961AFF7266], Granted Authorities=[ROLE_CONSUMER]]]
2023-02-21T16:20:16.845+01:00 DEBUG 897328 --- [nio-8080-exec-3] o.s.s.w.a.l.SecurityContextLogoutHandler : Invalidated session 348DCC90A54531D2C855906B14A6F8E4

然后调用Saml2RelyingPartyInitiatedLogoutSuccessHandler.onLogoutSuccess(),生成一个Saml2LogoutRequest,这个请求通过doRedirect()重定向到SAML2服务器。
我可以在SAML服务器的端点'SingleLogoutService.php'中看到请求,之后调用了resumelogout.php ',它无法声明状态信息丢失,如here所述。
就我所见,请求本身看起来不错,我还检查了SAML cookie中设置的SameSite = None和Secure = 1。
在我们自己的实现中,我们曾经有一个GET请求来注销重定向到SAML服务器,使用相同的SAML配置可以很好地工作。Saml2LogoutRequest需要设置Issuer,NameID,ID和IssueInstant,唯一需要覆盖的是NameID,我通过Saml2LogoutRequestResolver完成了这一操作。
我不确定到底是什么失败了,我也不确定是否是SpringBoot-App中的预注销导致了这个问题。
有什么想法吗?
当前的测试配置如下所示(本地主机上只有http,我们的测试环境使用https;但误差是相同):

spring:
  security:
    saml2:
      relyingparty:
        registration:
          saml:
            assertingparty:
              entity-id: "http://localhost:8000/saml2/idp/metadata.php"
              metadata-uri: "http://localhost:8000/saml2/idp/metadata.php"
              singlesignon:
                url: "http://localhost:8000/saml2/idp/SSOService.php"
            entity-id: "http://localhost:8080"
            singlelogout:
              url: "http://localhost:8000/saml2/idp/SingleLogoutService.php"
            signing:
              credentials:
              - certificate-location: "classpath:saml.crt"
                private-key-location: "classpath:saml.key"

安全配置如下所示:

@Bean
    fun filterChain(http: HttpSecurity, registrations: RelyingPartyRegistrationRepository): SecurityFilterChain {
    ...
    // saml (sso) login
    http.saml2Login { saml2LoginConfigurer ->
        saml2LoginConfigurer
            .authenticationRequestUri("/sso/{registrationId}/login")
            .loginProcessingUrl(samlResponseUrl.replace("{baseUrl}", ""))
            .authenticationManager(ProviderManager(samlAuthenticationProvider()))
            .defaultSuccessUrl(samlRedirectUrl)
            .failureUrl(samlRedirectUrlNewUser)
        }
    // saml (sso) logout
    http.saml2Logout { saml2LogoutConfigurer ->
        saml2LogoutConfigurer
            .logoutUrl("/sso/saml/logout")
            .logoutRequest { request ->
                request.logoutRequestResolver(
                    logoutRequestResolver(DefaultRelyingPartyRegistrationResolver(registrations))
                )
            }
        }
    ...
    }

    @Bean
    fun samlAuthenticationProvider(): OpenSaml4AuthenticationProvider =
        // provider configuration
        OpenSaml4AuthenticationProvider().apply {
            setResponseAuthenticationConverter { responseToken: OpenSaml4AuthenticationProvider.ResponseToken ->
                val authentication = OpenSaml4AuthenticationProvider
                    .createDefaultResponseAuthenticationConverter()
                    .convert(responseToken)
                val principal = (authentication!!.principal as DefaultSaml2AuthenticatedPrincipal)
                val email = principal.attributes[samlEmailAttributName]
                check(email != null) { "Configured email attribute not found in SAML response." }
                check(email.size == 1) { "Configured email attribute is not unique." }
                check(email[0] is String) { "Configured email attribute value is not a string." }
                val user = (authenticationService.loadUserByUsername(email[0] as String)
                AiToolSaml2Authentication(
                    user,
                    principal,
                    authentication.credentials.toString(),
                    user.authorities
                )
            }
        }

    @Bean
    fun logoutRequestResolver(
        registrationResolver: RelyingPartyRegistrationResolver
    ): Saml2LogoutRequestResolver =
        OpenSaml4LogoutRequestResolver(registrationResolver).apply {
            setParametersConsumer { parameters ->
                with(parameters.logoutRequest) {
                    nameID.value = parameters.authentication.getUserInternal().user.email
                }
            }
        }
2mbi3lxu

2mbi3lxu1#

这整个事情原来是一个CORS问题。正确设置访问控制头并允许在客户端中CORS提交凭据可以解决这个问题。

相关问题