Spring Security SAML插件 - 没有托管服务提供程序配置异常

时间:2016-01-14 18:03:58

标签: java spring spring-security spring-saml

我正在尝试使用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 - 并且示例应用程序(配置源)是。

提前感谢您的帮助。

5 个答案:

答案 0 :(得分:7)

本周我发现了这个问题。过滤器出现问题。其中一种方法是创建' 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服务器启动应用程序时,我试图仅连接&#39; samlFilter&#39;到应用程序上下文。因此,&#39; metadataGeneratorFilter&#39; 根本没有添加到过滤器链中。当我改变了#samlFilter&#39;到了SpringSecurityFilter&#39;,一切都开始了。由于我对Jetty的非标准使用,这并不容易找到。

感谢您的帮助!

答案 1 :(得分:1)

您是否配置了IDP?

答案 2 :(得分:1)

在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>

答案 3 :(得分:1)

我发现了这个异常(没有配置托管提供程序)。我试图使用预先配置的元数据。 如果要使用预先配置的元数据,则不需要 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 执行此操作,也可以在 cachingMetadataManager 上添加 bean(均使用 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 -->

答案 4 :(得分:0)

我能够通过为每个过滤器添加一个select b.vnd_nbr , b.upc_nbr , b.net_cost , min(b.inv_date) as inv_date_bgn , max(b.inv_date) as inv_date_end from ( select a.vnd_nbr , a.upc_nbr , a.inv_date , a.net_cost --determine the island number , sum(a.is_gap_start) over (partition by a.vnd_nbr, a.upc_nbr order by a.inv_date asc rows between unbounded preceding and 1 preceding) as island_nbr from ( select t.vnd_nbr , t.upc_nbr , t.inv_date , t.net_cost --check if the next row meets either condition to start new date range (4 day gap, change in net_cost) , case when datediff(d, t.inv_date, lead(t.inv_date, 1, t.inv_date) over (partition by t.vnd_nbr, t.upc_nbr order by t.inv_date asc)) >= 4 or t.net_cost <> lead(t.net_cost, 1, t.net_cost) over (partition by t.vnd_nbr, t.upc_nbr order by t.inv_date asc) then 1 else 0 end as is_gap_start from data_table as t ) as a ) as b group by b.vnd_nbr , b.upc_nbr , b.net_cost , isnull(b.island_nbr, 0) --forces each island to be a separate row order by b.vnd_nbr , b.upc_nbr , isnull(b.island_nbr, 0) (例如

)从Spring Boot的常规(非安全)过滤器的自动注册中排除SAML过滤器来解决此问题
+---------+------------+----------+--------------+--------------+
| vnd_nbr |  upc_nbr   | net_cost | inv_date_bgn | inv_date_end |
+---------+------------+----------+--------------+--------------+
|  183020 | 2840000211 | 1.47     | 2016-12-23   | 2016-12-26   |
|  183020 | 2840000211 | 1.39     | 2016-12-31   | 2017-01-03   |
|  183020 | 2840000211 | 1.39     | 2017-01-09   | 2017-01-16   |
+---------+------------+----------+--------------+--------------+