当前位置: 首页 > 知识库问答 >
问题:

Spring SAML-在运行时读取和刷新IdP元数据

岳晟
2023-03-14

我使用WSO2和SSOCircle以及Spring-SAML扩展。我们目前正在测试配置,并在我们的应用程序上下文中定义了2个IdP和2个SP。因此,目前,我们在spring xml配置中有2个静态定义的IdP,并且正在工作。出于测试目的,我们结合使用CachingMetadataManager和ResourceBackedMetadataProvider,因此IdP元数据构建在我们的WAR档案中。样本:

<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
  <list>
    <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/wso2idp_metadata.xml"/>
            </bean>
          </constructor-arg>
          <property name="parserPool" ref="parserPool"/>
        </bean>
      </constructor-arg>
      <constructor-arg>
        <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
        </bean>
      </constructor-arg>
    </bean>
    <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/ssocircleidp_metadata.xml"/>
            </bean>
          </constructor-arg>
          <property name="parserPool" ref="parserPool"/>
        </bean>
      </constructor-arg>
      <constructor-arg>
        <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
        </bean>
      </constructor-arg>
    </bean>
  </list>
</constructor-arg>

对于生产,我们希望能够将我们的IdP元数据存储在一个数据库中(位于中央)。我希望能够添加、删除和修改元数据,而无需重新部署WAR或重启html" target="_blank">服务器。最初,我认为我可以覆盖CachingMetadataManager,并定义一个可以动态加载所有元数据提供程序的noarg构造函数,但这是不可能的,因为CachingMetadataManager只定义了一个必须接受MetadataProvider列表的构造函数。我最终做了以下事情:

<bean id="metadataList" class="org.arbfile.util.security.saml.DBMetadataProviderList">
  <constructor-arg ref="parserPool" />
  <constructor-arg>
    <bean class="java.util.Timer"/>
  </constructor-arg>
</bean>

<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
   <constructor-arg ref="metadataList" />
</bean>

Bean metadataList 可以简单地定义为:

public final class DBMetadataProviderList extends ArrayList<MetadataProvider>
{
  private final static Logger log = LoggerFactory.getLogger(DBMetadataProviderList.class);
  private ParserPool parser;

  public DBMetadataProviderList(ParserPool _parser, Timer _timer) throws MetadataProviderException
  {
    this.parser = _parser;
// Lookup metadata from DB
  }

}

这确实允许我动态读取 IdP 元数据。不过,在刷新方面,我的逻辑就崩溃了。我在Spring论坛上找到了这篇文章,但是它已经3到4岁了。动态读取、添加和更新 IdP 元数据、缓存 IdP 元数据以及按某个时间间隔刷新缓存的最佳方式是什么?在我的例子中,数据库表中的 1 行相当于单个 IdP 元数据定义。

共有1个答案

金高飞
2023-03-14

在阅读了几篇文章并扫描了源代码之后,我发现这个问题的答案比我想象的要复杂得多。实际上有3种不同的情况需要解决。

  1. 从数据库表中初始读取所有IdP元数据提供程序。
  2. 过期并重新读取IdP元数据XML数据
  3. 动态添加和删除提供程序,而无需更改配置或重新启动服务器

我将一次承担其中的每一个。第1项:可能有几种方法可以解决这个问题,但回顾上面的DBMetadataProviderList类(在我的OP中)作为快速而肮脏的解决方案。下面是更完整的构造函数代码:

//This constructor allows us to read in metadata stored in a database. 
public DBMetadataProviderList(ParserPool _parser, Timer _timer) throws MetadataProviderException
{
    this.parser = _parser;
    List<String> metadataProviderIds = getUniqueEntityIdListFromDB();
    for (final String mdprovId : metadataProviderIds)
    {
        DBMetadataProvider metadataProvider = new DBMetadataProvider(_timer, mdprovId);
        metadataProvider.setParserPool(this.parser);
        metadataProvider.setMaxRefreshDelay(480000); // 8 mins (set low for testing)
        metadataProvider.setMinRefreshDelay(120000); // 2 mins
        ExtendedMetadataDelegate md = new ExtendedMetadataDelegate(metadataProvider,  new ExtendedMetadata());
        add(md);
    }
}

为了解决第二个问题,我使用了< code > FilesystemMetadataProvider 作为指导,并创建了一个< code > dbmetadatprovider 类。通过扩展< code > AbstractReloadingMetadataProvider 类并实现< code>fetchMetadata()方法,我们借助opensaml实现了内置缓存刷新。以下是重要的部分(仅示例代码):

public class DBMetadataProvider extends AbstractReloadingMetadataProvider
{
  private String metaDataEntityId;  // unique Id for DB lookups

