Configuring Authorization with Reactive Spring Security 5

3 minute read Published:

It takes just a few minutes to bring a compromised system to it's knees. Help fight this by securing your application with Spring Security
Table of Contents

Configuring Authorization against a Webflux App

Effective security can insulate our applications from ill effects of malicious, and accidental intent in many aspects of programming. Things like network security can only go so far in isolating harm to distributed computing applications. Prevent malicious hackers from gaining access to your systems by ensuring the tools meet the standards for your appcation.

Spring Security WebFlux is the framework that lets us declare security constructs to our ordinary WebFluxapplications. This is similar to classical Spring Security and WebMVC with the major difference being the use of functional and reactive techniques.

Getting Started

To get started, one may use start.spring.io, or just ensure the following dependencies are configured to the project going forward:

Next, lets briefly look at how this framework puts things together. We can compose our sample app throughout this article while highlighting common and often visited architectural pieces.

Configuring Authorization Components

How does Spring Security Webflux let us describe our security details?

ServerHttpSecurity surfaces components for customizing security behaviour across our web-stack through a DSL-like, fluent API. ServerHttpSecurity ultimately builds the state of a SecurityWebFilterChain which gets executed within the primary WebFilter.

Lets take a look at some of the components we’ll use to setup security throughout a web request/response lifecycle, hence ServerWebExchange. We are given a variety of specifications that let us decide on which part of the ServerWebExchange we can lock down.

Component ServerHttpSecurity method handling use cases
AuthorizeExchangeSpec .authorizeExchange() pathMatchers, RBAC, custom Authorization
HeadersSpec .headers() Cross Site Scriptiong, Strict Transport Security, cache-control, frame options, etc…
CsrfSpec .csrf() setup handler and token repository
ExceptionHandlingSpec .exceptionHandling() handler for authentication entry point and denial
HttpBasicSpec .httpBasic() custom AuthenticationManager, authentication context config
RequestCacheSpec .requestCache() handle saving httpRequest prior to authentication
FormLoginSpec .formLogin() set login page, authentication behaviour on success/deny
LogoutSpec .logout() set logout page and handler

NOTE: All of the above components may be disabled using it’s .disable() method!

Authorization Configuration

Using the AuthorizeExchangeSpec by invoking authorizeExchange(), one can issue URI PathPattern’s that will match Access Control rules to paths on the service route.

For example, hasRole() method is a shorthand for hasAuthority() method where the user’s GrantedAuthority (aka privilege) is checked for specific values. The hasRole() requires each authority be prefixed with ‘ROLE_‘.

Finally, there is the access() method that takes a anonymous or otherwise custom implementation of ReactiveAuthorizationManager. This is useful for in-house authorization implementations.

CSRF Configuration

Another component for when configuring SecurityWebFilterChain is the CsrfSpec enabled by calling csrf() method. This lets us configure CSRF tokens and handlers, or exclude CSRF entirely.

To configure CSRF metadata behaviour, create a bean of type ServerCsrfTokenRepository and set header and/or parameter attrubte names as shown.

SecurityConfiguration.java:

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
public class SecurityConfiguration {

    @Bean
    public ServerCsrfTokenRepository csrfTokenRepository() {
        WebSessionServerCsrfTokenRepository repository =
            new WebSessionServerCsrfTokenRepository();
        repository.setHeaderName("X-CSRF-TK");

        return repository;
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {

    return http
            .authorizeExchange()
            .pathMatchers("/who")
            .hasRole("USER")
            .pathMatchers("/primes")
            .hasAuthority("ROLE_USER")
            .pathMatchers("/admin")
            .access((mono, context) -> mono
                    .map(auth -> auth.getAuthorities().stream()
                            .filter(e -> e.getAuthority().equals("ROLE_ADMIN"))
                            .count() > 0)
                    .map(AuthorizationDecision::new)
            )
            .and()
            .csrf()
                .csrfTokenRepository(csrfTokenRepository())
                .and()
            .httpBasic()
            .and()
            .build();
    }

}

Additionally the and() and or() and disable() methods lets us build another component’s filter on the filter chain. In this case, we give our customized ServerCsrfTokenRepository, and configure HTTP Basic. Calling build() returns the completed SecurityWebFilterChain.

Review

This brief overview should set you up for engaging the ServerHttpSecurity components. Following it’s fluent API is a breeze once we get to know the components that we visit.