【问题标题】:Integrate Spring Boot Security SAML with Azure AD Gallery app as multi tenant将 Spring Boot Security SAML 与 Azure AD Gallery 应用程序集成为多租户
【发布时间】:2020-10-27 07:23:57
【问题描述】:

我正在开发 Java Spring Boot 系统并尝试使用 SAML 单点登录与 Azure 非图库应用程序集成。

我找到了如何创建非图库应用程序,如何将非图库应用程序应用到 Azure 图库列表等。例如,此链接是关于配置 SAML SSO: Configure SAML-based single sign-on 所以我了解了 Azure 端的配置和程序。

我正在使用 Spring Security SAML 扩展。但是,除了基于 XML 的官方 SAML 扩展文档外,我做了很多研究,也找不到 Spring Boot 端配置。

顺便说一下,我的主要目标是将我们的组织应用添加到 Azure 库应用列表。我们的应用被多家公司使用,因此如果我们将组织应用添加到 Azure Gallery App list,我们的客户可以将其 Azure AD 帐户配置为 SSO 集成。

我的问题如下:

  1. 如何将 Azure Non-Gallery 应用集成到 Spring Boot 应用?
  2. 如何处理多个 Azure AD 租户?

有人帮我解决这个问题吗?


编辑: 目前,我使用 Spring Boot 和 Azure AD 非图库应用程序进行了单租户 SSO 登录。我使用 Azure AD 联合 XML 元数据 URL 配置了 IdP 元数据。您可以在下面查看源代码:

