Securing methods - Spring in the back end - Spring in Action, 4th Edition: Covers Spring 4 (2015)

Spring in Action, 4th Edition: Covers Spring 4 (2015)

Part 3. Spring in the back end

Chapter 14. Securing methods

This chapter covers

· Securing method invocations

· Defining security rules with expressions

· Creating security expression evaluators

Before I leave my house or before I go to bed, one of the last things I do is make sure the doors to my house are locked. But just before that, I set the alarm. Why? Because although the locks on my doors are a good form of security, the alarm system gives a second line of defense, should any burglar make it past the locks.

In chapter 9, you saw how to use Spring Security to secure the web layer of your application. Web security is important, as it prevents users from accessing content that they’re not authorized to access. But what if there’s a hole in your application’s web layer security? What if somehow a user is able to request content that they may not be allowed to see?

Although there’s no reason to think that a user will be able to crack through your application’s security, a security hole at the web layer can sneak in rather easily. Imagine, for instance, if a user makes a request for a page that they’re allowed to see, but due to a lack of developer diligence, the controller that handles that request calls a method that fetches data that the user isn’t allowed to see. It’s an honest mistake. But security breaches are just as likely to arise from honest mistakes as they are from clever hacking.

By securing both the web layer of your application and the methods behind the scenes, you can be sure that no logic will be executed unless the user is authorized.

In this chapter, we’ll look at how you can secure bean methods using Spring Security. In doing so, we’ll declare security rules that prevent a method from being executed unless the user for whom it is being executed has the authority to execute it. We’ll start by looking at a couple of simple annotations that can be placed on methods to lock them away from unauthorized access.

14.1. Securing methods with annotations

The most commonly used approach to method-level security with Spring Security is to apply special security annotations to the methods you want secured. This has several benefits, not the least of which is that the security rules for any given method are clearly visible when looking at the method in an editor.

Spring Security provides three different kinds of security annotations:

· Spring Security’s own @Secured

· JSR-250’s @RolesAllowed

· Expression-driven annotations, with @PreAuthorize, @PostAuthorize, @PreFilter, and @PostFilter

The @Secured and @RolesAllowed annotations are the simplest options, restricting access based on what authorities have been granted to the user. When you need more flexibility in defining security rules on methods, Spring Security offers @PreAuthorize and @PostAuthorize. And@PreFilter/@PostFilter filter elements out of collections returned from or passed into a method.

Before the end of this chapter, you’ll have seen all of these annotations in action. To get the ball rolling, let’s start by looking at the @Secured annotation, the simplest of the method-level security annotations offered by Spring Security.

14.1.1. Restricting method access with @Secured

The key to enabling annotation-based method security in Spring is to annotate a configuration class with @EnableGlobalMethodSecurity, like this:

@Configuration

@EnableGlobalMethodSecurity(securedEnabled=true)

public class MethodSecurityConfig

extends GlobalMethodSecurityConfiguration {

}

In addition to being annotated with @EnableGlobalMethodSecurity, you’ll notice that the configuration class extends GlobalMethodSecurityConfiguration. Much like the WebSecurityConfigurerAdapter class that your web security configuration class extended in chapter 9, this class offers you the opportunity to configure the finer points of method-level security.

For example, if you haven’t already configured authentication in the web-layer security configuration, you may want to do that here by overriding the GlobalMethodSecurityConfiguration’s configure() method:

@Override

protected void configure(AuthenticationManagerBuilder auth)

throws Exception {

auth

.inMemoryAuthentication()

.withUser("user").password("password").roles("USER");

}

A little later in this chapter, in section 14.2.2, you’ll see how to override the GlobalMethodSecurityConfiguration’s createExpressionHandler() method to provide some custom security expression-handling behavior.

Getting back to the @EnableGlobalMethodSecurity annotation, notice that its securedEnabled attribute is set to true. When securedEnabled is true, a pointcut is created such that the Spring Security aspects will wrap bean methods that are annotated with @Secured. For example, consider this addSpittle() method that’s been annotated with @Secured:

