In this tutorial, you will learn how to use the @PostAuthorize annotation to secure the return of the method’s return value in your Spring Boot Application. The @PostAuthorize annotation is evaluated after the business logic in a method is executed and if needed will prevent the method from returning a return value.
There are other useful method level security annotations like the ones below. It is useful to know how they work as well.
If you are interested in video lessons, then I also show how to create user Roles and Authorities and how to use Spring Method Level Security annotations in my video course: RESTful Web Services, Spring Boot, Spring MVC, and JPA.
Enable Spring Security
To be able to use the @PostAuthorize annotation in your Spring Boot application you will first need to enable Spring Security in your project. To enable Spring Security in your Spring Boot application add the following dependency to the pom.xml file.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
Now when you have added the Spring Security support to your Spring Boot application, you can proceed and enable the Global Method Security and the @PostAuthorize annotation.
Enable the @PostAuthorize Annotation
To enable @PreAuthorize and also @PostAuthorize annotations in your Spring Boot application you will need to first enable the Global Method Security. To enable the Global Method Security, add the @EnableGlobalMethodSecurity annotation to any Java class in your application which has the @Configurationannotation. If your application has Spring Security enabled and at least a Basic Authentication configured, then you can add the @EnableGlobalMethodSecurity annotation and enable method level security in a class that configures HTTPSecurity and is annotated with @EnableWebSecurity annotation.
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurity extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // Some code here } }
Notice the use of prePostEnabled=true in @EnableGlobalMethodSecurity annotation. The prePostEnabled=true is what enables the @PreAuthorize and also @PostAuthorize annotations.
Now when you have the @PostAuthorize annotation enabled, let’s write a very simple security expression to allow the method to return a return object only when it is allowed.
@PostAuthorize Annotation Example
When using the @PostAuthorize annotation it is very important to keep in mind that this annotation will allow the business logic of a method to execute first and only then, the security expression it contains will be evaluated. So, be careful and do not use this annotation with methods that perform modifying queries like for example Delete User or Update User.
One of the good use cases to use the @PostAuthorize annotation will be a method that reads some information from a database and other sources and returns a return value. For example, we can use the @PostAuthorize annotation with a method that reads user details from a database and then returns a user object from a method.
Let’s write a security expression that allows the method to return user object only if the userId of a user matches the userId of a currently logged in principal user.
The returnObject
The @PostAuthorize annotation has access to an object that the method is going to return. Any object that your method is going to return, can be accessed in a security expression via the “returnObject“.
Below is an example of a Java class that contains user details. When writing a security expression for the @PreAuthorize annotation, you can access the userId property of the below class with returnObject.userId.
public class User { private String userId; private String firstName; private String lastName; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
An example of method-level security expression that accesses return objects userId property.
@PostAuthorize("returnObject.userId == principal.userId")
Let’s have a look at an example of @PreAuthorize annotation used above the method name in a Rest Controller class.
The below method returns an object of User class. A security expression used in the @PostAuthorize annotation will be validated after the method is executed but the method will actually return a value only of the userId of a currently logged in user will match the userId of a User object.
@PostAuthorize("returnObject.userId == principal.userId") @GetMapping(path = "/{id}", produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }) public User getUser(@PathVariable String id) { User returnValue = new User(); UserDto userDto = userService.getUserByUserId(id); ModelMapper modelMapper = new ModelMapper(); returnValue = modelMapper.map(userDto, User.class); return returnValue; }
Roles and Authorities
If your application supports user Roles and Authorities, you can write security expressions that validate user authority. For example, the below @PreAuthorize security annotation will allow a method to return a value only if a logged-in user has an ADMIN role or is an owner of the object that is being returned.
@PostAuthorize("hasRole('ADMIN') or returnObject.userId == principal.userId")
I hope this tutorial was some value to you.
If you are interested to learn how other security annotations work, then have a look at the following tutorials:
Happy learning! 🙋🏻♂️