Spring Security SAML注销-在Saml 2LogoutResponseFilter处失败

vql8enpb  于 2023-02-16  发布在  Spring
关注(0)|答案(1)|浏览(325)

我们有一个简单的Saml 2-Logout,用户在IdP中注销,但最后一步,允许我们终止应用程序中会话的回调导致Http状态:未找到。这是我们的RelyingPartyRegistrationRepository和其他与saml 2相关的配置

import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
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.configuration.WebSecurityCustomizer;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
import org.springframework.security.web.SecurityFilterChain;

import java.security.PrivateKey;
import java.security.cert.X509Certificate;

import static org.springframework.security.config.Customizer.withDefaults;

@Profile("!local")
@EnableWebSecurity
@Configuration
public class Saml2WebSecurityConfig {

    private static final String LOGOUT_CALLBACK_URL = "/logout/saml2/slo";
    private final EIAMConfigProperties eiamConfigProperties;

    public Saml2WebSecurityConfig(EIAMConfigProperties eiamConfigProperties) {
        this.eiamConfigProperties = eiamConfigProperties;
    }

    @Bean
    public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() throws Exception {
        final X509Certificate[] cert = ...;
        final PrivateKey privateKey = ...;
        final X509Certificate[] verificationCertificate = ...;

        Saml2X509Credential signingCredential = Saml2X509Credential.signing(privateKey, cert[0]);
        Saml2X509Credential verificationCredential = Saml2X509Credential.verification(verificationCertificate[0]);

        RelyingPartyRegistration registration = RelyingPartyRegistration
                .withRegistrationId(eiamConfigProperties.getRegistrationId())
                .entityId(eiamConfigProperties.getEntity())
                .signingX509Credentials(c -> c.add(signingCredential))
                .assertingPartyDetails(party -> party.entityId(eiamConfigProperties.getAssertionId())
                        .singleSignOnServiceLocation(eiamConfigProperties.getSingleSignOnServiceLocation())
                        .singleSignOnServiceBinding(Saml2MessageBinding.POST)
                        .singleLogoutServiceLocation(eiamConfigProperties.getSingleLogoutServiceLocation())
                        .singleLogoutServiceBinding(Saml2MessageBinding.POST)
                        .wantAuthnRequestsSigned(true)
                        .signingAlgorithms(sign -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256))
                        .verificationX509Credentials(c -> c.add(verificationCredential))
                )
                .build();
        return new InMemoryRelyingPartyRegistrationRepository(registration);
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated())
                .saml2Login(withDefaults())
                .saml2Logout((saml2) -> saml2
                        .logoutRequest((request) -> request.logoutUrl(LOGOUT_CALLBACK_URL))
                        .logoutResponse((response) -> response.logoutUrl(LOGOUT_CALLBACK_URL))
                );

        http.csrf().disable();
        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return web -> web.ignoring().requestMatchers(LOGOUT_CALLBACK_URL);
    }
}

如何让Spring Security公开/logout/saml 2/slo并终止会话(或者允许我实现自己的会话销毁逻辑)?
编辑:启用调试日志记录后,我看到:

2023-02-03 14:42:37.845 DEBUG 1 --- [p-nio-80-exec-2] .s.s.p.s.w.a.l.Saml2LogoutResponseFilter : Failed to validate LogoutResponse: [[invalid_destination] Failed to match destination to configured destination] 
0tdrvxhp

0tdrvxhp1#

我找到了解决方案,并将发布它,以供其他人帮助或为未来的我:)缺少的是RelyingPartyRegistration上的singleLogoutServiceResponseLocation,它必须与LogoutResponse目标匹配。否则Saml2LogoutResponseFilter将引发异常。

@Profile("!local")
@EnableWebSecurity
@Configuration
public class Saml2WebSecurityConfig {

private final EIAMConfigProperties eiamConfigProperties;

public Saml2WebSecurityConfig(EIAMConfigProperties eiamConfigProperties) {
    this.eiamConfigProperties = eiamConfigProperties;
}

@Bean
public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() throws Exception {
    final X509Certificate[] cert = ...;
    final PrivateKey privateKey = ...;
    final X509Certificate[] verificationCertificate = ...;

    Saml2X509Credential signingCredential = Saml2X509Credential.signing(privateKey, cert[0]);
    Saml2X509Credential verificationCredential = Saml2X509Credential.verification(verificationCertificate[0]);

    RelyingPartyRegistration registration = RelyingPartyRegistration
            .withRegistrationId(eiamConfigProperties.getRegistrationId())
            // this was missing and must match the LogoutResponse Destination
            .singleLogoutServiceResponseLocation(eiamConfigProperties.getSingleLogoutServiceResponseLocation())
            .entityId(eiamConfigProperties.getEntity())
            .signingX509Credentials(c -> c.add(signingCredential))
            .assertingPartyDetails(party -> party.entityId(eiamConfigProperties.getAssertionId())
                    .singleSignOnServiceLocation(eiamConfigProperties.getSingleSignOnServiceLocation())
                    .singleSignOnServiceBinding(Saml2MessageBinding.POST)
                    .singleLogoutServiceLocation(eiamConfigProperties.getSingleLogoutServiceLocation())
                    .singleLogoutServiceBinding(Saml2MessageBinding.POST)
                    .wantAuthnRequestsSigned(true)
                    .signingAlgorithms(sign -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256))
                    .verificationX509Credentials(c -> c.add(verificationCredential))
            )
            .build();

    return new InMemoryRelyingPartyRegistrationRepository(registration);
}

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(authorize -> authorize
                    .anyRequest().authenticated())
            .saml2Login(withDefaults())
            .saml2Logout(withDefaults());

    http.csrf().disable();
    return http.build();
}

}

相关问题