Spring Security (series) - Method authorization #7
Spring Security can go deeper than just URL matchers! 🐳
Brief
When adding authorization rules to our application, we can either:
- do the checking at the URL level, like we did before with the MVC matchers.
- add them directly on the service methods. This way, we are able to implement Spring Security even though we don't have a web application.
In this post, we'll implement the second approach with the help of some Spring annotations: @PreAuthorize
, @PostAuthorize
, @PreFilter
and @PostFilter
.
Implementation
🧠 Before we start, you need to keep in mind that method-level security is implemented using Spring AOP.
This means that if a secured
method is called by another method of the same class, the security rules will be ignored.
All these annotations support SpEL (Spring Expression Language) written predicates. Predicates can be applied on SecurityContext
information along with input parameters and output results.
Example:
@PreAuthorize("#user == authentication.principal.username")
public Integer getScore(String user) { //...}
Now, let's take a look at these 4 annotations one by one and see what they can do for us. 👀
@PreAuthorize
- Apply the predicate and if it fails, the method won't be called and a403 Forbidden
response will be returned.@PostAuthorize
- Apply the rule after the method is called and if it fails, the response won't be returned.- This annotation is used to apply rules on the returned object. Don't use it with "mutating" actions because they will still be executed.
@PreFilter
- Filter the parameter list, based on the predicate, before the actual method call.@PostFilter
- Applies the rule to each item of the output and returns only the items which conform to the predicate.
Let's see them in action! 🚀
First, we need some configuration in place.
🔵 On the SecurityConfig
class we need to add the following annotation:
@EnableGlobalMethodSecurity(prePostEnabled = true)
– so that the annotations work ( @PreAuthorize
, etc..)
❗️ If you code with me and work based on the previous implementation, you will need to remove this method, so that you don't use the MVC matchers too.
🔵 We need a service with some methods that we will secure.
🔵 Add a Controller
just to test it easier, exposing the secured methods on some endpoints. We don't really need it, we could test it using some integration tests as well.
🐞 Test it with Postman:
PreAuthorize
PostAuthorize
PreFilter
PostFilter
As always, you can find the full implementation in the Github repo! 🚀