@Secured("ROLE_SPITTER")

public void addSpittle(Spittle spittle) {

// ...

}

The @Secured annotation takes an array of String as an argument. Each String value is an authorization, one of which is required to invoke the method. By passing in ROLE_SPITTER, you tell Spring Security to not allow the addSpittle() method to be invoked unless the authenticated user has ROLE_SPITTER as one of their granted authorities.

If more than one value is passed into @Secured, then the authenticated user must be granted at least one of those authorities to gain access to the method. For example, the following use of @Secured indicates that the user must have ROLE_SPITTER or ROLE_ADMIN privilege to invoke the method:

@Secured({"ROLE_SPITTER", "ROLE_ADMIN"})

public void addSpittle(Spittle spittle) {

// ...

}

When the method is invoked by an unauthenticated user or by a user not possessing the required privileges, the aspect wrapping the method will throw one of Spring Security’s exceptions (probably a subclass of AuthenticationException or Access-DeniedException). These are unchecked exceptions, but ultimately someone will need to catch it and handle it. If the secured method is invoked in the course of a web request, the exception will be automatically handled by Spring Security’s filters. Otherwise, you’ll need to write the code to handle the exception.

One drawback of the @Secured annotation is that it’s a Spring-specific annotation. If you’re more comfortable using annotations defined in Java standards, then perhaps you should consider using @RolesAllowed instead.

14.1.2. Using JSR-250’s @RolesAllowed with Spring Security

The @RolesAllowed annotation is equivalent to @Secured in almost every way. The only substantial difference is that @RolesAllowed is one of Java’s standard annotations as defined in JSR-250.

This difference carries more political consequence than technical. But using the standard @RolesAllowed annotation may have implications when used in the context of other frameworks or APIs that process that annotation.

Regardless, if you choose to use @RolesAllowed, you’ll need to turn it on by setting @EnableGlobalMethodSecurity’s jsr250Enabled attribute to true:

@Configuration

@EnableGlobalMethodSecurity(jsr250Enabled=true)

public class MethodSecurityConfig

extends GlobalMethodSecurityConfiguration {

}

Although here we’ve only enabled jsr250Enabled, it’s good to note that it’s not mutually exclusive with securedEnabled. These two annotation styles can both be enabled at the same time.

With jsr250Enabled set to true, a pointcut will be effected such that any methods annotated with @RolesAllowed will be wrapped with Spring Security’s aspects. This makes it possible to use @RolesAllowed on your methods in much the same way that you might use @Secured. For example, here’s the same addSpittle() method annotated with @RolesAllowed instead of @Secured:

@RolesAllowed("ROLE_SPITTER")

public void addSpittle(Spittle spittle) {

// ...

}

Although @RolesAllowed has a slight political advantage over @Secured in that it’s a standards-based annotation for method security, both annotations share a common shortcoming. They can restrict the invocation of a method based only on whether or not that user has been granted a specific privilege. No other factors can play a part in the decision to allow the method to execute or not. You saw in chapter 9, however, that SpEL expressions could be used to overcome a similar limitation when securing URLs. Let’s see how you can use SpEL along with Spring Security’s pre- and postinvocation annotations to perform expression-based method security.

14.2. Using expressions for method-level security

Although @Secured and @RolesAllowed seem to do the trick when it comes to keeping unauthorized users out, that’s about all that they can do. Sometimes security constraints depend on more than just whether a user has privileges or not.

Spring Security 3.0 introduced a handful of new annotations that use SpEL to enable even more interesting security constraints on methods. These new annotations are described in table 14.1.

Table 14.1. Spring Security 3.0 offers four new annotations that can be used to secure methods with SpEL expressions.

Annotations

Description

@PreAuthorize

Restricts access to a method before invocation based on the result of evaluating an expression

@PostAuthorize

Allows a method to be invoked, but throws a security exception if the expression evaluates to false

@PostFilter

Allows a method to be invoked, but filters the results of that method based on an expression

@PreFilter

Allows a method to be invoked, but filters input prior to entering the method

