Spring session 与 spring security saml 的集成

Posted

技术标签:

【中文标题】Spring session 与 spring security saml 的集成【英文标题】:Integration of Spring session with spring security saml 【发布时间】:2016-12-03 22:43:30 【问题描述】:

我想使用 openAm 对用户进行身份验证并共享会话以与其他微服务进行通信,而无需再次对用户进行身份验证。会话详细信息存储在外部数据库中。我正在使用带有所有 java 配置的 spring security saml 示例应用程序来实现这一点。 有两个会话正在生成。一个在登录 IDP 之前,一个在输入凭据之后。如果我在 openAm 检查,用户已登录,但在应用程序中,我收到 SAMLException,指定响应的 InResponseToField 与发送的消息 12345(随机 id)不对应。 如何将 spring session 与 saml 集成?

我的示例应用程序的安全配置文件

package com.vdenotaris.spring.boot.security.saml.web.config;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private SAMLUserDetailsServiceImpl samlUserDetailsServiceImpl;


    @Bean
    public VelocityEngine velocityEngine() 
        return VelocityFactory.getEngine();
    


    @Bean(initMethod = "initialize")
    public StaticBasicParserPool parserPool() 
        return new StaticBasicParserPool();
    

    @Bean(name = "parserPoolHolder")
    public ParserPoolHolder parserPoolHolder() 
        return new ParserPoolHolder();
    


    @Bean
    public MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager() 
        return new MultiThreadedHttpConnectionManager();
    

    @Bean
    public HttpClient httpClient() 
        return new HttpClient(multiThreadedHttpConnectionManager());
         

    @Bean
    public SAMLAuthenticationProvider samlAuthenticationProvider() 
        SAMLAuthenticationProvider samlAuthenticationProvider = new SAMLAuthenticationProvider();
        samlAuthenticationProvider.setUserDetails(samlUserDetailsServiceImpl);
        samlAuthenticationProvider.setForcePrincipalAsString(false);
        return samlAuthenticationProvider;
         
  @Bean
    public SAMLContextProviderImpl contextProvider() 
        return new SAMLContextProviderImpl();
         

    @Bean
    public static SAMLBootstrap sAMLBootstrap() 
        return new SAMLBootstrap();
         

    @Bean
    public SAMLDefaultLogger samlLogger() 
        return new SAMLDefaultLogger();
    
    @Bean
    public WebSSOProfileConsumer webSSOprofileConsumer() 
        return new WebSSOProfileConsumerImpl();
    
    @Bean
    public WebSSOProfileConsumerHoKImpl hokWebSSOprofileConsumer() 
        return new WebSSOProfileConsumerHoKImpl();
    
    @Bean
    public WebSSOProfile webSSOprofile() 
        return new WebSSOProfileImpl();
    
    @Bean
    public WebSSOProfileConsumerHoKImpl hokWebSSOProfile() 
        return new WebSSOProfileConsumerHoKImpl();
    
    @Bean
    public WebSSOProfileECPImpl ecpprofile() 
        return new WebSSOProfileECPImpl();
    

    @Bean
    public SingleLogoutProfile logoutprofile() 
        return new SingleLogoutProfileImpl();
    
    @Bean
    public KeyManager keyManager() 
        DefaultResourceLoader loader = new DefaultResourceLoader();
        Resource storeFile = loader
                .getResource("classpath:/saml/keystore.jks");
        String storePass = "password";
        Map<String, String> passwords = new HashMap<String, String>();
        passwords.put("mydomain", "password");
        String defaultKey = "mydomain";
        return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
    
    @Bean
    public TLSProtocolConfigurer tlsProtocolConfigurer() 
      return new TLSProtocolConfigurer();
    

    @Bean
    public ProtocolSocketFactory socketFactory() 
        return new TLSProtocolSocketFactory(keyManager(), null, "default");
    

    @Bean
    public Protocol socketFactoryProtocol() 
        return new Protocol("https", socketFactory(), 443);
    

    @Bean
    public MethodInvokingFactoryBean socketFactoryInitialization() 
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        methodInvokingFactoryBean.setTargetClass(Protocol.class);
        methodInvokingFactoryBean.setTargetMethod("registerProtocol");
        Object[] args = "https", socketFactoryProtocol();
        methodInvokingFactoryBean.setArguments(args);
        return methodInvokingFactoryBean;
    

    @Bean
    public WebSSOProfileOptions defaultWebSSOProfileOptions() 
        WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions();
        webSSOProfileOptions.setIncludeScoping(false);
        return webSSOProfileOptions;
    

    // Entry point to initialize authentication, default values taken from
    // properties file
    @Bean
    public SAMLEntryPoint samlEntryPoint() 
        SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint();
        samlEntryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions());
        return samlEntryPoint;
    
    @Bean
    public ExtendedMetadata extendedMetadata() 
      ExtendedMetadata extendedMetadata = new ExtendedMetadata();
      extendedMetadata.setIdpDiscoveryEnabled(true); 
      extendedMetadata.setSignMetadata(false);
      return extendedMetadata;
    

    @Bean
    public SAMLDiscovery samlIDPDiscovery() 
        SAMLDiscovery idpDiscovery = new SAMLDiscovery();        
        idpDiscovery.setIdpSelectionPath("/saml/idpSelection");
        return idpDiscovery;
    

  @Bean
  @Qualifier("idp-ssocircle")
  public ExtendedMetadataDelegate ssoCircleExtendedMetadataProvider()
      throws MetadataProviderException 
    String idpSSOCircleMetadataURL = "http://openam.com:8080/OpenAM/saml2/jsp/exportmetadata.jsp";
    Timer backgroundTaskTimer = new Timer(true);
    HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(
        backgroundTaskTimer, httpClient(), idpSSOCircleMetadataURL);
    httpMetadataProvider.setParserPool(parserPool());
    ExtendedMetadataDelegate extendedMetadataDelegate = 
        new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
    extendedMetadataDelegate.setMetadataTrustCheck(true);
    extendedMetadataDelegate.setMetadataRequireSignature(false);
    return extendedMetadataDelegate;
  
    @Bean
    @Qualifier("metadata")
    public CachingMetadataManager metadata() throws MetadataProviderException 
        List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
        providers.add(ssoCircleExtendedMetadataProvider());
        return new CachingMetadataManager(providers);
    

    // Filter automatically generates default SP metadata
    @Bean
    public MetadataGenerator metadataGenerator() 
        MetadataGenerator metadataGenerator = new MetadataGenerator();
        metadataGenerator.setEntityId("http://localhost:8080/spring-boot-security-saml2-sample/saml/web/metadata");
        metadataGenerator.setExtendedMetadata(extendedMetadata());
        metadataGenerator.setIncludeDiscoveryExtension(false);
        metadataGenerator.setKeyManager(keyManager()); 
        return metadataGenerator;
    

    // The filter is waiting for connections on URL suffixed with filterSuffix
    // and presents SP metadata there
    @Bean
    public MetadataDisplayFilter metadataDisplayFilter() 
        return new MetadataDisplayFilter();
    

    // Handler deciding where to redirect user after successful login
    @Bean
    public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() 
        SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
                new SavedRequestAwareAuthenticationSuccessHandler();
        successRedirectHandler.setDefaultTargetUrl("/");     
        return successRedirectHandler;
    

  // Handler deciding where to redirect user after failed login
    @Bean
    public SimpleUrlAuthenticationFailureHandler authenticationFailureHandler() 
      SimpleUrlAuthenticationFailureHandler failureHandler =
          new SimpleUrlAuthenticationFailureHandler();
      failureHandler.setUseForward(true);
      failureHandler.setDefaultFailureUrl("/error");
      return failureHandler;
    

    @Bean
    public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception 
        SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter();
        samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
        samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationManager());
        samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
        return samlWebSSOHoKProcessingFilter;
    
    @Bean
    public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception 
        SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter();
        samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager());
        samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
        samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
        return samlWebSSOProcessingFilter;
    

    @Bean
    public MetadataGeneratorFilter metadataGeneratorFilter() 
        return new MetadataGeneratorFilter(metadataGenerator());
    
    @Bean
    public SimpleUrlLogoutSuccessHandler successLogoutHandler() 
        SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
        successLogoutHandler.setDefaultTargetUrl("/");
        return successLogoutHandler;
    
    @Bean
    public SecurityContextLogoutHandler logoutHandler() 
        SecurityContextLogoutHandler logoutHandler = 
            new SecurityContextLogoutHandler();
        logoutHandler.setInvalidateHttpSession(true);
        logoutHandler.setClearAuthentication(true);
        return logoutHandler;
    

    @Bean
    public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() 
        return new SAMLLogoutProcessingFilter(successLogoutHandler(),
                logoutHandler());
             
    @Bean
    public SAMLLogoutFilter samlLogoutFilter() 
        return new SAMLLogoutFilter(successLogoutHandler(),
                new LogoutHandler[]  logoutHandler() ,
                new LogoutHandler[]  logoutHandler() );
    

    private ArtifactResolutionProfile artifactResolutionProfile() 
        final ArtifactResolutionProfileImpl artifactResolutionProfile = 
            new ArtifactResolutionProfileImpl(httpClient());
        artifactResolutionProfile.setProcessor(new SAMLProcessorImpl(soapBinding()));
        return artifactResolutionProfile;
    

    @Bean
    public HTTPArtifactBinding artifactBinding(ParserPool parserPool, VelocityEngine velocityEngine) 
        return new HTTPArtifactBinding(parserPool, velocityEngine, artifactResolutionProfile());
    

    @Bean
    public HTTPSOAP11Binding soapBinding() 
        return new HTTPSOAP11Binding(parserPool());
    

    @Bean
    public HTTPPostBinding httpPostBinding() 
      return new HTTPPostBinding(parserPool(), velocityEngine());
    

    @Bean
    public HTTPRedirectDeflateBinding httpRedirectDeflateBinding() 
      return new HTTPRedirectDeflateBinding(parserPool());
    

    @Bean
    public HTTPSOAP11Binding httpSOAP11Binding() 
      return new HTTPSOAP11Binding(parserPool());
    

    @Bean
    public HTTPPAOS11Binding httpPAOS11Binding() 
      return new HTTPPAOS11Binding(parserPool());
    

    // Processor
  @Bean
  public SAMLProcessorImpl processor() 
    Collection<SAMLBinding> bindings = new ArrayList<SAMLBinding>();
    bindings.add(httpRedirectDeflateBinding());
    bindings.add(httpPostBinding());
    bindings.add(artifactBinding(parserPool(), velocityEngine()));
    bindings.add(httpSOAP11Binding());
    bindings.add(httpPAOS11Binding());
    return new SAMLProcessorImpl(bindings);
  
    @Bean
    public FilterChainProxy samlFilter() throws Exception 
        List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
                samlEntryPoint()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
                samlLogoutFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
                metadataDisplayFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
                samlWebSSOProcessingFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
                samlWebSSOHoKProcessingFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
                samlLogoutProcessingFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"),
                samlIDPDiscovery()));
        return new FilterChainProxy(chains);
    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    


    @Override  
    protected void configure(HttpSecurity http) throws Exception 
        http
            .httpBasic()
                .authenticationEntryPoint(samlEntryPoint());
        http
          .csrf()
            .disable();
        http
            .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
            .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class)
            .addFilterBefore(sessionRepositoryFilter(sessionRepository(), httpSessionStrategy()),
                ChannelProcessingFilter.class)
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        http        
            .authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/publicUrl").permitAll()
            .antMatchers("/app/**").permitAll()
            .antMatchers("/error").permitAll()
            .antMatchers("/saml/**").permitAll()
            .antMatchers("/landing").authenticated();

        http
            .logout()
                .logoutSuccessUrl("/");
    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth
            .authenticationProvider(samlAuthenticationProvider());
     

    @Bean
    public HttpSessionStrategy httpSessionStrategy() 
    
        return new HeaderHttpSessionStrategy();
        

    @Bean
    public SessionRepositoryFilter<ExpiringSession> sessionRepositoryFilter(
                    SessionRepository<ExpiringSession> sessionRepository, HttpSessionStrategy httpSessionStrategy) 
    
        SessionRepositoryFilter<ExpiringSession> sessionRepositoryFilter = new SessionRepositoryFilter<>(
                        sessionRepository);
        sessionRepositoryFilter.setHttpSessionStrategy(httpSessionStrategy);
        return sessionRepositoryFilter;
    

    @Bean
    public SessionRepository<ExpiringSession> sessionRepository() 
    
        return new JPASessionRepository(1800);
        

