Saturday, 7 November 2009

Create a default access rule for URLs using Spring Security

Using Spring Security, you can specify access rules for URLs in a declarative manner in a central configuration file. However, it's easy to forget a controller or two, which could have tragic, or at least embarassing, consequences!

One way to avoid that is to create a catch-all rule that will ensure that all URLs are secured. This will be fired if you access a forgotten URL, both prompting you to fix but also preventing anyone exploiting the mistake.

The key to it is realising that the first matching rule from the top is applied to each URL - therefore, defining a catch-all at the bottom will ensure that any unmatched URL is matched by the catch all. The pattern matching is based on Ant patterns, so /** will do the trick.

This is demonsrated below.



  
     
  
    
    
    
    
    
    
   
    
    
   
    
   
    
  


Note that the login and logout URLs have been set to ROLE_ANONYMOUS. Otherwise, attempts to access them will result in an infinite loop as the catch all will fire. ROLE_ANONYMOUS is the default role given to all unauthenticated requests by Spring Security - if you have overridden it, use the overridden name instead.

Using Spring Security with Tuckey URLRewriteFilter

If you are developing a servlets, JSF or Spring MVC application, it’s very likely you will want to implement declarative security to restrict access to functions based on URLs. It's also likely that you want to provide intuitive, SEO friendly and RESTFul URLs for your app.

Luckily, both of these requirements can be met by existing, high-quality open source frameworks in a way that is not intrusive to your application code. I’m referring to the Spring Security framework (what used to be Acegi Security) and the Tuckey URLRewriteFilter.

I won’t go in to the details of setting up both of these frameworks, as these are well covered in other excellent blogs:

http://sziebert.net/posts/restful-urls-with-spring-mvc-and-urlrewritefilter/
http://www.mularien.com/blog/2008/07/07/5-minute-guide-to-spring-security/

However, as I discovered after some false starts, getting both of these components to work together needs the correct configuration, which might not be apparent unless you really understand how Servlet Filters work.

Both frameworks rely on servlet filters to apply rules to requests – the Spring Security framework’s filter chain applies access decision rules to incoming requests and the URLRewriteFilter intercepts requests and forwards them to the re-written URL. The order in which these things occur is obviously quite important.

For example, consider this simplified Spring Security configuration (which goes in your application context).




This rule restricts the URL /clientlist.htm (which maps to a Spring MVC controller but could be a servlet or JSP) to users who are in the ROLE_MANAGER role. Note that the pattern matching is based on Ant pattern matching, and this is described in the Spring Security documentation.

However, we want this to be accessed on the more friendly URL of /clients. To do that we use a URL rewriting rule in the URLRewriter filters configuration file:


^/clients$
  /clientlist.htm?status=1

Of course, we would also need a corresponding outbound rule:


  /clientlist.htm\?status=1$
/clients

However, that’s not really important for the purposes of security. The important thing to note is that the URL Rewriter will take a request for /clients and forward it to /clientlist.htm?status=1. At the same time, the Spring Security filter applies access decisions to requests for /clientlist.htm.

Clearly, the order that the filters process the request in is important. For this to work, we would want the URL rewriting to happen first (translating /clients to /clientlist.htm) before the security rules are applied. Otherwise, Spring Security would need to be configured to match the re-written URLs rather than the “real” URLs. While this is technically feasible it might cause maintenance difficulties. Imagine how much head-scratching would be involved for a maintainer trying to understand why the Spring Security config doesn’t match up with the expected URLs for the Spring Controller definitions!

We all know (because we memorised the Servlets specification) that Filters are applied in the order that they are defined in the web.xml. So easy, we just put the URL rewriter in front of the Spring Security filter in the web.xml! Job’s a good ‘un, now what’s for lunch?

Not so fast... Just defining the filters in the right order is not enough on its own, especially if using the standard configuration examples provided in the documentation for both products.

The symptoms of an incorrect configuration would be that URL rewriting works but the Spring Security filter does not process the request, resulting in either an error (if your code expects an authenticated user) or in security not being applied.

That is because the URL rewriter forwards the request to the rewritten URL. Having memorised the servlet spec, we know that filters do not automatically process forwarded requests. By default, they only apply to direct requests. Luckily, this can be easily changed using the <dispatcher /> element in the filter-mapping configuration.

A working configuration of Tuckey URLRewriteFilter and the Spring Security filter including the additional <dispatcher>FORWARD</dispatcher> configuration is included below:




    UrlRewriteFilter
    
       org.tuckey.web.filters.urlrewrite.UrlRewriteFilter
    
    
       logLevel
       WARN
    
    
       confReloadCheckInterval
       
    




   UrlRewriteFilter
   /*

 


    springSecurityFilterChain
    org.springframework.web.filter.DelegatingFilterProxy  
  



    springSecurityFilterChain
    /*
    REQUEST
    FORWARD
    INCLUDE
    ERROR

Adding the <dispatcher /> elements within the filter-mapping element for the Spring Security filter will cause it to process the forwarded request. It means you can configure security based on the “real” URLs rather than the rewritten URLs. Note you should also make the filter apply to direct requests, otherwise you won't be able to access Spring Security URLs like j_spring_security_check (unless you use the URL rewriter to create a forward to this URL, which seems unnecessary).

As a final remark; note that any other filters that you want to apply to requests will need to be configured as above as well. For example, the Spring Hibernate “open session in view” filter that is needed for lazy initialisation and loading in the web tier:



    hibernateFilter
    
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
   
   
       sessionFactoryBeanName
       sessionFactory
    




    hibernateFilter
    /*
    REQUEST
    FORWARD
    INCLUDE
    ERROR

Hope this is useful to someone.

Tuesday, 28 July 2009

Service Method Caching and Hibernate LazyInitializationExceptions

I ran into a slightly baffling problem using a combination of the Caching Spring Module (0.9) with Ehcache, Spring 2.5.6 and Hibernate 3.3.1 in a servlet application. After adding a Spring @Cacheable attribute to a method in a service object that returned a Hibernate data object I started to get the dreaded LazyInitializationException when iterating through a child collection in the JSP view whenever I hit the page a second time – and I was already using the OpenSessionInViewFilter!
@Cacheable(modelId=projectCachingModel)
@Transactional(readOnly = true)
public Project getProject(Integer projectId) {
return projectDao.load(projectId);
}
What gives? It seems that if the Project object is returned from the cache rather than from the DAO (which, incidentally, extends Spring HibernateDaoSupport) then the lazy loaded collections within the object can’t bind on to the Hibernate Session opened in the view, hence the LazyInitializationException. This sort of thing can be hard to test in a normal unit test. Luckily, it's possible to simulate what's going on in the view in a unit test using a few useful utilities in the Spring framework:
org.springframework.transaction.support.TransactionSynchronizationManager;
Now you can open and close sessions in much the same way that they would within the view as you navigate between pages.
// Get Hibernate session
SessionFactory sessionFactory = (SessionFactory)ctx.getBean("sessionFactory");
Session s = sessionFactory.openSession();

// Bind session to transaction synchroniser
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));

// Get Project (loads from database)
Project project = projectFacade.getProject(3);
  
// Close session
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
s = holder.getSession(); 
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSession(s);
  
// Start new session
Session s2 = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s2));

// Reload Project (from cache)
Project reload = projectFacade.findProject(3);
// Test lazy loading/initialisation
  
// Close session
holder = (SessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
s2 = holder.getSession(); 
s2.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSession(s2);