Each of these annotations accepts a SpEL expression for its value parameter. The expression can be any valid SpEL expression and may include any of the Spring Security extensions to SpEL listed in table 9.5. If the expression evaluates to true, then the security rule passes; otherwise, it fails. The implications of a passing versus failing security rule differ depending on which annotation is in use.

We’ll look at specific examples of each of these in a moment. But first, you’ll need to enable them by setting @EnableGlobalMethodSecurity’s prePostEnabled attribute to true:

@Configuration

public class MethodSecurityConfig

extends GlobalMethodSecurityConfiguration {

}

Now that the pre/post annotations are enabled, you can start using them. Let’s start by seeing how you can restrict access to a method using the @PreAuthorize and @Post-Authorize annotations.

14.2.1. Expressing method access rules

Thus far you’ve seen how @Secured and @RolesAllowed prevent a method from being executed unless the user has the required authority. But their weakness is that they’re only able to make their decisions based on the user’s granted authorities.

Spring Security offers two more annotations, @PreAuthorize and @PostAuthorize, that restrict method access based on expression evaluation. Expressions add a tremendous amount of flexibility in defining security constraints. Using expressions, you can allow or disallow access to a method using almost any conditions you can imagine.

The key difference between @PreAuthorize and @PostAuthorize is in when their expressions are evaluated. @PreAuthorize is evaluated before the method executes and prevents method execution unless the expression evaluates to true. In contrast, @PostAuthorize waits until the method has returned before deciding whether or not to raise a security exception.

We’ll first look at preauthorization, as it’s the most commonly used of the expression-driven security annotations. After that, we’ll see how to secure access to methods after the method executes.

Preauthorizing method access

At first glance, @PreAuthorize may appear to be nothing more than a SpEL-enabled equivalent to @Secured and @RolesAllowed. In fact, you could use @PreAuthorize to limit access based on the roles given to the authenticated user:

@PreAuthorize("hasRole('ROLE_SPITTER')")

public void addSpittle(Spittle spittle) {

// ...

}

When used this way, @PreAuthorize has no tangible benefit over @Secured or @Roles-Allowed. If the user has the ROLE_SPITTER role, then the method will be allowed to execute. Otherwise, a security exception will be thrown and the method won’t execute.

But there’s a lot more to @PreAuthorize than is apparent in this simple example. The String argument to @PreAuthorize is a SpEL expression. With SpEL expressions guiding access decisions, far more advanced security constraints can be written. For example, suppose that the average Spittr user can only write spittles of 140 characters or less, but premium users are allowed unlimited spittle lengths.

The @Secured and @RolesAllowed annotations would be of no help here, but @PreAuthorize is on the case:

@PreAuthorize(

"(hasRole('ROLE_SPITTER') and #spittle.text.length() <= 140)"

+"or hasRole('ROLE_PREMIUM')")

public void addSpittle(Spittle spittle) {

// ...

}

The #spittle portion of the expression refers directly to the method parameter of the same name. This enables Spring Security to examine the parameters passed to the method and use those parameters in its authorization decision making. In this example, you dig into the Spittle’s text to make sure it doesn’t exceed the length allowed for standard Spittr users. Or if the user is a premium user, then the length doesn’t matter.

Postauthorizing method access

A slightly less obvious way to authorize a method is to postauthorize it. Postauthorization typically involves making security decisions based on the object returned from the secured method. This of course means that the method must be invoked and given a chance to produce a return value.

For example, suppose that you wanted to secure the getSpittleById() method so that it only authorizes access if the Spittle object returned belongs to the authenticated user. There’s no way of knowing if a Spittle belongs to the current user until you’ve already fetched it. Therefore, getSpittleById() must execute first. If, after fetching the Spittle, it turns out to not belong to the current user, then a security exception should be thrown.

Spring Security’s @PostAuthorize works much the same way as @PreAuthorize, except that it waits to apply the security rule until after the method has already executed. At that point it has the opportunity to consider the return value in its decision-making.

