Sunday, February 10, 2013

Integrating Spring Security with Dropwizard


Dropwizard is a great framework for streamlining Java web apps (I'd argue any app) for deployment in a production environment.  It's an answer to the complexity of Java Web and Application Containers, which tend to be overkill for 90% of your use-cases.
As a framework, Dropwizard is still a little young (I won't say immature because it's a rock-solid environment).    Dropwizard comes with two authenticators, Basic and OAuth, and it's SSL-based Client Authentication is relatively undocumented.  We needed certificated-based client authentication, but didn't want to have to also force users to do BasicAuth in addition to using a certificateust so we could inject a "principal" object (i.e. @Auth) in our JAX-RS controllers.
More importantly, we wanted the rich features of Spring Security, which not only includes support for other authentication mechanisms like SPNEGO+Kerberos, but also route-based and expression-based security.
With that said, we did the hard work in figuring out how to integrate Spring Security with Dropwizard and this is the resulting work:  https://github.com/Berico-Technologies/Fallwizard.  To use our set of extensions, follow the instructions below:

Configure Spring Security in your applicationContext.xml

Here is an example for certificate-based authentication.
<security:http>
  <security:intercept-url pattern="/*" access="ROLE_USER" />
  <security:intercept-url pattern="/admin/*" access="ROLE_ADMIN" />
  <security:x509 subject-principal-regex="CN=(.*?)," />
</security:http>

<security:authentication-manager>
  <security:authentication-provider>
    <security:user-service>
      <security:user name="Super Awesome Client" authorities="ROLE_USER" />
      <security:user name="The Boss" authorities="ROLE_USER, ROLE_ADMIN" />
    </security:user-service>
  </security:authentication-provider>
</security:authentication-manager>


Initialize your Spring Application Context.

Initialize your Spring Application Context in your Dropwizard Service class. We explicitly require the location of theapplicationContext in our Dropwizard Configuration class.
ApplicationContext applicationContext = 
  new FileSystemXmlApplicationContext(
    configuration.getSpringApplicationContext());

Register Spring Security with the Dropwizard Environment.

@Override
public void run(BlahBlahConfiguration configuration, Environment environment) throws Exception {

  ApplicationContext applicationContext = 
    new FileSystemXmlApplicationContext(
      configuration.getSpringApplicationContext());

  new SpringSecurityAuthProvider(applicationContext).registerProvider(environment);
}

Use @Auth UserDetails userDetails in your JAX-RS controllers.

We're going to inject the Spring Security UserDetails context into your controllers (why invent a new User object?).
@GET
@Timed
@Path("/chitty-chat/{topic}")
public ChittyChat getChittyChatOnTopic(@Auth UserDetails userDetails, @PathParam("topic") String topic){
   // ... get Chitty-Chat ...
}

Get to working on your app!

You're done. Spring Security is filtering requests prior to you're resources being called.  It it also possible to use the Spring Security annotations if your resources are instantiated and managed by Spring or by using the "component-scan" capability (see http://www.mkyong.com/spring/spring-auto-scanning-components/).