Spring Security: Method Level Security @Secured, @RolesAllowed and @PreAuthorize/@PostAuthorize

The complete code of this article can be found at GitHub

One of the great feature in spring security is, it has the ability of providing both URL based security and method level security.  All these annotations – @Secured , @RolesAllowed, @PreAuthorize / @PostAuthorize are used to  achieve the method level security.

AbstractSecurityInterceptor

We will refresh our knowledge about spring security authorization. In spring security, the initial authorization for the user request will be handled by the AbstractSecurityInterceptor.

There are two concrete implementation of the AbstractSecurityInterceptor and they are as follows.

  1. FilterSecurityInterceptor – responsible for handling URL based security/authorization
  2. MethodSecurityInterceptor – responsible for handling method level security/authorization

In Spring Security filter chain, FilterSecurityInterceptor is loaded by default as the last filter in the chain. Therefore all authenticated user requests will go through the FilterSecurityInterceptor for authorization. In spring security, URL based authorization can be treated as the default way of handling user authorization because, FilterSecurityInterceptor is loaded by default and it will intercept all authenticated requests without any configuration.

But if you need to implement method level security, then you need to add the @EnableGlobalMethodSecurity annotation to the spring security configuration class. Then it will register and add the MethodSecurityInterceptor  in the filter chain and it will allow to secure the methods using the security annotations. (Those security annotations types must be enabled. Please refer the below code)

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

securedEnabled : set true if  @Secured annotation need to be used  for method level security

prePostEnabled : set true if @PreAuthorize and @PostAuthorize annotations need to be used  for method level security

jsr250Enabled  : set true if  @RolesAllowed annotation need to be used  for method level security

 

@Secured

This annotation is introduced by spring security and can be used for method level security just  giving the role name.

 

e.g:- @Secured with single ROLE

@Secured("ROLE_ADMIN")
@GetMapping(value = "/admin/hello")
public String welcomeAdminUser() {
   return "Welcome Admin ";
}

This method can be accessed by any user with ROLE_ADMIN.

 

e.g:- @Secured with multiple roles

@Secured({"ROLE_ADMIN","ROLE_SUPER_ADMIN"})
@GetMapping(value = "/admin/hello")
public String welcomeAdminUser() {
  return "Welcome Admin ";
}

This method can be accessed by any user with ROLE_ADMIN  OR  ROLE_SUPER_ADMIN.

 

 

 Problem with @Secured

even if @Secured is a easiest way of handling method level security, it has following drawbacks.

  • It does not support multiple roles conjunctions with AND operator. (If there are multiple roles defined, they will be automatically combined with OR operator)
  •  It does not support SpEL (Spring Expression Language)

As a solution for the above problem Spring Security has introduced @PreAuthorize annotation later on.

 

@PreAuthorize and @PostAuthorize

Spring Security provides @PreAuthorize and @PostAuthorize annotations for method level security and those are known as expression based annotations. that means SPEL (Spring Expression Language) will be used to determine the authorization of the user on the requested resource. These annotations provide more fine-grained authorization and access control for the methods.

As name implies, @PreAuthorize check the authorization before method execution (before entering into the method)  and @PostAuthorize check the authorization after method execution (after completing the execution of the method, but before returning from the method).

Normally @PreAuthorize uses SpEL to check the authorization based on the logged user  roles and content if the method parameters.  @PostAuthorize uses  SpEL to check the authorization on specially on logged users and return object content.   Spring Security provides special SpEL built-in keywords to access required objects for SpEL expressions.

e.g:-

returnObject – refer the return object by the method

authentication – refer the user authentication object.

principal – refer the user authentication principal

 

According to the spring security documentation

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 == authentication.name")
public void doSomething(Contact contact);

Here we are accessing another built-in expression, authentication, which is the Authentication stored in the security context. You can also access its “principal” property directly, using the expression principal. The value will often be a UserDetails instance, so you might use an expression like principal.username or principal.enabled.

Less commonly, you may wish to perform an access-control check after the method has been invoked. This can be achieved using the @PostAuthorize annotation. To access the return value from a method, use the built-in name returnObject in the expression.

 

 Lets look at some real examples.

As we are already aware, @PreAuthorize checks for the authorization before method execution and @PostAuthorize checks for the authorization after method execution. Lets remember these two principles and move forward.

 

Example1

 @PreAuthorize("hasRole('USER')")
 @GetMapping(value = "/user/hello1")
 public String welcomeAppUser1(@AuthenticationPrincipal User user) 
 {
      return "Welcome User " + user.getName();
 }

@PreAuthorize(“hasRole(‘USER’)”)  is identical to the @Secured(“ROLE_USER’). The only difference is that @PreAuthorize accepts only the regular expression as the parameter and ROLE_ prefix is missing (It will be automatically added by the hasRole method).  Therefore this method can be accessed by any user who is having the ROLE_USER.

 

Example2

 @PreAuthorize("hasRole('USER') AND hasRole('ADMIN')")
 @GetMapping(value = "/user/hello2")
 public String welcomeAppUser2(@AuthenticationPrincipal User user) 
 {
      return "Welcome User " + user.getName();
 }

This method can be accessed by any user who is having both ROLE_USER and ROLE_ADMIN.  (only the users with both of above roles will be allowed)

 

Example 3

 @PreAuthorize("hasRole('USER') OR hasRole('ADMIN')")
 @GetMapping(value = "/user/hello3")
 public String welcomeAppUser3(@AuthenticationPrincipal User user) 
 {
    return "Welcome User " + user.getName();
 }

This method can be accessed by any user who is having ROLE_USER or ROLE_ADMIN. (users with any of above rule will be allowed). This is identical to the below.

@Secured({“ROLE_USER” , “ROLE_ADMIN“})

 

Example 4

@PreAuthorize("hasRole('USER')")
@PostAuthorize("(returnObject.username == authentication.name) AND hasRole('ADMIN')")
@GetMapping(value = "/user/hello5")
public UserProfile welcomeAppUser5(@AuthenticationPrincipal User user) 
{
    return new UserProfile("chathuranga", "Chathuranga Tennakoon");
}

Since the @PreAuthorize checks for only the ROLE_USER, this method can be accessed by any user who is having the ROLE_USER.  But the @PostAuthorize has a different set of authorization conditions and in order to return from the method those conditions need to be satisfied.

It says that the returned object username should be equal to the authenticated principal name. In addition the user should have the ROLE_ADMIN.  According to this block of code, the authenticated principle name should also be “chathuranga

 

 

@RolesAllowed

All of the above annotations (@Secured, @PreAuthorize, @PostAuthorize) has been defined by spring security and they are known as Spring security based annotations.

@RolesAllowed is a standard java annotation and defined in JSR 250 specification. It works in the same way as @Secured annotation. The only difference is that @Secured is a spring security based and @RolesAllowed is a java standard annotation based on JSR 250 specification.

 

Example 5

@RolesAllowed("ROLE_USER")
@GetMapping(value = "/user/hello6")
public String welcomeAppUser6(@AuthenticationPrincipal User user) 
{
    return "Welcome User " + user.getName();
}

This method can be accessed by any user having ROLE_USER

 

Example 6

@RolesAllowed({"ROLE_USER", "ROLE_ADMIN"})
@GetMapping(value = "/user/hello7")
public String welcomeAppUser7(@AuthenticationPrincipal User user) 
{
    return "Welcome User " + user.getName();
}

This method can be accessed by any user having ROLE_USER or ROLE_ADMIN.

We have covered all three ways of achieving method level security with Spring Security. The complete code of this article can be found at GitHub

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s