For example, to secure the getSpittleById() method as previously described, you can use @PostAuthorize like this:

@PostAuthorize("returnObject.spitter.username == principal.username")

public Spittle getSpittleById(long id) {

// ...

}

For easy access to the object returned from the secured method, Spring Security provides the returnObject variable in SpEL. Here you know that the returned object is a Spittle, so the expression digs into its spitter property and pulls the username property from that.

On the other side of the double-equal comparison, the expression digs into the built-in principal object to get its username property. principal is another one of Spring Security’s special built-in names that represents the principal (typically the username) of the currently authenticated user.

If the Spittle object has a Spitter whose username property is the same as the principal’s username, the Spittle will be returned to the caller. Otherwise, an AccessDeniedException will be thrown, and the caller won’t get to see the Spittle.

It’s important to keep in mind that, unlike methods annotated with @Pre-Authorize, @PostAuthorize-annotated methods will be executed first and intercepted afterward. That means that care should be taken to make sure that the method doesn’t have any side effects that would be undesirable if authorization fails.

14.2.2. Filtering method inputs and outputs

@PreAuthorize and @PostAuthorize are great if you’re using expressions to secure a method. But sometimes restricting access to a method is too heavy-handed. Sometimes it’s not the method that’s being secured, but rather the data being passed into or returned from that method.

For instance, suppose that you have a method called getOffensiveSpittles() that returns a list of Spittles that have been flagged as offensive. This is a method that’s primarily intended to be used by an administrator to help moderate the content on the Spittr application. But it could also be used by an individual user to see if any of their Spittles have been flagged as offensive. The method signature might look something like this:

public List<Spittle> getOffensiveSpittles() { ... }

As it is, the getOffensiveSpittles() method isn’t concerned with any specific user. It merely returns a list of offensive Spittles, no matter who they belong to. That’s perfect for the administrative use of the method, but it falls short of limiting the list to those Spittles that belong to the current user.

Certainly, you could overload getOffensiveSpittles() with another version that accepts a user ID as a parameter and uses that to fetch only the offensive Spittles for a given user. But as I stated in the outset of this chapter, there’s always the possibility that the less restrictive version could be used in places where some restriction is needed.[1]

1 Besides that, if I overloaded getOffensiveSpittles() I’d have to dream up another example for showing you how to filter method output with SpEL.

What’s needed is a way to filter the collection of Spittles returned from get-OffensiveSpittles(), narrowing it down to the list that the current user is allowed to see. That’s precisely what Spring Security’s @PostFilter does. Let’s give it a try.

Postfiltering method return values

Just like @PreAuthorize and @PostAuthorize, @PostFilter takes a SpEL expression as its value parameter. But instead of using that expression to restrict access to a method, @PostFilter evaluates that expression against each member of a collection being returned from the method, removing those members for whom the expression evaluates to false.

To demonstrate, let’s apply @PostFilter to the getOffensiveSpittles() method:

@PreAuthorize("hasAnyRole({'ROLE_SPITTER', 'ROLE_ADMIN'})")

@PostFilter( "hasRole('ROLE_ADMIN') || "

+ "filterObject.spitter.username == principal.name")

public List<Spittle> getOffensiveSpittles() {

...

}

Here, the @PreAuthorize annotation only allows users with ROLE_SPITTER or ROLE_ADMIN authority to execute the method. If the user makes it through that checkpoint, the method will execute and a List of Spittles will be returned. But the @PostFilter annotation will filter that list, ensuring that the user only sees those Spittle objects that they’re allowed to see. Specifically, administrators get to see all offensive Spittles, and non-administrators will only be given Spittles that belong to them.

The filterObject referenced in the expression refers to an individual element (which you know to be a Spittle) in the List returned from the method. If that Spittle’s Spitter has a username that’s the same as the authenticated user (the principal.name in the expression) or if the user has the role of ROLE_ADMIN, then the element will end up in the filtered list. Otherwise, it’ll be left out.

Prefiltering method parameters

In addition to postfiltering a method’s return value, you also have the option of prefiltering the values passed into a method. This is a much less common technique, but it may come in handy on occasion.

