Spring Security is too Generic

Posted on 2013-12-3

A fact I learned while playing Final Fantasy that comes to mind in my work on a daily basis is that

being a jack of all trades means being a master of none

What that means in the software world is that frameworks with too large a scope tend to be less optimal at solving problems than a custom made solution. That seems intuitive, but it's not always true. Frameworks are often spawned out of the need for filling one specific use-case. So someone builds a simple solution, but then it starts growing and you see the potential for creating something a lot of people could benefit from. As the scope expands and encompasses way more than the problem it was originally meant to solve, it is usually still pretty great at solving that original use case. Spring Security on the other hand gives the impression of having been started as a framework in mind. Not unlikely, because...

Spring Security is necessary

Web applications are most likely the biggest consumers of Spring Security's services. Which for Java is sad, because (IMHO) it's a testament to how much the Java EE spec has dropped the ball, saying "Here! Container implementers, YOU solve this" and consequently giving barely enough bindings/hooks into that service. I would have loved to have seen something like what Spring Security does, integrated into the spec. Allowing applications to expose their own UserDetailsService (which provides access to a generic username+password object) while still letting the container do the redirecting, cookie setting, etcetera. I like that the spec lets you use a custom login form, but that has its own problems(see "information leaks"). This also touches on how HTTP authentication is outdated, but let's not get into that.

So Spring Security doesn't JUST do web applications, "No!" it's also a very generic way of accessing authentication and authorization data that's stored in a threadlocal SecurityContext, with annotations for securing methods and objects managed by Spring. So once you plug in your authentication provider of choice, the rest is going to be the same, right? How often do you switch authentication providers? How much work is it mapping your custom user DB to Spring's UserDetails and GrantedAuthority, and what is the productivity benefit of being able to use Spring Security's expression language extensions? You, as a developer, only really see the productivity gain as you work on the next project, where plugging in a different provider is new, but the rest of the securing of an application is simple and familiar. The return on the investment of learning the framework depends on how popular the framework is/becomes.

Why am I complaining?

Now trying to plug in a backing LDAP directory service is actually pretty damn simple thanks to the facilities provided by Spring Security. I don't feel the need to discuss them, because of how widely known/documented they are. There is really no need for anything beyond Spring Security's own documentation, which is very well-written. However, my complaint starts when I try to create account management functionality. There is a lesser known and (AFAIK) internally unused class called LdapUserDetailsManager. It's awesome and exactly what I needed for creating and updating user data. Now comes the SPI divergence.

The LdapUserDetailsManager plugs nicely into the generic authentication provider mechanism, but I've already used the LDAP-specific authentication provider, because it's so easy and simple to configure. The generic provider works in a way that the password validation happens inside the application, while generally you want the LDAP server to take care of that FOR you (through the bind operation). Why? Because if you're using an LDAP directory it's very likely that you're sharing user data with other services. Even though it should work the same way, why would you want to authenticate differently in your Spring app? It's not that difficult to unite the UserDetailsService style of authentication with a bind-style LDAP authentication, but you need to build it yourself. Why? Because the Spring Security guys facilitated LDAP authentication in an optimal way (props to them), which (by it's very nature) circumvented several of the key generic components. That's where my bold claim, which is the title of this post, came from. Obviously it's subjective, but that's what a blog is for, isn't it?

What's my point?

Do I propose a better solution? Yes and no. Yes, I will, in applications where I need to write anything that mangles/manipulates the security filter chain, not bother using Spring Security. It is not its optimal use-case. It supports it, but not gracefully. On the other hand, Spring Security is a great framework for plugging in read-only LDAP, stupid-simple in memory user database, and stupid-simple read-write SQL-backed user database with CRUD operations provided.