 /**
  * Constructor.
  * @param entityId the entity Id of the metadata.  Use as key to identify a database row.
  */
 public DBMetadataProvider(String entityId)
 {
    super();
    setMetaDataEntityId(entityId);
 }

 /**
  * Constructor.
  * @param backgroundTaskTimer timer used to refresh metadata in the background
  * @param entityId the entity Id of the metadata.  Use as key to identify a database row.
  */
 public DBMetadataProvider(Timer backgroundTaskTimer, String entityId)
 {
    super(backgroundTaskTimer);
    setMetaDataEntityId(entityId);
 }

 public String getMetaDataEntityId() { return metaDataEntityId;  }

 public void setMetaDataEntityId(String metaDataEntityId){ this.metaDataEntityId = metaDataEntityId; }

 @Override
 protected String getMetadataIdentifier() { return getMetaDataEntityId(); }

// This example code simply does straight JDBC
 @Override
 protected byte[] fetchMetadata() throws MetadataProviderException
 {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try
    {
        conn = JDBCUtility.getConnection();
        ps = conn.prepareStatement("select  bla bla bla ENTITY_ID = ?");
        ps.setString(1, getMetaDataEntityId());
        rs = ps.executeQuery();
        if (rs.next())
        {
            // include a modified date column in schema so that we know if row has changed
            Timestamp sqldt = rs.getTimestamp("MOD_DATE"); // use TimeStamp here to get full datetime
            DateTime metadataUpdateTime = new DateTime(sqldt.getTime(), ISOChronology.getInstanceUTC());
            if (getLastRefresh() == null || getLastUpdate() == null || metadataUpdateTime.isAfter(getLastRefresh()))
            {
                log.info("Reading IdP metadata from database with entityId = " + getMetaDataEntityId());
                Clob clob = rs.getClob("XML_IDP_METADATA");
                return clob2Bytes(clob);
            }
            return null;
        }
        else
        {
            // row was not found
            throw new MetadataProviderException("Metadata with entityId = '" + getMetaDataEntityId() + "' does not exist");
        }
    }
    catch (Exception e)
    {
        String msg = "Unable to query metadata from database with entityId = " + getMetaDataEntityId();
        log.error(msg, e);
        throw new MetadataProviderException(msg, e);
    }
    finally
    {
        // clean up connections
    }
  }

 }

这个资源帮助我获得了缓存重新加载元数据提供程序类的正确技术。最后,第#3项可以通过实现这篇文章来解决。

 类似资料:
  • 我正在集成spring security saml扩展以支持我的web应用程序中的SSO,我的应用程序应允许不同的客户将其IDP元数据和证书添加到我的webapp(这是一个SP),以便我的webapp可以针对其IDP启动SSO。 现在我正在我的java配置中定义一个“元数据”bean,在其中我将idp元数据添加到CachingMetadataManager。但是这种情况只发生一次,我无法弄清楚如何

  • Java类: 应用yml公司 应用程序模拟ecom。yml公司 当我击中时,http://localhost:8080/hello,我得到的回应是“Hello mock api.com!”。 如果我从应用程序中删除模拟的ecom。然后调用刷新后api调用http://localhost:8080/refresh要刷新上下文,我希望得到“Hello dev api.com!”但我收到了“你好,moc

  • 我在这个JTable上有问题。我这样编辑单元格 然后,我按Enter键提交更改。在这里,我希望gui用新的值刷新表。 但它们不显示,只有当我像这样更改选择时它们才显示 这是在TableModel中调用的。

  • 在阅读如何在运行时访问元组元素时,我想到了以下使用运行时索引获取元组元素的实现 我自己用GCC11.2试过了。我调用了API来检索元组,但出现了以下错误 找到下面的代码(实时演示)

  • 我们正在使用keyclope身份代理将身份验证联合到外部IDP。标识提供程序的类型为OpenID Connect v1.0。此外,我们正在PKCE中使用OIDC授权代码流。 我们能够根据以下留档成功地从外部IDP中检索令牌:https://www.keycloak.org/docs/latest/server_admin/#retrieving-external-idp-tokens 但是,当使用

  • 官方记录说 写入易失性字段与监视器释放具有相同的记忆效果,从易失性字段读取与监视器获取具有相同的记忆效果。 和 有效地,挥发性的语义学得到了实质性的加强,几乎达到了同步的水平。为了可见性的目的,挥发性字段的每次读取或写入都像“半”次同步。 从这里开始。 这是否意味着,对volatile变量的任何写入都会使执行线程将其缓存刷新到主存中,而每次从volatile字段读取都会使线程从主存重新读取其变量?