For instance, suppose you have a list of Spittles that you want to delete as a batch. To accomplish that, you might write a method with a signature that looks a little like this:

public void deleteSpittles(List<Spittle> spittles) { ... }

Seems simple enough, right? But what if you want to apply some security rules to it, such that the Spittles can only be deleted by the user who owns them or by an administrator. In that case, you could write logic into the deleteSpittles() method to sift through each Spittle in the list and only delete those belonging to the current user (or all of them if the current user is an administrator).

While that would work, it means that you’re embedding security logic directly into the logic of the method. And that security logic represents a separate (albeit related) concern from the concern of deleting Spittles. What would be better is if the list only contained Spittles that were actually going to be deleted. That would keep the logic for deleteSpittles() simpler and focused on the task of deleting Spittles.

Spring Security’s @PreFilter seems to be a perfect fit for this problem. Much like @PostFilter, @PreFilter uses SpEL to filter a collection to only the elements that satisfy the SpEL expression. But instead of filtering the value returned from a method, @PreFilter filters those members of a collection going into the method.

Using @PreFilter is quite simple. Here’s the deleteSpittles() method, now annotated with @PreFilter:

@PreAuthorize("hasAnyRole({'ROLE_SPITTER', 'ROLE_ADMIN'})")

@PreFilter( "hasRole('ROLE_ADMIN') || "

+ "targetObject.spitter.username == principal.name")

public void deleteSpittles(List<Spittle> spittles) { ... }

As before, @PreAuthorize will prevent this method from being called on behalf of any user who doesn’t have either ROLE_SPITTER or ROLE_ADMIN authority. But also, @PreFilter will ensure that the list being passed into deleteSpittles() will contain only Spittles that the current user has permission to delete. The expression will be evaluated against each item in the collection, and only those items for whom the expression evaluates to true will remain in the list. The targetObject variable is another Spring Security–provided value that represents the current list item to evaluate against.

At this point, you’ve seen how to use all four of Spring Security’s expression-driven annotations. Expressions are a much more powerful way to define security constraints than just specifying an authority that must be granted to the user.

Even so, you should take care not to get too clever with the expressions. Certainly you should avoid writing complex security expressions or trying to embed too much non-security business logic into the expressions. Ultimately, expressions are just String values that are given to the annotations. As such, they’re difficult to test and difficult to debug.

If you find yourself thinking that maybe your security expressions are getting out of hand, you might want to look into writing a custom permission evaluator to help simplify your SpEL expressions. Let’s see how you can create and use a custom permission evaluator to simplify the expressions you’ve used for filtering.

Defining a permission evaluator

The expression we used with @PreFilter and @PostFilter certainly isn’t that complex. But it’s not trivial either, and it doesn’t take much to imagine how you might keep growing that expression to accommodate other security rules. Before long, the expression could become unwieldy, complex, and difficult to test.

What if you replaced that entire expression with a much simpler one that looks a little something like this:

@PreAuthorize("hasAnyRole({'ROLE_SPITTER', 'ROLE_ADMIN'})")

@PreFilter("hasPermission(targetObject, 'delete')")

public void deleteSpittles(List<Spittle> spittles) { ... }

Now the expression given to @PreFilter is much tighter. It simply asks the question “Does the user have permission to delete the target object?” If so, the expression will evaluate to true and the Spittle will remain in the list passed to deleteSpittles(). If not, then it will be tossed out.

But where did hasPermission() come from? What does it mean? And more importantly, how does it know whether or not the user has permission to delete the Spittle in targetObject?

The hasPermission() function is a Spring Security–provided extension to SpEL, and it represents an opportunity for you, the developer, to plug in whatever logic you want to perform when it’s evaluated. All you need to do is write and register a custom permission evaluator. Listing 14.1shows SpittlePermissionEvaluator, a custom permission evaluator that contains the expression logic.

Listing 14.1. A permission evaluator provides the logic behind hasPermission()

package spittr.security;

import java.io.Serializable;