实现 SessionRepository 的 JPA Session 存储库是:

  package com.security.repositories.session;
    public class JPASessionRepository implements SessionRepository<ExpiringSession> 
    

        private static final Logger LOG = LogManager.getLogger(JPASessionRepository.class);

        private int maxInactiveInterval = -1;

        @Autowired
        private SpringSessionRepository springSessionRepository;

        public JPASessionRepository() 
                    
        
        public JPASessionRepository(int maxInactiveInterval) 
        
            this.maxInactiveInterval = maxInactiveInterval;
            
        @Override
        public ExpiringSession createSession() 
        
            ExpiringSession result = new MapSession();
            result.setMaxInactiveIntervalInSeconds(maxInactiveInterval);
            return result;
           

        @Transactional
        @Override
        public void save(ExpiringSession session) 
        
            springSessionRepository.save(convertToDomain(session));
        

        @Transactional
        @Override
        public ExpiringSession getSession(String id) 
        
            SessionEntity sessionEntity = springSessionRepository.findOne(id);
            ExpiringSession saved = null;
            if(sessionEntity != null)
            
                saved = convertToSession(sessionEntity);
            

            if (saved == null) 
            
                return null;
            
            if (saved.isExpired()) 
            
                delete(saved.getId());
                return null;
            
            return saved;
           
        @Override
        public void delete(String id) 
        
            SessionEntity currentSession = springSessionRepository.findOne(id);
            if (null != currentSession) 
            
                springSessionRepository.delete(id);
            
        
        private SessionEntity convertToDomain(ExpiringSession session) 
        
            SessionEntity sessionEntity = new SessionEntity();
            sessionEntity.setId(session.getId());
            sessionEntity.setLastAccessedTime(session.getLastAccessedTime());
            sessionEntity.setCreationTime(session.getCreationTime());
            sessionEntity.setData(serializeAttributes(session));
            return sessionEntity;
        
         byte[] serializeAttributes(ExpiringSession session) 
        
            Map<String, Object> attributes = new HashMap<>();
            for (String attrName : session.getAttributeNames()) 
            
                attributes.put(attrName, session.getAttribute(attrName));

            
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer;
            try 
            
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
                objectOutputStream.writeObject(new SessionAttributes(attributes));
                buffer = out.toByteArray();
                objectOutputStream.close();
             
            catch (IOException e) 
            
                throw new RuntimeException(e);
            

            return buffer;
            

        private ExpiringSession convertToSession(SessionEntity sessionEntity) 
        
            MapSession mapSession = new MapSession();
            mapSession.setId(sessionEntity.getId());
            mapSession.setLastAccessedTime(sessionEntity.getLastAccessedTime());
            mapSession.setCreationTime(sessionEntity.getCreationTime());
            mapSession.setMaxInactiveIntervalInSeconds(this.maxInactiveInterval);

            SessionAttributes attributes = deserializeAttributes(sessionEntity);
            if (attributes != null) 
            
                for (Map.Entry<String, Object> attribute : attributes.getAttributes().entrySet()) 
                
                    mapSession.setAttribute(attribute.getKey(), attribute.getValue());
                
            
            return mapSession;
               
        private SessionAttributes deserializeAttributes(SessionEntity sessionEntity) 
        
            SessionAttributes attributes = null;
            if (sessionEntity.getData() != null && sessionEntity.getData().length > 0) 
            
                try 
                
                    ObjectInputStream objectInputStream = new ObjectInputStream(
                                    new ByteArrayInputStream(sessionEntity.getData()));

                    Object obj = objectInputStream.readObject();                    

                    attributes = (SessionAttributes) obj;
                    objectInputStream.close();
                 
                catch (IOException | ClassNotFoundException e) 
                
                    LOG.warn(e);
                    //FIXME:How should this exception be handled?
                
            
            return attributes;
            

        public Integer getDefaultMaxInactiveInterval() 
        
            return maxInactiveInterval;
            
        public void setDefaultMaxInactiveInterval(int maxInactiveInterval) 
        
            this.maxInactiveInterval = maxInactiveInterval;
            

        public SpringSessionRepository getSpringSessionRepository() 
        
            return springSessionRepository;
            
        public void setSpringSessionRepository(SpringSessionRepository springSessionRepository) 
        
            this.springSessionRepository = springSessionRepository;
        
    

