Spring 4: XML to @Annotation Configuration

I got 99 problems, but an XML based security configuration aint one..

I have for a long time preferred to use Spring (or generally Java) annotations over XML config for my development (I concede the point on XML configuration allowing a central place to understand all Controllers etc, but with modern decent IDEs this info can still be viewed centrally even if using annotations).

A while ago I made the switch to pure code configuration for my Spring webapps, with the exception of the web.xml (dependency on using Tomcat7 that I didn't want to enforce just yet) and the security config - which Spring didn't support.  Along with Spring 4, Spring have announced that they will now support security config as code, which is great news!


XML Configuration

Below is an example of a typical XML based configuration.

This basically turns on security for the webapp, it:
  • Adds a single intercept URL rule (in this case, our rule says all URLs are open to everyone - so not that secure! We would likely add more URL intercept rules above this as we have more URLs we want to secure - these rules are read top to bottom and fall through until a match is found, so permitAll must be the lowest rule found)
  • Defines a login form - including location of the form, the submission target URL, login failure redirect URL and the logout URL. Spring will automatically handle all these. If you submit a form to the login-processing-url then Spring will intercept it and attempt to authenticate - you don't need a specific controller handler defined.
  • Defines an authentication manager - this will be used to authenticate submitted requests. In this case, I have overriden the standard Spring User Service with my own implementation (this will be a common requirement as you will want to authenticate against users in your DB etc)


The config is all pretty simple really - the only unpleasantness is the requirement to have XML!


Code Configuration

And here is the config in code using the latest and greatest Spring dependencies.

Let's have a lookat what is going on here.
  • I am @Autowiring in my custom User Service class - this will be used in the Authentication manager later.
  • The configure() method is where we set the URL intercept rules and login form - it should be pretty self explanatory reading it through if you are familiar with old fashioned xml config (will come back to CSRF config shortly - but it is NOT recommended that you disable this! this is just in development)
  • The registerAuthentication() method sets up the Authentication Manager using our User Service implementation. Whilst setting up the Authentication Manager, we also define a password encryptor - you will note in the code config I am using the BCrypt password encoder, and in XML using a base64 MD5 hash - but whichever encoder you want to use you can configure it in the same fashion using the secondary method to instantiate and return the encoder desired (although you probably shouldn't be using MD5 over BCrypt.. really, you shouldn't ).



Observations and Gotchas

As you can see in the above code example, I am explicitly disabling CSRF. As of Spring 3.2 onwards, the security layer provides CSRF protection by default - so if this is not disabled then you have to provide a CSRF token to prove your request is legit. Should you need to disable it, you can using the above syntax, but obviously CSRF protection is built in to help you out, so probs best not to disable it on a prod system!

The link above details how to include tokens in requests for your webapp. It's really pretty easy to do.


The second point to note is one that caught me out.  Previously in Spring, when submitting a form to authenticate, the default field names had to be j_username and j_password. If you are hoping to switch the config out on an existing webapp then you have to make sure you update your login form with the correct field names (Spring 3.2 with XML config you still need j_*, its only Spring 3.2 & Java config..)



As always, the code is on GitHub - feel free to check it out.

4 comments:

  1. Which versions of Spring and Spring Security did you use here? The registerAuthentication method is not available in WebSecurityConfigurerAdapter version 3.2

    ReplyDelete
  2. You can just change that to the configure(...) method to achieve the same thing..

    http://docs.spring.io/autorepo/docs/spring-security/4.0.0.M1/apidocs/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder)

    ReplyDelete
  3. You can configure the old field names like so:

    .and()
    .formLogin()
    .usernameParameter("j_username")
    .passwordParameter("j_password")

    ReplyDelete