【问题标题】:Spring Security SAML plugin - No hosted service provider is configured exceptionSpring Security SAML 插件 - 未配置托管服务提供商异常
【发布时间】:2016-04-20 04:40:36
【问题描述】:

我正在尝试使用 Spring Security SAML 扩展将 SAML SSO 与 Spring Security 集成。之前,我成功地运行了一个在这里找到的概念证明:https://github.com/vdenotaris/spring-boot-security-saml-sample。不幸的是,将配置移至我的项目后,它无法正常工作。

分析日志后,我发现我的应用程序 (SP) 正在从提供的 URL 正确下载 IdP 元数据。但是,在尝试通过在浏览器中尝试https://localhost:8443/saml/metadata 来下载我的 SP 的元数据后,会引发以下异常:

javax.servlet.ServletException: Error initializing metadata
at org.springframework.security.saml.metadata.MetadataDisplayFilter.processMetadataDisplay(MetadataDisplayFilter.java:120)
at org.springframework.security.saml.metadata.MetadataDisplayFilter.doFilter(MetadataDisplayFilter.java:88)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1645)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:564)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1111)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:498)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1045)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:98)
at org.eclipse.jetty.server.Server.handle(Server.java:461)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:284)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:244)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:534)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.opensaml.saml2.metadata.provider.MetadataProviderException: No hosted service provider is configured and no alias was selected
    at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalEntity(SAMLContextProviderImpl.java:311)
    at org.springframework.security.saml.context.SAMLContextProviderImpl.populateLocalContext(SAMLContextProviderImpl.java:216)
    at org.springframework.security.saml.context.SAMLContextProviderImpl.getLocalEntity(SAMLContextProviderImpl.java:107)
    at org.springframework.security.saml.metadata.MetadataDisplayFilter.processMetadataDisplay(MetadataDisplayFilter.java:114)
    ... 24 more

调试后,我无法弄清楚为什么 Spring 无法找出我的应用程序的实体 ID。我是这样设置的:

// Filter automatically generates default SP metadata
@Bean
public MetadataGenerator metadataGenerator() {
    MetadataGenerator metadataGenerator = new MetadataGenerator();
    metadataGenerator.setEntityId(environment.getRequiredProperty("saml.entity-id"));
    metadataGenerator.setEntityBaseURL("URL is here");
    metadataGenerator.setExtendedMetadata(extendedMetadata());
    metadataGenerator.setIncludeDiscoveryExtension(false);
    metadataGenerator.setKeyManager(keyManager());
    return metadataGenerator;
}

当然 saml.entity-id 属性是从我的配置中正确下载的。整个安全配置在这里:https://gist.github.com/mc-suchecki/671ecb4d5ae4bae17f81

过滤器的顺序正确 - 元数据生成器过滤器在 SAML 过滤器之前。我不确定这是否相关 - 我想不是 - 但我的应用程序没有使用 Spring Boot - 示例应用程序(配置源)是。

提前感谢您的帮助。

【问题讨论】:

  • 您是否尝试过在浏览器中使用 url : localhost:8443/<Application Context Root>/saml/metadata 来下载元数据?您尝试的网址根本没有应用程序名称。
  • 我认为URL是正确的-如果不是,将有404状态码而不是500状态码,没有例外。
  • 我在使用 spring-saml-extension 进行 SSO 时也遇到了这个错误。你能告诉我 saml.entity-id 属性的值吗
  • 准确地说是 SAMLContextProviderImpl 类中的方法的代码 sn-p,您会在其中遇到错误。您的实体 ID 似乎为空。 void populateLocalEntity(SAMLMessageContext samlContext) 抛出 MetadataProviderException { String localEntityId = samlContext.getLocalEntityId(); QName localEntityRole = samlContext.getLocalEntityRole(); if (localEntityId == null) { throw new MetadataProviderException("未配置托管服务提供商,未选择别名"); }
  • 是的,完全正确 - 我的 SAMLContext 中的本地实体 ID 为空。但问题是 - 为什么?

标签: java spring spring-security spring-saml


【解决方案1】:

您是否配置了 IDP?

【讨论】:

  • 我认为您的 IDP 中没有任何带有 saml.entity-id 的条目
  • 我的 IDP 是通过 Web 界面配置的,当我从另一个应用程序中使用它时它运行良好。问题出在 SP 方面,我对此 100% 肯定。我的 SP 还根据日志正确下载 IDP 元数据。
【解决方案2】:

在 MetadataGenerator 中,entityId 是一个共享密钥,您可以使用它与您的 IDP 沟通您的应用程序想要访问它。在 IDP 端有一个 samlConfiguration,您需要在其中输入相同的 entityId 以使您的应用程序能够访问 IDP 用户。

<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
    <bean class="org.springframework.security.saml.metadata.MetadataGenerator">              
        <property name="entityId" value="****"/>
        <property name="extendedMetadata">
            <bean class="org.springframework.security.saml.web.MyExtendedMetadata">
                <property name="signMetadata" value="true"/>                        
                <property name="signingKey" value="****"/>
                <property name="encryptionKey" value="****"/>
            </bean>
        </property>
    </bean>
</constructor-arg>

【讨论】:

  • 不幸的是,这不是问题所在。我的 IDP 是通过 Web 界面配置的,当我从另一个应用程序使用它时它运行良好。问题出在 SP 方面,我对此 100% 肯定。我的 SP 还根据日志正确下载 IDP 元数据。
【解决方案3】:

我这周发现了这个问题。过滤器有问题。其中一种方法是创建“samlFilter”,如下所示:

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);
}

