February 03, 2020

Spring Security-Part 4.2: @PreAuthorize, @PostAuthorize, @Secured, @RoleAllowed, @EnableGlobalMethodSecurity annotations


Annotation Based Roles and Authorities/Permissions

In our previous tutorial we had configured the roles and authorities in ApplicationSecurityConfig (which extends WebSecurityConfigurerAdapter). We can do the same thing by using @PreAuthorize annotation.

Both @PreAuthorize and @PostAuthorize annotations provide expression-based access control.

The @PreAuthorize annotation checks the given expression before entering the method, whereas, the @PostAuthorize annotation verifies it after the execution of the method and could alter the result.

The @PreAuthorize("hasRole(‘ROLE_VIEWER')") has the same meaning as @Secured("ROLE_VIEWER").

@PreAuthorize("hasRole('ROLE_ADMIN')")
public String getCourseDetails() {
     //get the course details from database
}

We can also use the method argument as part of the expression, e.g:
@PreAuthorize("#username == authentication.principal.username")
public String getMyRoles(String username) {
    //...fetch the details from database
}

We can do the same thing with @PostAuthorize, however, the authorization would get delayed after the execution of getMyRoles() method.
@PostAuthorize("#username == authentication.principal.username")
public String getMyRoles(String username) {
    //...fetch the details from database
}

The @PostAuthorize annotation provides the ability to access the method result. In the below example, the loadUserDetail method would only execute successfully if the username of the returned User is equal to the current authentication principal's nickname.

@PostAuthorize("returnObject.username == authentication.principal.nickname")
public User loadUserDetail(String username) {
    return userRepository.loadUserByUserName(username);
}

Securing Service Layer with @PreAuthorize, @PostAuthorize, @Secured, @RoleAllowed Annotation

@Secured annotation is used to specify a list of roles on a method. A user can access that method only if she/he has at least one of the specified roles. The @Secured annotation doesn't support Spring Expression Language (SpEL).

In the below example, users with Admin role can only access getCourseDetails() method.

@Secured("ROLE_ADMIN")
public String getCourseDetails() {
     //get the course details from database
}

We can also define a list of roles in a @Secured annotation. In the below example getStudentDetails() can be accessed by both Students and Admin uses.

@Secured({"ROLE_ADMIN", "ROLE_STUDENT" })
public String getStudentDetails() {
     //get the student details from database
}

@RoleAllowed annotation is the JSR-250’s equivalent annotation of the @Secured annotation. We can use the @RoleAllowed annotation in a similar way as @Secured.

In the below example, users with Admin role can only access getCourseDetails() method.

@RoleAllowed("ROLE_ADMIN")
public String getCourseDetails() {
     //get the course details from database
}

We can also define a list of roles in a @RoleAllowed annotation. In the below example getStudentDetails() can be accessed by both Students and Admin uses.

@RoleAllowed({"ROLE_ADMIN", "ROLE_STUDENT" })
public String getStudentDetails() {
     //get the student details from database
}

@PreFilter and @PostFilter Annotations can be used to filter a collection argument before or after executing the method.

In the below example, we're joining all owner names except for the one who is authenticated. The name filterObject to represent the current object in the collection.

@PreFilter("filterObject != authentication.principal.ownername")
public String joinCourseOwner(List courseNames) {
     //get the course owner name's from database and join them
}

If the method has more than one argument which is a collection type, we need to use the filterTarget property to specify which argument we want to filter.

@PreFilter(value = "filterObject != authentication.principal.ownername",
filterTarget = "ownernames")
public String joinCourseOwner(List courseNames) {
     //get the course owner name's from database and join them
}

We can also filter the returned collection of a method by using @PostFilter annotation. In the below example the name filterObject refers to the current object in the returned collection. Spring Security will iterate through the returned list and remove any value which matches with the principal's username.

@PostFilter("filterObject != authentication.principal.ownername")
public List getAllOwnernamesExceptCurrent() {
    //get the owner names
}

@EnableGlobalMethodSecurity
  • To enable the Global Method Security, we need to add the @EnableGlobalMethodSecurity annotation to any Java class in our application which has the @Configuration annotation. 
  • If an application has Spring Security enabled and at least a Basic Authentication configured, then we can add the @EnableGlobalMethodSecurity annotation and enable method level security in a class that configures HTTPSecurity and is annotated with @EnableWebSecurity annotation.
@EnableGlobalMethodSecurity has below properties:
  • prePostEnabled property: Determines if Spring Security's pre post annotations should be enabled. Default is false.
  • securedEnabled property: Determines if the @Secured annotation should be enabled. Default is false. 
  • jsr250Enabled property allows us to use the @RoleAllowed annotation. Default is false.

-K Himaanshu Shuklaa..

No comments:

Post a Comment