使用 spring-SAML 在 Pentaho 中进行多租户 SSO 登录后重定向
Redirect after multi-tenant SSO login in Pentaho with spring-SAML
我想使用 SAML 插件 Link(扩展 Spring SAML)为 Pentaho 配置多租户 SSO 登录。
现在我已经在 blueprint.xml 中声明了多个服务提供商 (SP) 和身份提供商 (IDP)(每个租户一个,外加一个公共 SP)。然而,在登录流程结束时,我没有被重定向到主页,而是被重定向到一个通用错误页面。
这是 SAML 插件中 blueprint.xml 设置的示例:
<bean id="spResourceFactoryCommon" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.sp.metadata.filesystem.common}" />
</map>
</argument>
<argument value="${saml.sp.metadata.classpath.fallback}" />
</bean>
<bean id="spResourceFactoryTenant1" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.sp.metadata.filesystem.tenant1}" />
</map>
</argument>
<argument value="${saml.sp.metadata.classpath.fallback}" />
</bean>
<bean id="spResourceFactoryTenant2" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.sp.metadata.filesystem.tenant2}" />
</map>
</argument>
<argument value="${saml.sp.metadata.classpath.fallback}" />
</bean>
<bean id="idpResourceFactoryTenant1" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.idp.metadata.filesystem.tenant1}" />
</map>
</argument>
<argument value="${saml.idp.metadata.classpath.fallback}" />
</bean>
<bean id="idpResourceFactoryTenant2" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.idp.metadata.filesystem.tenant2}" />
</map>
</argument>
<argument value="${saml.idp.metadata.classpath.fallback}" />
</bean>
<!-- MetadataManager configuration - paths to metadata of IDPs and SP's -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager" depends-on="pentahoSamlBootstrap">
<argument>
<list>
<!-- sp metadata with extended metadata -->
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="spResourceFactoryCommon" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.incoming.logout.request.signed}"/>
<property name="alias" value="pentahoCommon"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="spResourceFactoryTenant1" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.incoming.logout.request.signed}"/>
<property name="alias" value="tenant1sp"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="spResourceFactoryTenant2" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.incoming.logout.request.signed}"/>
<property name="alias" value="tenant2sp"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
<!-- idp metadata -->
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="idpResourceFactoryTenant1" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.outgoing.logout.request.signed}"/>
<property name="requireLogoutResponseSigned" value="${ensure.outgoing.logout.response.signed}"/>
<property name="alias" value="tenant1idp"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="idpResourceFactoryTenant2" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.outgoing.logout.request.signed}"/>
<property name="requireLogoutResponseSigned" value="${ensure.outgoing.logout.response.signed}"/>
<property name="alias" value="tenant2idp"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
</list>
</argument>
<property name="keyManager" ref="keyManager" />
<property name="defaultIDP" value="${saml.idp.url}" />
</bean>
有了这个配置,当我去 url
https://my.application.com/pentaho/alias/tenant1sp/sp?idp=tenant.1.name
我被重定向到 IDP 为 tenant1 公开的登录页面。登录后,我被重定向到之前的 url 并收到一般错误:see the screenshot
Sorry, something went wrong.
Please try again or contact
your system administrator.
如果我去 URL https://my.application.com/pentaho/Home I am logged to the Pentaho dashboard. This makes me think that the login process has been successful but something went wrong with the redirect at the end of the flow. Indeed I would expect to be redirected to the URL https://my.application.com/pentaho/Home 。 我可以配置这个重定向 somewhere/somehow 吗?
一种解决方法似乎可以解决这个问题,但是我发现在使用 Pentaho 的公开 API 时它会与 SAML 身份验证产生冲突。要使用那些 API,此解决方案并不好。
解决方法
更改 defaultTargetUrl 和 alwaysUseDefaultTargetUrl 属性 successRedirectHandler bean (在 Pentaho-SAML 插件的 blueprint.xml 文件中声明)
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler" class="org.pentaho.platform.spring.security.saml.PentahoSamlAuthenticationSuccessHandler"
init-method="afterPropertiesSet">
<property name="defaultTargetUrl" value="https://my.application.com/pentaho/Home"/>
<property name="alwaysUseDefaultTargetUrl" value="true"/>
<property name="requireProxyWrapping" value="false"/>
</bean>
编辑:解决 API 登录问题
我用自定义的 org.pentaho.platform.spring.security.saml.PentahoSamlAuthenticationSuccessHandler class 扩展了 onAuthenticationSuccess 方法
private String contextPath;
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
//Apply the redirect to the Pentaho console Home if and only if the original targetUrl is not a Pentaho exposed API but the home (contextPath/Home)
if (!savedRequest.getRedirectUrl().contains("API")
&& savedRequest.getRedirectUrl().contains(contextPath+"/Home")) {
//The Pentaho console Home is set as defaultTargetUrl in the blueprint.xml
this.setAlwaysUseDefaultTargetUrl(true);
log.info("The request is not on a Pentaho API. Forcing the target URL to redirect to the defaultTargetUrl");
}
super.onAuthenticationSuccess(request, response, authentication);
//retore the original value of alwaysUseDefaultTargetUrl
this.setAlwaysUseDefaultTargetUrl(false);
}
我想使用 SAML 插件 Link(扩展 Spring SAML)为 Pentaho 配置多租户 SSO 登录。
现在我已经在 blueprint.xml 中声明了多个服务提供商 (SP) 和身份提供商 (IDP)(每个租户一个,外加一个公共 SP)。然而,在登录流程结束时,我没有被重定向到主页,而是被重定向到一个通用错误页面。
这是 SAML 插件中 blueprint.xml 设置的示例:
<bean id="spResourceFactoryCommon" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.sp.metadata.filesystem.common}" />
</map>
</argument>
<argument value="${saml.sp.metadata.classpath.fallback}" />
</bean>
<bean id="spResourceFactoryTenant1" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.sp.metadata.filesystem.tenant1}" />
</map>
</argument>
<argument value="${saml.sp.metadata.classpath.fallback}" />
</bean>
<bean id="spResourceFactoryTenant2" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.sp.metadata.filesystem.tenant2}" />
</map>
</argument>
<argument value="${saml.sp.metadata.classpath.fallback}" />
</bean>
<bean id="idpResourceFactoryTenant1" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.idp.metadata.filesystem.tenant1}" />
</map>
</argument>
<argument value="${saml.idp.metadata.classpath.fallback}" />
</bean>
<bean id="idpResourceFactoryTenant2" class="org.pentaho.platform.spring.security.saml.resources.MetadataResourceFactory">
<argument>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="org.opensaml.util.resource.FilesystemResource" value="${saml.idp.metadata.filesystem.tenant2}" />
</map>
</argument>
<argument value="${saml.idp.metadata.classpath.fallback}" />
</bean>
<!-- MetadataManager configuration - paths to metadata of IDPs and SP's -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager" depends-on="pentahoSamlBootstrap">
<argument>
<list>
<!-- sp metadata with extended metadata -->
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="spResourceFactoryCommon" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.incoming.logout.request.signed}"/>
<property name="alias" value="pentahoCommon"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="spResourceFactoryTenant1" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.incoming.logout.request.signed}"/>
<property name="alias" value="tenant1sp"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="spResourceFactoryTenant2" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.incoming.logout.request.signed}"/>
<property name="alias" value="tenant2sp"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
<!-- idp metadata -->
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="idpResourceFactoryTenant1" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.outgoing.logout.request.signed}"/>
<property name="requireLogoutResponseSigned" value="${ensure.outgoing.logout.response.signed}"/>
<property name="alias" value="tenant1idp"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<argument>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<argument>
<bean class="java.util.Timer"/>
</argument>
<argument>
<bean factory-ref="idpResourceFactoryTenant2" factory-method="factoryResource" />
</argument>
<property name="parserPool" ref="parserPool"/>
</bean>
</argument>
<argument>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="${saml.discovery.idp.enabled}"/>
<property name="requireLogoutRequestSigned" value="${ensure.outgoing.logout.request.signed}"/>
<property name="requireLogoutResponseSigned" value="${ensure.outgoing.logout.response.signed}"/>
<property name="alias" value="tenant2idp"/>
<property name="local" value="true"/>
</bean>
</argument>
</bean>
</list>
</argument>
<property name="keyManager" ref="keyManager" />
<property name="defaultIDP" value="${saml.idp.url}" />
</bean>
有了这个配置,当我去 url https://my.application.com/pentaho/alias/tenant1sp/sp?idp=tenant.1.name 我被重定向到 IDP 为 tenant1 公开的登录页面。登录后,我被重定向到之前的 url 并收到一般错误:see the screenshot
Sorry, something went wrong. Please try again or contact your system administrator.
如果我去 URL https://my.application.com/pentaho/Home I am logged to the Pentaho dashboard. This makes me think that the login process has been successful but something went wrong with the redirect at the end of the flow. Indeed I would expect to be redirected to the URL https://my.application.com/pentaho/Home 。 我可以配置这个重定向 somewhere/somehow 吗?
一种解决方法似乎可以解决这个问题,但是我发现在使用 Pentaho 的公开 API 时它会与 SAML 身份验证产生冲突。要使用那些 API,此解决方案并不好。
解决方法
更改 defaultTargetUrl 和 alwaysUseDefaultTargetUrl 属性 successRedirectHandler bean (在 Pentaho-SAML 插件的 blueprint.xml 文件中声明)
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler" class="org.pentaho.platform.spring.security.saml.PentahoSamlAuthenticationSuccessHandler"
init-method="afterPropertiesSet">
<property name="defaultTargetUrl" value="https://my.application.com/pentaho/Home"/>
<property name="alwaysUseDefaultTargetUrl" value="true"/>
<property name="requireProxyWrapping" value="false"/>
</bean>
编辑:解决 API 登录问题 我用自定义的 org.pentaho.platform.spring.security.saml.PentahoSamlAuthenticationSuccessHandler class 扩展了 onAuthenticationSuccess 方法
private String contextPath;
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
//Apply the redirect to the Pentaho console Home if and only if the original targetUrl is not a Pentaho exposed API but the home (contextPath/Home)
if (!savedRequest.getRedirectUrl().contains("API")
&& savedRequest.getRedirectUrl().contains(contextPath+"/Home")) {
//The Pentaho console Home is set as defaultTargetUrl in the blueprint.xml
this.setAlwaysUseDefaultTargetUrl(true);
log.info("The request is not on a Pentaho API. Forcing the target URL to redirect to the defaultTargetUrl");
}
super.onAuthenticationSuccess(request, response, authentication);
//retore the original value of alwaysUseDefaultTargetUrl
this.setAlwaysUseDefaultTargetUrl(false);
}