Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 4535

Spring Security AuthorizationManager

$
0
0

1. Introduction

Spring Security is an extension of the Spring Framework that makes it easy to build common security practices into our applications. This includes things like user authentication and authorization, API protection, and much more.

In this tutorial, we look at one of the many pieces inside Spring Security: the AuthorizationManager. We’ll see how it fits into the larger Spring Security ecosystem, as well as various use cases for how it can help secure our applications.

2. What Is Spring Security AuthorizationManager

The Spring AuthorizationManager is an interface that allows us to check if an authenticated entity has access to a secured resource. AuthorizationManager instances are used by Spring Security to make final access control decisions for request-based, method-based, and message-based components.

As background, Spring Security has a few key concepts that are helpful to understand before looking at the specific role of the AuthorizationManager:

  • Entity: anything that can make a request into the system. This could be a human user or a remote web service, for example.
  • Authentication: the process of verifying that an entity is who they say they are. This can be via username/password, token, or any number of other methods.
  • Authorization: the process of verifying an entity has access to a resource
  • Resource: any information the system makes available for access — for example, a URL or document
  • Authority: often referred to as a Role, this is a logical name representing the permissions an entity has. A single entity may have zero or more authorities granted to it.

With these concepts in mind, we can dive deeper into the AuthorizationManager interface.

2.1. How to Use AuthorizationManager

AuthorizationManager is a simple interface that contains only two methods:

AuthorizationDecision check(Supplier<Authentication> authentication, T object);
void verify(Supplier<Authentication> authentication, T object);

Both methods look similar because they take the same arguments:

  • authentication: a Supplier that provides an Authentication object representing the entity making the request.
  • object: the secure object being requested (will vary depending on the nature of the request)

However, each method serves a different purpose. The first method returns an AuthorizationDecision, which is a simple wrapper around a boolean value that indicates whether or not the entity can access the secure object.

The second method doesn’t return anything. Instead, it simply performs the authorization check and throws an AccessDeniedException if the entity is not authorized to access the secure object.

2.2. Older Versions of Spring Security

It’s worth noting that the AuthorizationManager interface was introduced in Spring Security 5.0. Prior to this interface, the primary method for authorization was via the AccessDecisionManager interface. While the AccessDecisionManager interface still exists in recent versions of Spring Security, it is deprecated and should be avoided in favor of AuthorizationManager.

3. Implementations of AuthorizationManager

Spring provides several implementations of the AuthorizationManager interface. In the following sections, we’ll take a look at several of them.

3.1. AuthenticatedAuthorizationManager

The first implementation we’ll look at is the AuthenticatedAuthorizationManager. Put simply, this class returns a positive authorization decision based solely on whether or not the entity is authenticated. Additionally, it supports three levels of authentication:

  • anonymous: the entity is not authenticated
  • remember me: the entity is authenticated and is using remembered credentials
  • fully authenticated: the entity is authenticated and not using remembered credentials

Note that this is the default AuthorizationManager that Spring Boot creates for web-based applications. By default, all endpoints will allow access regardless of role or authority, as long as it comes from an authenticated entity.

3.2. AuthoritiesAuthorizationManager

This implementation works similarly to the previous one, except it can make decisions based on multiple authorities. This is more suitable for complex applications where resources may need to be accessible by more than one authority.

Consider a blogging system that uses different roles to manage the publishing process. The resource for creating and saving an article might be accessible to both the Author and Editor roles. However, the resource for publishing is available to only the Editor role.

3.3. AuthorityAuthorizationManager

This implementation is fairly straightforward. It makes all of its authorization decisions based on whether the entity has a specific role.

This implementation works well for simple applications where each resource requires a single role or authority. For example, it would work well for protecting a specific set of URLs to only entities with an Administrator role.

Note that this implementation delegates its decision-making to an instance of AuthoritiesAuthorizationManager. It’s also the implementation that Spring uses whenever we call hasRole() or hasAuthorities() while customizing a SecurityFilterChain.

3.4. RequestMatcherDelegatingAuthorizationManager

This implementation doesn’t actually make authorization decisions. Instead, it delegates to another implementation based on URL patterns, usually one of the above manager classes.

For example, if we have some URLs that are public and available to anyone, we could delegate those URLs to a no-op implementation that always returns a positive authorization. We could then delegate secured requests to an AuthoritiesAuthorizationManager that handles checking for roles.

In fact, this is exactly what Spring does when we add a new request matcher to a SecurityFilterChain. Each time we configure a new request matcher and specify one or more required roles or authorities, Spring just creates a new instance of this class along with an appropriate delegate.

3.5. ObservationAuthorizationManager

The final implementation we’ll look at is the ObservationAuthorizationManager. This class is really just a wrapper around another implementation, with the added ability to log metrics related to authorization decisions. Spring will automatically use this implementation whenever a valid ObservationRegistry is available in the application.

3.6. Other Implementations

It’s worth mentioning that several other implementations exist in Spring Security. Most of them are related to the various Spring Security annotations used to secure methods:

  • SecuredAuthorizationManager -> @Secured
  • PreAuthorizeAuthorizationManager -> @PreAuthorize
  • PostAuthorizeAuthorizationManager -> @PostAuthorize

Essentially, any Spring Security annotation we can use to secure resources has a corresponding AuthorityManager implementation.

3.7. Using Multiple AuthorizationManagers

In practice, we rarely ever use just a single instance of AuthorizationManager. Let’s take a look at an example SecurityFilterChain bean:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((authorize) -> authorize
        .requestMatchers("/posts/publish/**").hasRole("EDITOR")
        .requestMatchers("/posts/create/**").hasAnyRole("EDITOR", "AUTHOR")
        .anyRequest().permitAll());
    return http.build();
}

This example uses five different AuthorizationManager instances:

  • The call to hasRole() creates an instance of AuthorityAuthorizationManager, which in turn delegates to a new instance of AuthoritiesAuthorizationManager.
  • The call to hasAnyRole() also creates an instance of AuthorityAuthorizationManager, which in turn delegates to a new instance of AuthoritiesAuthorizationManager.
  • The call to permitAll() uses a static no-op AuthorizationManager provided by Spring Security that always provides a positive authorization decision.

Additional request matchers with their own roles, along with any method-based annotations, would all create additional AuthorizationManager instances.

4. Using a Custom AuthorizationManager

The provided implementations above are sufficient for many applications. However, as with many interfaces in Spring, it is entirely possible to create a custom AuthorizationManager to suit whatever needs we have.

Let’s define a custom AuthorizationManager:

AuthorizationManager<RequestAuthorizationContext> customAuthManager() {
    return new AuthorizationManager<RequestAuthorizationContext>() {
        @Override
        public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
            // make authorization decision
        }
    };
}

We would then pass this instance while customizing the SecurityFilterChain:

SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((authorize) ->
                    authorize.requestMatchers("/custom/**").access(customAuthManager())
    return http.build();
}

In this case, we’re making authorization decisions using a RequestAuthorizationContext. This class provides access to the underlying HTTP request, meaning we can make decisions based on things like cookies, headers, and more. We can also delegate to a third-party service, database, or cache, among other constructs, to make any type of authorization decision we want.

5. Conclusion

In this article, we’ve taken a close look at how Spring Security handles authorization. We saw the generic AuthorizationManager interface and how its two methods make authorization decisions.

We also saw various implementations of this implementation, and how they are used in various places in the Spring Security framework.

Finally, we created a simple custom implementation that can be used to make any type of authorization decisions that we need in our applications.

As always, the code examples in this article are available over on GitHub.

       

Viewing all articles
Browse latest Browse all 4535

Trending Articles