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]

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

[/xml]

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:
  • [java]
    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
    }
    }
    }
    [/java]

  • 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.

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

    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.

    Some best practices on Spring framework context configuration files naming

    Like many other developers you may required to work on a pre-existing spring based codebase developed by a team of developers in the past couple of years where few of them really know why there are handful of spring configuration file scattered here and there and each one of them has a mixture of different configuration elements like security, aop, data sources and service definitions without a proper naming of the configuration files. That is a pain in the neck and headache to follow and further refactor/refine. Here I list some of the best practices around spring context configuration files which might help ease the reading and understanding the files:

    1. Use unified and proper naming schema like applicationContexte-[server|client |shared]<Configuration Domain>-<Configuration | Beans>-<Additional Clarifier>.xml which may seem to be long but believe me it will save someone or yourself fair deal of time later on. The explanation of the naming schema is as follow:
      • If your if the codebase is to run both client and server, some modules shared and some exclusive for each tier, use server and client to distinguish them.
      • The configuration domain can be any of: security, data-access, aop, resources, etc.
      • Use configuration when the file purpose is configure a set of services using some existing base beans
      • Use beans when you are defining the base beans which will be used to put together the services for the whole system
    2. Do not reference any of these files directly in the source code but rather use one single utility class responsible for returning references to different groups of the configuration files used in different places. Make sure that the methods returning these references uses proper signature return type like an String[] even if you have just one file for an specific domain or group of configuration files. Spring provides the possibility of composing a configuration file from multiple other files but I prefer using the code as it gives us more flexibility.
    3. Leave a one paragraph comment describing the purpose of the the document rather than leaving it as it is and hope that whoever is going to read it will understand the purpose by looking at the content or at the name because you don’t know when someone will look at the code and how s/he will interpret the content. Do not leave room for next to come people to guess where to put the new bean definitions, etc but rather specify it by leaving some comments if not documentation about the configuration file.