SP 元数据

 <?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="http___localhost_8080_spring-boot-security-saml2-sample_saml_web_metadata" entityID="http://localhost:8080/spring-boot-security-saml2-sample/saml/web/metadata"><md:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>x509 certificate data</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:KeyDescriptor use="encryption"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>--x509 certificate</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml/SingleLogout"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8080/saml/SingleLogout"/><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat><md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml/SSO" index="0" isDefault="true"/><md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="http://localhost:8080/saml/SSO" index="1"/></md:SPSSODescriptor></md:EntityDescriptor>

IDP 元数据:-

    <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8081/OpenAM">
    <IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <KeyDescriptor use="signing">
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:X509Data>
    <ds:X509Certificate>
   sample certificate
    </ds:X509Certificate>
    </ds:X509Data>
    </ds:KeyInfo>
    </KeyDescriptor>
    <ArtifactResolutionService index="0" isDefault="true" Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/ArtifactResolver/metaAlias/idp"/>
    <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/IDPSloRedirect/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPSloRedirect/metaAlias/idp"/>
    <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/IDPSloPOST/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPSloPOST/metaAlias/idp"/>
    <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/IDPSloSoap/metaAlias/idp"/>
    <ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/IDPMniRedirect/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPMniRedirect/metaAlias/idp"/>
    <ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/IDPMniPOST/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPMniPOST/metaAlias/idp"/>
    <ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/IDPMniSoap/metaAlias/idp"/>
    <NameIDFormat>
    urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
    </NameIDFormat>
    <NameIDFormat>
    urn:oasis:names:tc:SAML:2.0:nameid-format:transient
    </NameIDFormat>
    <NameIDFormat>
    urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
    </NameIDFormat>
    <NameIDFormat>
    urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
    </NameIDFormat>
    <NameIDFormat>
    urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
    </NameIDFormat>
    <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos</NameIDFormat>
    <NameIDFormat>
    urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
    </NameIDFormat>
    <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/SSORedirect/metaAlias/idp"/>
    <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/SSOPOST/metaAlias/idp"/>
    <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/SSOSoap/metaAlias/idp"/>
    <NameIDMappingService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/NIMSoap/metaAlias/idp"/>
    <AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/AIDReqSoap/IDPRole/metaAlias/idp"/>
    <AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" Location="http://localhost:8081/OpenAM/AIDReqUri/IDPRole/metaAlias/idp"/>
    </IDPSSODescriptor>
    </EntityDescriptor>

