Masoud Kalali's Blog

My thoughts on software engineering and beyond…

By

Updating Web application’s Spring Context from Beans definitions partially stored in the database…

As you know spring security providers can get complex as  you may need several beans like, implementations of UserDetailsService, SaltSource, PasswordEncoder, the JDBC setup and so on. I was working on an spring based application which needed to load the security configuration from the database because the system administrator was able to select the security configuration from several pre-defined templates like LDAP, JDBC, File Based, etc. change some attributes like LDAP address or file location, etc. to fit the template in the environment and then apply the new configuration to be used by the application.

I was to port some parts of the application to the web and 3 tier architecture and so I had to have the authentication configured for the web application from the database and current implementations of the required beans for the security providers configurations.

It is plain and simple, load all of the context configuration by adding them to the web.xml and let the spring filter use them to initialize the context or extend XmlWebApplicationContext or its siblings and return the configuration file addresses by overriding the getConfigLocations method. This works perfectly when everything is in plain XML file and you have access to everything… It wont work when some of context configuration files are stored in the database and the only means of accessing the database is the spring context and that needs to be initialized before you could access the database through it.

What I needed to do was putting together a basic authentication in front of the web application while using the ProviderManager which its configuration is stored in the database. Without the ProviderManager you cannot have the security filters and thus no security will be applied over the context.

The first part, creating the security configuration and specifying the URL patterns which are needed to be protected is straight forward. The filters use the ProviderManager which is not there and thus the context initialization will fail. To solve this I used the following workaround which might help someone else as well. In all of our templates the ProviderManager bean name was the same so I could simply devise the following solution. Create a temporary basic security provider definition file with the following beans:

  • A basic UserDetailsService bean based on InMemoryDaoImpl
  • An AuthenticationProvider on top of the above UserDetailsService
  • A ProviderManager which uses the above AuthenticationProvider.

The complete file look like this:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
    default-lazy-init="true">

    <bean id="tmpUserDetailsService"
          class="org.springframework.security.core.userdetails.memory.InMemoryDaoImpl">
        <property name="userMap">
            <value>
            </value>
        </property>
    </bean>

    <bean id="tmpAuthenticationProvider"
          class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <property name="userDetailsService" ref="tmpUserDetailsService"/>
    </bean>

    <bean id="authenticationManager"
        class="org.springframework.security.authentication.ProviderManager">
        <property name="providers">
            <list>
                <ref local="tmpAuthenticationProvider"/>
            </list>
        </property>
    </bean>
</beans>

Using this file, the spring context will get initialized and thus no NoSuchBeanDefinitionException will be thrown at your face for the least.  You may say, ok why you are not loading the entire security definitions and configurations after the context is initialized so you wont need to have the temporary security provider, the answer to this question is that having no security applied right after the context initialization is finished is a security risk because at the brief moment before the context get updated with the security definitions, people can access the entire system without any authentication or access control. Let’s say that brief moment is negliable but a bigger factor here is the possible failure of loading the definitions after the context is initialized means that the application will remain without any security restriction if we do not lock down the application with the temporary provider.

Now that spring context can get initialized you can hook into spring context initialization by a listener and load the security provider from the database into the context to override the temporary beans with the actual one stored in the database.

Too hook into spring context initialization process you need to follow the below steps:

  • Implement your ApplicationListener, following snippet shows how:
  • public class SpringContextEventListener implements ApplicationListener {
    
        private XmlWebApplicationContext context;
    
        public void onApplicationEvent(ApplicationEvent e) {
    
            if (e instanceof ContextRefreshedEvent) {
                context = (XmlWebApplicationContext) ((ContextRefreshedEvent) e).getApplicationContext();
                loadSecurityConfigForServer();
            }
        }
    
        private void loadSecurityConfigForServer() {
    
            AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;
            String securityConfig = loadSecurityConfigFromDatabase();
            XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(registry);
            xmlReader.loadBeanDefinitions(new ByteArrayResource(securityConfig.getBytes()));
        }
    
        private String loadSecurityConfigFromDatabase() {
            //use the context and load the configuration from the database
        }
    }
    }
    
  • Now that you have a listener which listen for ApplicationContext event and load your security configuration  you can hook this listener to you context because by its own it wont do anything :-)
  • To add the listener to your application context, just add something similar to the following snippet to one of the context configuration files and you will be done.

    <bean id="applicationListener" class="your.package.SpringContextEventListener"/>
    

    This is all you needed to do in order to get the context updated after web application starts without imposing any security hole on the application for lack of security definitions right after the application startup.

    Leave a Reply

    Your email address will not be published. Required fields are marked *


    − four = 4

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">