Spring 安全 SAML 插件 - 没有配置托管服务提供商异常
Spring Security SAML plugin - No hosted service provider is configured exception
我正在尝试使用 Spring 安全 SAML 扩展将 SAML SSO 与 Spring 安全集成。之前,我成功地 运行 在这里找到了一个概念证明: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.run(AbstractConnection.java:534)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
at org.eclipse.jetty.util.thread.QueuedThreadPool.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 引导 - 而示例应用程序(配置源)是。
提前感谢您的帮助。
您是否配置了 IDP?
在 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>
我这周发现了这个问题。过滤器有问题。其中一种方法是创建 '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' 连接到应用程序上下文。因此,需要在之前'metadataGeneratorFilter' 'metadataDisplayFilter'未添加到过滤器链中全部。当我将 'samlFilter' 更改为 'springSecurityFilter' 时,一切都开始工作了。这不容易找到,因为我 non-standard 使用 Jetty。
感谢您的帮助!
我能够通过从 Spring Boot 的常规(非安全)过滤器的自动注册中排除 SAML 过滤器来解决这个问题,方法是为每个过滤器添加 FilterRegistrationBean
,例如
@Bean
public FilterRegistrationBean disableSAMLEntryPoint() {
final FilterRegistrationBean registration =
new FilterRegistrationBean<>(samlEntryPoint());
registration.setEnabled(false);
return registration;
}
我发现了这个异常(没有配置托管提供商)。我正在尝试使用预配置的元数据。
如果你想使用预配置的元数据,你不需要 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 添加 extendedMetadata,将其指定为 local:
<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 -->
我正在尝试使用 Spring 安全 SAML 扩展将 SAML SSO 与 Spring 安全集成。之前,我成功地 运行 在这里找到了一个概念证明: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.run(AbstractConnection.java:534)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
at org.eclipse.jetty.util.thread.QueuedThreadPool.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 引导 - 而示例应用程序(配置源)是。
提前感谢您的帮助。
您是否配置了 IDP?
在 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>
我这周发现了这个问题。过滤器有问题。其中一种方法是创建 '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' 连接到应用程序上下文。因此,需要在之前'metadataGeneratorFilter' 'metadataDisplayFilter'未添加到过滤器链中全部。当我将 'samlFilter' 更改为 'springSecurityFilter' 时,一切都开始工作了。这不容易找到,因为我 non-standard 使用 Jetty。
感谢您的帮助!
我能够通过从 Spring Boot 的常规(非安全)过滤器的自动注册中排除 SAML 过滤器来解决这个问题,方法是为每个过滤器添加 FilterRegistrationBean
,例如
@Bean
public FilterRegistrationBean disableSAMLEntryPoint() {
final FilterRegistrationBean registration =
new FilterRegistrationBean<>(samlEntryPoint());
registration.setEnabled(false);
return registration;
}
我发现了这个异常(没有配置托管提供商)。我正在尝试使用预配置的元数据。 如果你想使用预配置的元数据,你不需要 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 添加 extendedMetadata,将其指定为 local:
<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 -->