请帮我解决问题

【问题讨论】:

请发布您的 IDP 和 SP 元数据。 @blur0224 更新了 SP 和 IDP 元数据 你检查过这个吗? github.com/ulisesbocchio/spring-boot-security-saml。使 Spring Boot 和 SAML 之间的配置更容易 【参考方案1】:

此错误通常是由于 EntityID 以某种方式不匹配造成的。看起来您的元数据配置正确,所以我做了一些查找并找到了this。

确保应用程序在发送请求和接收响应期间使用相同的 HttpSession。通常,当从 localhost 地址或 http 方案初始化身份验证请求时会出现此问题,而在公共主机名或 https 方案中接收到响应时。例如,从 URL https://host:port/app/saml/login 初始化身份验证时,必须在https://host;port/app/saml/SSO 接收响应,而不是http://host:port/app/saml/SSO 或https://localhost:port/app/saml/SSO。

可以通过重新配置上下文提供程序来禁用 InResponseToField 的检查,如下所示:

<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl">
  <property name="storageFactory">
    <bean class="org.springframework.security.saml.storage.EmptyStorageFactory"/>
  </property>
</bean>

您应该注意,这只能用于开发目的。您可能应该使用Spring Profiles 在本地启用此配置,因为它不太安全。

【讨论】:

以上是关于Spring session 与 spring security saml 的集成的主要内容,如果未能解决你的问题,请参考以下文章

spring session

Spring session 与 spring security saml 的集成

Spring+shiro session与线程池的坑

帅气的 Spring Session 功能,基于 Redis 实现分布式会话,还可以整合 Spring Security!

如何从多个服务器获取与 Spring Security 和 Spring Session 相同的会话

SpringSpringBoot + SpringSession + Redis 实现Session共享