@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
    @Value("${security.saml2.metadata-url}")
    private String IdPMetadataURL;

    @Value("${server.ssl.key-alias}")
    private String keyStoreAlias;

    @Value("${server.ssl.key-store-password}")
    private String keyStorePassword;

    @Value("${server.port}")
    String port;

    @Value("${server.ssl.key-store}")
    private String keyStoreFile;

    @Autowired
    private SAMLUserService samlUserService;

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/saml/**", "/", "/login", "/home", "/about").permitAll()
                .anyRequest().authenticated()
                .and()
                .apply(saml())
                .webSSOProfileConsumer(getWebSSOProfileConsumerImpl())
                .userDetailsService(samlUserService)
                .serviceProvider()
                .keyStore()
                .storeFilePath(this.keyStoreFile)
                .password(this.keyStorePassword)
                .keyname(this.keyStoreAlias)
                .keyPassword(this.keyStorePassword)
                .and()
                .protocol("https")
                .hostname(String.format("%s:%s", "localhost", this.port))
                .basePath("/")
                .and()
                .identityProvider()
                .metadataFilePath(IdPMetadataURL)
                .and();
    }

    public WebSSOProfileConsumerImpl getWebSSOProfileConsumerImpl(){
        WebSSOProfileConsumerImpl consumer = new WebSSOProfileConsumerImpl();
        consumer.setMaxAuthenticationAge(26000000); //300 days
        return consumer;
    }
}

从现在开始,我需要生成 IdP 元数据 XML,而不是使用 IdP 元数据 URL。使用以下字段:

  • IdP 实体 ID
  • IdP SSO URL
  • IdP 公共证书

我正在考虑的过程是:

  1. 我们的客户在上面注册他们的 Azure AD IdP 字段
  2. 我的 Spring Boot 系统自动生成 IdP 元数据 XML
  3. 然后客户的 Azure AD SSO 可以集成到我们的系统中

如果有什么不对的地方请指教。

【问题讨论】:

    标签: spring-boot spring-security azure-active-directory saml-2.0 spring-saml


    【解决方案1】:

    最后我完成了动态 IDP 的解决方案。我使用了spring-boot-security-saml这个简化的项目。感谢ulisesbocchio这个实现它的人。 也非常感谢ledjon 分享了他的经验。

    这是我如何配置 http 安全性的 saml 部分

    http.apply(saml)
        .serviceProvider()
            .metadataGenerator()
            .entityId(LocalSamlConfig.LOCAL_SAML_ENTITY_ID)
            .entityBaseURL(entityBaseUrl)
            .includeDiscoveryExtension(false)
        .and()
            .sso()
            .successHandler(new SendToSuccessUrlPostAuthSuccessHandler(canvasAuthService))
        .and()
            .metadataManager(new LocalMetadataManagerAdapter(samlAuthProviderService))
            .extendedMetadata()
            .idpDiscoveryEnabled(false)
        .and()
            .keyManager()
            .privateKeyDERLocation("classpath:/saml/localhost.key.der")
            .publicKeyPEMLocation("classpath:/saml/localhost.cert")
        .and()
            .http()
                .authorizeRequests()
                .requestMatchers(saml.endpointsMatcher())
                .permitAll();
    

    这里的重要部分是 .metadataManager(new LocalMetadataManagerAdapter(samlAuthProviderService)) 这是我们在这里试图解决的问题。 samlAuthProviderService 对象是一个 Bean 管理的对象,它包含从数据库中实际检索元数据的逻辑,因此没有太多关于它的特别之处。但这是我的LocalMetadataManagerAdapter 大致的样子:

    @Slf4j
    public class LocalMetadataManagerAdapter extends CachingMetadataManager {
    
        private final SamlAuthProviderService samlAuthProviderService;
    
        public LocalMetadataManagerAdapter(SamlAuthProviderService samlAuthProviderService) throws MetadataProviderException {
            super(null);
            this.samlAuthProviderService = samlAuthProviderService;
        }
    
        @Override
        public boolean isRefreshRequired() {
            return false;
        }
    
        @Override
        public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException {
            // we don't really want to use our default at all, so we're going to throw an error
            // this string value is defined in the "classpath:/saml/idp-metadata.xml" file:
            // which is then referenced in application.properties as saml.sso.idp.metadata-location=classpath:/saml/idp-metadata.xml
            if("defaultidpmetadata".equals(entityID)) {
                throw exNotFound("Unable to process requests for default idp. Please select idp with ?idp=x parameter.");
            }
    
            EntityDescriptor staticEntity = super.getEntityDescriptor(entityID);
    
            if(staticEntity != null)
                return staticEntity;
    
            // we need to inject one, and try again:
            injectProviderMetadata(entityID);
    
            return super.getEntityDescriptor(entityID);
        }
    
        @SneakyThrows
        private void injectProviderMetadata(String entityID) {
            String xml =
                samlAuthProviderService.getMetadataForConnection(entityID)
                    .orElseThrow(() -> exRuntime("Unable to find metadata for entity: " + entityID));
    
            addMetadataProvider(new LocalMetadataProvider(entityID, xml));
    
            // this will force a refresh/re-wrap of the new entity
            super.refreshMetadata();
        }
    }
    

    这里的重要部分是 getEntityDescriptor() 的覆盖,它将在运行时被调用以获取元数据对象。我还通过覆盖isRefreshRequired() 来禁用刷新以返回false。您可以确定这对您的用例是否有意义。

    引用的LocalMetadataProvider 只是一个包装类,用于在需要时存储/返回 xml 字符串:

    public class LocalMetadataProvider extends AbstractReloadingMetadataProvider {
    
        private final String Id;
        private final String xmlData;
    
        public LocalMetadataProvider(String id, String xmlData) {
            this.Id = id;
            this.xmlData = xmlData;
    
            setParserPool(LocalBeanUtil.getBeanOrThrow(ParserPool.class));
        }
    
        @Override
        protected String getMetadataIdentifier() {
            return this.Id;
        }
    
        @Override
        protected byte[] fetchMetadata() throws MetadataProviderException {
            return xmlData.getBytes();
        }
    }
    

    最后我们可以将 idp 元数据 entityID 作为参数传递。并从数据库等中检索 entityID 元数据: /saml/login?idp=X 其中 X 是我们想要传递给 getEntityDescriptor() 的 entityID 值。

    【讨论】:

      【解决方案2】:

      我在 Spring Boot 中使用 Spring Security SAML 扩展。您使用哪个 SAML IdP 无关紧要,因为您只需要 IdP 元数据。您生成您的 SP 元数据并按照 MS 文档中的说明使用它。您可以查看 Spring Security SAML 文档。

      【讨论】:

      • 您好@Bernhard Thalmayr 感谢您的回答。我明白了,我将只使用 IdP 元数据,对吗?如果您看这里,我可以从 MS 收到 Base64 证书:docs.microsoft.com/en-us/azure/active-directory/manage-apps/…。有的话可以举个简单的例子吗?
      • 另外,我们的客户(公司)似乎需要将我们的应用程序配置到他们的 Azure。所以必须从数据库中读取 IdP 元数据。这种情况下怎么配置?
      • IdP 元数据包含 SAML 签名证书,但 MS(ADFS 或 Azure AD)元数据导出包含的不仅仅是 IDPSSODescriptor,因此您只需提取相关的 SAML 元数据。
      • 嗨@Bernhard!感谢您的评论。我们可以使用 IdP entityID、IdP SSO URL、公共证书的字段生成 IdP 元数据 XML 吗?
      【解决方案3】:

      要将您的应用程序列入 Azure Gallery 应用程序列表,请通过document。请按照文档中所述完成整个过程,以便在 azure Gallery 中列出您的应用程序。

      仅针对库中已存在的应用程序,才会提及应用程序的配置端。对于非图库应用,您需要在应用端配置 azure AD 元数据值。

      【讨论】:

      • 感谢您的回答。我的主要问题是如何将 Java Spring Boot 集成到 Azure 非图库应用程序中。是否有任何文档或示例如何使用 Azure 凭据配置 Spring SAML。
      猜你喜欢
      • 2021-05-04
      • 2020-12-09
      • 1970-01-01
      • 2017-11-28
      • 1970-01-01
      • 2017-04-24
      • 1970-01-01
      • 2017-07-07
      • 1970-01-01
      相关资源
      最近更新 更多