import org.springframework.security.access.PermissionEvaluator;

import org.springframework.security.core.Authentication;

import spittr.Spittle;

public class SpittlePermissionEvaluator implements PermissionEvaluator {

private static final GrantedAuthority ADMIN_AUTHORITY =

new GrantedAuthorityImpl("ROLE_ADMIN");

public boolean hasPermission(Authentication authentication,

Object target, Object permission) {

if (target instanceof Spittle) {

Spittle spittle = (Spittle) target;

String username = spittle.getSpitter().getUsername();

if ("delete".equals(permission)) {

return isAdmin(authentication) ||

username.equals(authentication.getName());

}

}

throw new UnsupportedOperationException(

"hasPermission not supported for object <" + target

+ "> and permission <" + permission + ">");

}

public boolean hasPermission(Authentication authentication,

Serializable targetId, String targetType, Object permission) {

throw new UnsupportedOperationException();

}

private boolean isAdmin(Authentication authentication) {

return authentication.getAuthorities().contains(ADMIN_AUTHORITY);

}

}

SpittlePermissionEvaluator implements Spring Security’s PermissionEvaluator interface, which demands that two different hasPermission() methods be implemented. One of the hasPermission() methods takes an Object as the object to evaluate against in the second parameter. The other hasPermission() is useful when only the ID of the target object is available, and it takes that ID as a Serializable in its second parameter.

For our purposes, we’ll assume that you’ll always have the Spittle object to evaluate permissions against, so the other method simply throws UnsupportedOperation-Exception.

As for the first hasPermission() method, it checks to see that the object being evaluated is a Spittle and that you’re checking for delete permission. If so, it checks that the Spitter’s username is equal to the authenticated user’s name or that the current authentication has ROLE_ADMINauthority.

Once the permission evaluator is ready, you need to register it with Spring Security for it to back the hasPermission() operation in the expression given to @PostFilter. To do that, you’ll need to replace the expression handler with one that’s configured to use your custom permission evaluator.

By default, Spring Security is configured with a DefaultMethodSecurity-ExpressionHandler that’s given an instance of DenyAllPermissionEvaluator. As its name suggests, DenyAllPermissionEvaluator always returns false from its has-Permission() methods, denying all method access. But you can provide Spring Security with a DefaultMethodSecurityExpressionHandler configured with your custom SpittlePermissionEvaluator by overriding the createExpressionHandler method fromGlobalMethodSecurityConfiguration:

@Override

protected MethodSecurityExpressionHandler createExpressionHandler() {

DefaultMethodSecurityExpressionHandler expressionHandler =

new DefaultMethodSecurityExpressionHandler();

expressionHandler.setPermissionEvaluator(

new SpittlePermissionEvaluator());

return expressionHandler;

}

Now anytime you secure a method with an expression that uses hasPermission(), the SpittlePermissionEvaluator will be invoked and get to decide whether or not the user has permission to call the method.

14.3. Summary

Method-level security is an important complement to Spring Security’s web-level security, which we discussed in chapter 9. For non-web applications, method-level security is the front line of defense. When applied in a web application, method-level security backs up the security rules declared to secure web requests.

In this chapter, we looked at six annotations that can be placed on methods to declare security constraints. For simple, authorities-oriented security, Spring Security’s @Secured annotation or the standards-based @RolesAllowed come in handy. When the security rules get more interesting,@PreAuthorize and @PostAuthorize and SpEL provide more power. You also saw how to filter a method’s inputs and outputs using SpEL expressions given to @PreFilter and @PostFilter.

Finally, we looked at how you can make your security rules easier to maintain, test, and debug by defining a custom expression evaluator that works behind the scenes of the hasPermission() function in SpEL.

Starting with the next chapter, we’ll switch gears from developing the back end of the application to using Spring to integrate with other applications. Over the next several chapters, we’ll look at all kinds of integration techniques, including remoting, asynchronous messaging, REST, and even sending emails. The first integration technique on tap will be working with Spring remoting, which we’ll explore in the next chapter.