In this tutorial we’ll set up Spring Security in a Maven powered Spring MVC / Hibernate application. We’ll cover some of the basic usage and core components, then look at some more sophisticated “black magic” security we can apply by tying in to Spring’s Expression Language.
Like most of the Spring projects, Spring Security’s Official Documentation is awesome and sums it up pretty well on it’s own, but for those of you who are adverse to reading documentation I’ll give you the skinny version: Spring Security offers a number of configurable mechanisms to provide security to Spring-powered J2EE applications. It focuses on the two most prominent security concerns: Authentication (verifying a user is who they say they are) and Authorization (verifying a user has permission to access / execute a given resource).
The most commonly used mechanisms for securing a web-app are URL-based restrictions:
… and Class-level / Method-level annotations:
.. In the annotation-based example above, all of the methods inside this PersonService class will require the user to have the ROLE_ADMIN role. Since deletePersonalso has another @Secured annotation, users will also need the ROLE_ADMIN_WRITE permission.
It’s important to note that Spring Security uses dynamically generated proxies to wrap objects in security logic, as this technique can introduce some small caveats that need to be considered when working in “secured code”. Most notably: if a secured object calls this.foobar(); somewhere on itself, it will effectively bypass the proxy and any security-related logic applied to it.
While Spring Security is a huge framework that can easily be extended to leverage whatever authentication services security, we’re going to keep things simple and look at the core components to address Authentication and Authorization.
Authentication in Spring Security eventually boils down to a user being identified with some implementation of UserDetails, which is allowed access to resources based on the presence of GrantedAuthority objects (which eventually boil down to String-based tokens). Most applications end up subclassing UserDetails and provide a UserDetailsService to integrate with Spring Security.
Authorization (or Access-Control) is handled by handing wrapped (aka secured) objects to a AccessDecisionManager and asking if the current user has access. There are a number of ways to flexibly apply this logic throughout your application, a few of which we’ll look at later.
To get started, generate a new project from the org.fluttercode.knappsack:spring-mvc-jpa-archetype (version 1.1 as of this writing). This project sets up a simple skeleton with Spring MVC & Hibernate (configured to an in-memory database).
The major piece is the HomeController.java which has a simple Spring MVC Controller configured via Annotations. The application’s main Spring context is configured in the root-context.xml & db.xml located in src/main/webapp/WEB-INF/spring, with the MVC portions split among controllers.xml & servlet-context.xml. I don’t particularly love having so many different configuration files, but we can live with it for now.
Let’s start out by “cleaning” up this app. For starters, let’s change the url-pattern of the Spring DispatcherServlet in web.xml from ‘/spring/*’ to ‘/’. This way, we all resources will be served up from the root context of our webapp (except for any static resource files/folders we might map). After changing this, we’ll also want delete src/main/webbapp/index.html since it’s only purpose was to redirect us to the DispatcherServlet at /spring/.
Let’s get started by adding Spring Security. We’re going to configure a login page at /login, a URL to process logging in at /doLogin, and a “secured” page at /secure/users.
To start, add the following to your pom.xml’s dependencies:
Next, we’ll update our root-context.xml to the following:
We’ll also need to create a UserDetailsAuthenticationProvider for Spring Security. A simple example is listed below:
With all that done, we just need to add the Spring Security FilterChain to our web.xml file, as listed below:
And just like that, we’ve got a simple Spring MVC app that has a secure route. Now, the last thing we need to do is create our users.jsp, login.jsp and have a Controller serve them up. We’ll add the logic to HomeController.java to keep things simple. Code for each is provided below:
Milestone 1: Recap
Okay, so now we have a simple application that can be configured with one User + Password pairing and a secure URL. Wow, lots of work, right? Well, sort of. I like this setup because it lets us see the individual pieces of Spring Security and then figure out how we could apply the already-shown tools to secure most of a URL.
This code was committed and checked in under 22d52c4997fc3059d7e3a1ac5435c854d08d6e4b.
Next, we’ll look at sprinkling in a little magic to add fine-grained controls over method invocations and business logic.
Enhancing Security with Expressions
Earlier, we configured security for all URLs starting with /secure/ to be locked down to users with the ROLE_ADMIN role. We also saw how to use @Secured annotations to secure classes and methods, which is great for business logic and services. Next, we’ll look at a few of the other security-related annotations and how we can take advantage of Spring Expression Language to extend their functionality.