Preauthorize annotation can be use to enforce access control in Spring Boot applications. It's part of Spring Security that allows you to specify access control expression for methods. These expressions are evaluated before method execution, determining whether the current user has the authority to invoke the method.
public class AuthorizedService {
@PreAuthorize("hasAuthority(@config.getAuthority())")
public Mono hasAuthority() {
return Mono.just(true);
}
}
In this example, the hasAuthority() method is protected by a @PreAuthorize annotation. The expression "hasAuthority(@config.getAuthority())" checks if the current user has the authority specified by the getAuthority() method of a @Config bean.
To make this work, we need to define our Config class:
@Component@Getter
public class Config {
@Value("${spring.application.authority}")
private String authority;
}
This class reads the required authority from the application's configuration. In your application.yaml, you can specify the required authority:
The Challenge of Unit Testing
When it comes to unit testing a service with @PreAuthorize, we encounter some challenges. Let's look at a common approach that doesn't work as expected:
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
This approach fails because Mockito-created mocks are not proxied by Spring Security. As a result, the @PreAuthorize annotation is not processed, and the security checks are not performed during the test.
The Solution: Integration Testing
To properly test methods annotated with @PreAuthorize, we need to use integration tests that load the entire Spring context. Here's how we can modify our test: