使用 spring-SAML 在 Pentaho 中多租户 SSO 登录后重定向

Posted

技术标签:

【中文标题】使用 spring-SAML 在 Pentaho 中多租户 SSO 登录后重定向【英文标题】:Redirect after multi-tenant SSO login in Pentaho with spring-SAML 【发布时间】:2021-01-22 05:37:35 【问题描述】:

我想使用 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 为租户 1 公开的登录页面。登录后,我被重定向到上一个 url,出现一般错误:see the screenshot

抱歉,出了点问题。 请重试或联系 您的系统管理员。

如果我访问 URL https://my.application.com/pentaho/Home,我将登录到 Pentaho 仪表板。这让我认为登录过程已经成功,但是在流程结束时重定向出现问题。事实上,我希望被重定向到 URL https://my.application.com/pentaho/Home 。 我可以在某处/以某种方式配置此重定向吗?

【问题讨论】:

【参考方案1】:

一种变通方法似乎解决了这个问题,但是我发现它在使用 Pentaho 的公开 API 时会与 SAML 身份验证产生冲突。要使用这些 API,这个解决方案并不好。


解决方法


更改 successRedirectHandler bean 的 defaultTargetUrlalwaysUseDefaultTargetUrl 属性(在 blueprint.xml 文件中声明Pentaho-SAML 插件)

  <!-- 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 登录问题的解决 我用一个覆盖 onAuthenticationSuccess 方法的自定义类扩展了 org.pentaho.platform.spring.security.saml.PentahoSamlAuthenticationSuccessHandler

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

【讨论】:

以上是关于使用 spring-SAML 在 Pentaho 中多租户 SSO 登录后重定向的主要内容,如果未能解决你的问题,请参考以下文章

这个标题日期从哪里来的响应? (带有Tomcat的spring-saml2)

ADFS 和 Spring-SAML 应用程序之间的循环

Spring-SAML:传入的 SAML 消息无效

如何在运行时在 spring-SAML 中添加新的 idp 元数据

ADFS spring-saml 依赖方没有配置 AssertionConsumerService

Spring-SAML 的 SSL 配置问题