之后,另一种方法是为 Spring 设置整个过滤器链,如下所示:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic().authenticationEntryPoint(samlEntryPoint());
    http.csrf().disable();
    http.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
        .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class);
    http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/error").permitAll()
        .antMatchers("/saml/**").permitAll().anyRequest().authenticated();
    http.logout().logoutSuccessUrl("/");
}

那是完全正确的。但是,当我使用 Jetty 服务器启动应用程序时,我试图仅将“samlFilter”连接到应用程序上下文。因此,要求在“metadataDisplayFilter”之前的“metadataGeneratorFilter”根本没有添加到过滤器链中。当我将“samlFilter”更改为“springSecurityFilter”时,一切都开始工作了。由于我对 Jetty 的非标准使用,这并不容易找到。

感谢您的帮助!

【讨论】:

  • " 当我将 'samlFilter' 更改为 'springSecurityFilter' 时,一切都开始工作了。" 你能提供一个代码示例吗?你的意思是你在securityContext.xml 中添加了一个Spring IOC Ref 吗?除了 web.xml springSecurityFilterChain 中定义的 Servlet 之外,我找不到任何“springSecurityFilter”
  • 我遇到了类似的问题。您能否分享一个代码示例或详细说明“将 samlFilter 更改为 springSecurityFilter”?
  • 对我来说,基本上是一样的故事:SAML 配置是在另一个 Http 配置之后加载的。我已经通过实现@Order 注释解决了这个问题(比其他配置更早地加载 SAML 配置)。
【解决方案4】:

我可以通过为每个过滤器添加FilterRegistrationBean,从 Spring Boot 的常规(非安全)过滤器自动注册中排除 SAML 过滤器来解决此问题,例如

  @Bean
  public FilterRegistrationBean disableSAMLEntryPoint() {
    final FilterRegistrationBean registration = 
        new FilterRegistrationBean<>(samlEntryPoint());
    registration.setEnabled(false);
    return registration;
  }

【讨论】:

    【解决方案5】:

    我发现了这个异常(未配置托管提供商)。我试图使用预配置的元数据。 如果您想使用预配置的元数据,则不需要 metadataGeneratorFilter,因此您可以将其从配置中删除(对此示例代码进行注释):

    @Override
        protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            http.authorizeRequests()
                .antMatchers("/logout.jsp").permitAll()
                .antMatchers("/loginFailed.jsp").permitAll()
                .antMatchers("/saml/**").permitAll()
            .anyRequest()
                    .authenticated()
                    .and().httpBasic().authenticationEntryPoint(samlEntryPoint())
                    .and().requiresChannel().anyRequest().requiresInsecure();
            
            http
                //.addFilterBefore(metadataGeneratorFilter, ChannelProcessingFilter.class)
                .addFilterAfter(samlFilter, BasicAuthenticationFilter.class);
            
            http.csrf().disable();
                    
        }
    

    接下来您要做的就是添加预配置的元数据。您可以将其作为 securityConfig.xml 根目录上的 bean 执行,或者将 bean 添加到您的缓存MetadataManager 上(均使用 xml 配置)。 这就是问题所在。如果选择预配置元数据,则必须为此 bean 添加扩展元数据,将其指定为本地:

        <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
        <constructor-arg>
            <list>
                <!-- PreConfigured SP MetaData -->
                <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
                    <constructor-arg>
                        <bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
                            <constructor-arg>
                                <bean class="java.util.Timer"/>
                            </constructor-arg>
                            <constructor-arg>
                                <bean class="org.opensaml.util.resource.ClasspathResource">
                                    <constructor-arg value="/metadata/spMetadata.xml"/>
                                </bean>
                            </constructor-arg>
                            <property name="parserPool" ref="parserPool"/>
                        </bean>
                    </constructor-arg>
                    <constructor-arg>
                        <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                            <property name="local" value="true"/>
                            <property name="alias" value="metadataAlias"/>
                        </bean>
                    </constructor-arg>
                </bean>
                <!-- IP MetaData -->
    

    【讨论】:

      猜你喜欢
      • 2014-04-24
      • 1970-01-01
      • 2019-07-23
      • 1970-01-01
      • 1970-01-01
      • 2016-05-08
      • 2016-11-14
      • 2011-07-27
      • 2016-12-14
      相关资源
      最近更新 更多