Spring MVC Method Security with @PreAuthorize and Sp-EL

Spring Security supports some nifty method-level access checks. Some of the built-in expressions include hasRole([role]), isAnonymous(), isAuthenticated() and others. You can also access Security Context objects like principal and authentication. What we’re discussing today is using custom methods to perform Access Checks on our MVC methods.

In NixMash Spring I added a very basic User Profile page which I likely will build-out in the future. The Access Rules for viewing the page are that users can view their own profiles, but not the profiles of other users. User “bob” could access http://site/bob, but would trigger a 403 Not Authorized if he tried to access http://site/bill. Administrators, on the other hand, can view all user profile pages.

Before getting into the Method Security logic it’s important to mention that to use Method Level Authorization you need to add the following annotation to your Security Configuration class.

@EnableGlobalMethodSecurity(prePostEnabled = true)

Back to our Controller method for the user profile page, which without any Method Security begins like this.

@RequestMapping(value = "/{username}", method = GET)
public String profilePage(@PathVariable("username")
String username, Model model)

throws UsernameNotFoundException {

If we wanted to only prevent users from seeing other user profiles we could simply add the following annotation to profilePage().

@PreAuthorize("#username == authentication.name")

We want to give Administrators the ability to view any profile they want so we’re going to need a custom method and call it with @PreAuthorize using Spring Expression Language. Here’s the UserService method that performs our Access Check.

Here is the final annotation we’ll use with the profilePage() Controller method.

@PreAuthorize("@userService.canAccessUser(principal, #username)")

@userService is a Bean which we’ve autowired in the Controller so we’re annotating it with an ampersand.  Notice we have a hashtag for “username” because the hashtag denotes a method parameter in Sp-EL. Remember that the #parameter name matches the name in the method, not the parameter passed in.

References for this post include:

All source code referenced in this post is available in the v0.1.8 branch of NixMash Spring on GitHub.