Tuesday, February 8, 2011

Grails - Spring Security with Spring Cache: Caching content per user

We are using Spring Security in one Grails project, we needed to integrate Spring Cache to cache content, but we had the problem that certain information on the page depends on the user logged in like the user name that is displayed on the header of the page, among other things for instance. The problem was that for example if you load the page being not logged in, the page gets cached and then you logged in and access the page again, you will see the cached page and it will not show your user name because the first time you accessed it since you were not logged in the user name was not there. Because of this we needed a way to consider the user when caching the page, so we will get a cached page for a not logged in user and a cached page for the user logged in.

Originally we were using the MimeTypeAwareKeyGenerator class to generate the cache keys, we used this generator because some of our actions are used as REST services as well that can be used with JSON or XML, so what we did was to create a new generator that extended this class and made it not only mime type aware but also user aware, our class looked like this:

public class MimeTypeAndAuthenticationAwareKeyGenerator extends MimeTypeAwareKeyGenerator {

@Override
protected void generateKeyInternal(CacheKeyBuilder builder, FilterContext context) {
super.generateKeyInternal(builder, context)
def springSecurityService = ApplicationHolder.application.mainContext.getBean('springSecurityService')
if (springSecurityService?.isLoggedIn()) {
builder << "authUserId=${springSecurityService.principal.getId()}".toString()
}
}
}

With this approach we are considering the user id in the key for the caching allowing us to create separate cache entries if the user is logged in or not. So, now if you access a page and you are not logged in you will get a cached page with no user name on the top, but if you logged in you will get a page with your user name on the top that will be cached for the next time you access it.

Now you need to tell your Grails application to use this new class you created and to do it you just need to set the keyGenerator attribute of the springcacheFilter, you can do this via the Config.groovy, like this:

beans {
springcacheFilter {
keyGenerator = new MimeTypeAndAuthenticationAwareKeyGenerator()
}
}