Spring SAML - Reading and refreshing IdP metadata at runtime

元气小坏坏 提交于 2019-12-03 21:18:29
Jim Kennedy

After reading several more posts and scanning source code, I discovered the answer to this question is more complex than I thought. There are really 3 different scenarios to address.

  1. The initial reading of all IdP metadata providers from a database table
  2. Expiring and re-reading the IdP metadata XML data
  3. Dynamically adding and removing a provider without config changes or server restart

I'll take on each of these one at a time. Item 1: There are probably several ways to solve this but review the DBMetadataProviderList class (in my OP) above as a quick and dirty solution. Here is more complete constructor code:

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

To solve item #2 I used the FilesystemMetadataProvider as a guide and created a DBMetadataProvider class. By extending the AbstractReloadingMetadataProvider class and implementing the fetchMetadata() method we have built-in cache refreshing thanks to opensaml. Below are the important parts (example code only):

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
    }
  }

 }

This resource helped me get to the right technique for a cache reloading metadata provider class. Finally, item #3 can be solved by implementing this post.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!