Engineering
Luke Taylor
June 03, 2009
We’re pleased to announce that the first milestone of Spring Security 3.0 is now available for download. The release is also available through the Maven milestone repository at http://maven.springframework.org/milestone. As with Spring 3.0, this is the first release which requires a minimum JDK 1.5 to run and also require Spring 3.0, so you should get hold of the Spring 3.0.0.M3 release if you aren’t already using it. So what’s new and what has changed in this release?
Expression–Based Access Control
This release is the first to include a preview of our Spring-EL based authorization support. You can now use expressions both in method annotations and for web security. This opens up many new possibilities when compared to the familiar attributes and voter–based mechanism. A simple example will probably be a good start. Here’s one for a web application, using the security namespace:
... ;The built–in hasRole(‘ROLE_SUPERVISOR’) expression is nothing fancy – it just checks the current user’s authority list and returns true if they have the given role. But we’ve added an extra expression to specify that the IP address making the request must be in the range defined by the subnet/mask argument. This may or may not be of practical use depending on your network setup, but it illustrates how powerful the expression-based approach is. The list of security attributes which was previously contained in the “access” XML attribute has been replaced by a boolean expression. If it evaluates to “true” access will be granted. If it evaluates to “false”, access will be denied.
@Pre and @Post Annotations
Method security is a bit more complicated than the simple allow or deny that we’ve just seen for web requests. To provide more comprehensive support for the use of expressions with method security, we’ve introduced four new annotations which can contain expression attributes which are applied before and after the method invocation. To enable support for them, there’s a new attribute on the global-method-security namespace element:
<global-method-security pre-post-annotations="enabled"/>
The most obviously useful annotation is @PreAuthorize which controls whether a method can actually be invoked or not. For example (from the “Contacts” sample application):
@PreAuthorize("hasRole('ROLE_USER')")
public void create(Contact contact);
which means that access will only be allowed for users with the role “ROLE_USER”. Nothing new here. But what about:
@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);
Here we’re actually using a method argument as part of the expression to decide whether the current user has the “admin” permission for the given contact. The hasPermission() expression is linked into the Spring Security ACL module through the application context (see the Contacts sample configuration for how this is achieved). You can access any of the method arguments by name as expression variables, provided your code has debug information compiled in. Any Spring-EL functionality is available within the expression, so you can also access properties on the arguments. For example, if you wanted a particular method to only allow access to a user whose username matched that of the contact, you could write
@PreAuthorize("#contact.name == principal.name)")
public void doSomething(Contact contact);
Here we are accessing another built–in expression, which is the “principal” of the current Spring Security Authentication object obtained from the security context. You can also access the Authentication object itself directly using the expression name “authentication”. Authorization can also be performed after the method call has taken place, using the @PostAuthorize annotation. To access the return value from a method, use the built–in name “returnObject” in the expression.
Filtering
As you may already be aware, Spring Security also supports filtering of collections and arrays and this can now be achieved using expressions. This is most commonly performed on the return value of a method. For example:
@PreAuthorize("hasRole('ROLE_USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List getAll();
When used with the @PostFilter annotation, Spring Security iterates through the returned collection and removes any elements for which the supplied expression is false. The name “filterObject” refers to the current object in the collection. You can also filter before the method call, using @PreFilter, though this is probably a less common requirement. The syntax is just the same, but if there is more than one argument which is a collection type then you have to select one by name using the “filterTarget” property of this annotation.
Codebase Restructuring
In versions prior to 3.0, most of Spring Security’s code was contained in the spring-security-core jar. Over the years, as more features have been added, it has become more difficult to track the dependencies both within the codebase itself and also on third party libraries. For example, it’s hard for a user to determine which of the listed dependencies in the core Maven pom.xml are required for a particular set of features within the framework. In addition, the original package structure and class names have been around since the framework’s origins as Acegi Security in 2003, when only a few basic authentication mechanisms were supported. As the amount of code has increased and the feature set has expanded, this package structure has begun to show its age.
Spring Security 2.0.4 Package Structure
Spring Security 2.0.4 Package Structure
The diagram above shows the high-level package diagram of the core, core-tiger, cas-client and acl jars in the 2.0.4 release, as produced by Structure101. You don’t have to be an expert in code structure to realize that there is a bit of a problem here. There are a lot of circular references and no clear overall dependency structure within the packages. There are also some issues with packages being split across jar boundaries, which can cause problems with OSGi. This fragility in the code structure would likely have caused a maintenance overhead as Spring Security evolved, so the decision was made to restructure the code for the 3.0 release to give us a stable base for future development. Let’s take a look at how things are now organized.
Project Jars
The first thing we did was split the core out into several jars. The spring-security-core jar now contains only basic authentication and access-control code and is much cleaner. It has no dependencies on LDAP or the servlet API, for example, and there are now separate jars for web-specific code and for LDAP. We’ve also split out the namespace parsing code out int a separate jar, as it depends on most of the other jars and doesn’t expose any public APIs that you are likely to use directly in your application. You only need to use it if you are using Spring Security namespace configuration in your application context XML files. The main project jars are shown in the following table.