Exploring Spring Security's Authorization Mechanisms


In this blog post, we’ll explore how Spring Security handles authorization. The first part concentrates on web security and the integration with Spring Web MVC. In the second part, we’ll have a look at method security and the integration with Spring AOP.

This post builds upon the previous one (‘Exploring Spring Security’s Authentication Mechanism in Web Applications’) where we had a detailed look at how Spring Security handles authentication in the context of a web application. Because there are some parallels between the handling of authentication and authorization, I’ll omit details already discussed there. If you have the feeling this post skips something, have a look at the previous one.

Authorization in the Spring Web MVC Context

In this section, we will explore how Spring Web MVC uses Spring Security to secure specific pages based on the authority of a user. This picture shows the integration of Spring Web MVC and Spring Security

As you can see, the involved components are nearly identical to the ones used in the authentication of a user. Instead of the AuthenticationFilter, the AuthorizationFilter inside the SecurityFilterChain is used to handle authorization. Like other security-related filters, you typically add and configure the AuthorizationFilter inside the bean definition of the SecurityFilterChain.

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
            .authorizeHttpRequests(authorize -> {
                authorize.requestMatchers("/admin/**").authenticated();
                authorize.anyRequest().permitAll();
            })
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
            .build();
    }
}
  • This method configures the AuthorizationFilter and adds it to the SecurityFilterChain.

With this high-level diagram of the involved components done, let’s dig a bit deeper into how the AuthorizationFilter works.

This picture shows how the AuthorizationFilter works in detail.

The AuthorizationFilter doesn’t contain much authorization-related logic itself. It merely delegates the authorization request to an AuthorizationManager. In the context of Spring Web MVC, this is per default a RequestMatcherDelegatingAuthorizationManager. It receives a Supplier<Authentication> and the HttpServletRequest that needs to be authorized. It also contains a List<RequestMatcherEntry>, which is used to determine if it is responsible for the authorization of the given request. If one of the RequestMatcherEntrys matches, its corresponding AuthorizationManager is used to do the actual authorization of the request. The used AuthorizationManagers could be an AuthenticatedAuthorizationManager, an AuthorityAuthorizationManager, any of the other provided ones, or a custom one. In the end, this AuthorizationManager returns its AuthorizationDecision to the AuthorizationFilter where the SecurityFilterChain is resumed, or an AccessDeniedException is thrown.

Authorization in the Method Security Context

As you may know, there are multiple ways to add authorization checks to our Spring application. We can use method security, to directly annotate the methods we want to secure. To use it, we must first enable this feature by adding the @EnableMethodSecurity annotation to any configuration class. The configuration handling other security aspects is a good fit.

@Configuration
@EnableMethodSecurity
public class SecurityConfiguration {
    // bean definitions omitted for brevity
}
  • By adding the @EnableMethodSecurity annotation to a class annotated with @Configuration we enable the method security.

As soon as method security is enabled, we can start to annotate our methods with @PreAuthorize, @PostAuthorize, and more.

@GetMapping("/")
@PreAuthorize("hasRole('admin')")
public String helloWorld(){
    return "Hello, World";
}
  • We use method security to only allow users with the role “admin” to invoke this method

With the explanation of what method security is, we can now start exploring how it works.

This picture shows the integration of Spring AOP and Spring Security

By using one of the method security annotations, the starting context of the authorization isn’t Spring Web MVC anymore. The authorization is started in the context of Spring AOP where the ReflectiveMethodInvocation acts as a bridge to Spring Security. Depending on the used method security annotation, the ReflectiveMethodInvocation invokes one of the MethodInterceptors. In the case of @PreAuthorize, this is the AuthorizationManagerBeforeMethodInterceptor. These MethodInterceptors use an AuthorizationManager to do the actual authorization. A concrete implementation of such an AuthorizationManager is the PreAuthorizeAuthorizationManager. By receiving a MethodInvocation, it can extract the SPel-Expression used in the @PreAuthorize annotation and evaluate it. The result of this evaluation is returned to the MethodInterceptor that either allows the invocation of the target method or throws an AccessDeniedException.

Conclusion

Spring Boot provides a versatile set of authorization tools out-of-the box. With a combination of web security and method security, we can protect the HTTP endpoints and all the methods of our application. We also saw the incredible flexibility of Spring’s authorization mechanism. With the generic nature of the AuthorizationManager, we can authorize things in any context. By specifying the generic type, we can authorize web requests (HttpServletRequest), method invocations (MethodInvocation), WebSocket messages (Message), and more.