We often need to restrict access to our domain objects based on who has logged in. Take this business rule for example – “an employee record could be edited only by her department head.”

Spring Security does support ACL to handle this kind of domain object security requirments, but that often seems too heavy weight. In this post, let’s instead discuss a light-weight pattern for this. If you have gone through our Spring Tutorial Course III, you’ll find it quite familiar. So, below are the steps.

1) Configure method-level authorization

You may be knowing that we can restrict access to our service methods using pre/post annotations. To configure it, just annotate one of your configuration classes with @EnableGlobalMethodSecurity(prePostEnabled = true). E.g.

@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class FooApplication {
    public static void main(String[] args) {
        SpringApplication.run(FooApplication.class, args);
    }
}

Now, to restrict access to a method, we can annotate it with with pre/post annotations like @PreAuthorize. Here is an example:

@PreAuthorize("isAuthenticated()")
public void editEmployee(Employee employee) {
   ...
}

@PreAuthotize takes a Spring EL expression, which is isAuthenticated() in the above case.

Here is a list of methods that can be used in the expression. Of those, hasPermission(Object target, Object permission) ‘ll be of our interest here.

2) Use hasPermission

hasPermission decides whether the currently logged-in user should be allowed a particular operation on a particular object. It can be used as below:

@PreAuthorize("hasPermission(#employee, 'edit')")
public void editEmployee(Employee employee) {
   ...
}

The above method will be executed only if the current-user has edit permission to the employee parameter. Otherwise, an AccessDeniedException will be thrown.

For this to work, we need to supply a PermissionEvaluator.

3) Code a PermissionEvaluator

A PermissionEvaluator implementation would look like this:

@Component
public class PermissionEvaluatorImpl implements PermissionEvaluator {
    @Override
    public boolean hasPermission(Authentication auth,
            Object targetDomainObject, Object permission) {
        // return true if "auth" has "permission" permission for the user.
        // Current-user can be obtained from auth.
    }
    ...
}

As you see above, the PermissionEvaluatorImpl has a hasPermission method, which receives three parameters:

  1. The Spring Security authentication object, from which we can get the currently logged in user
  2. The object for which the permission has to be checked
  3. The permission

Using these parameters, we can decide whether to allow the annotated method to be executed, and return true if yes, else return false. For example, a naive implementation for our employee example could look like this:

@Component
public class PermissionEvaluatorImpl implements PermissionEvaluator {
    @Override
    public boolean hasPermission(Authentication auth,
        Object targetDomainObject, Object permission) {
        // Get the User from auth
        User loggedInUser =
            (auth == null || auth.getPrincipal() == null)
                ?  null
                : (User) auth.getPrincipal();
        if (targetDomainObject == null) // if no domain object is provided,
            return true;                // let's pass, allowing the service method
                                        // to throw a more sensible error message
        if (targetDomainObject instanceof Employee ) {
            if (permission.equals("edit") {
                Employee employee = (Employee) targetDomainObject;
                return employee.getDeparementHead().equals(loggedInUser);
            } 
        }
        return false;
    }   
    @Override
    public boolean hasPermission(Authentication authentication,
             Serializable targetId, String targetType, Object permission) {
        throw new UnsupportedOperationException(
                "hasPermission() by ID is not supported");
    }
}

The above would work, but, when we have tons of domain classes and permissions, wouldn’t it be dirty to code all the rules at one place?

So, in the next post, we’ll look at a pattern to segregate the rules in different classes. Meanwhile, if you found it useful, please share it with your friends!