EnableReactiveMethodSecurity

Spring Security supports method security by using Reactor’s Context, which is set up by ReactiveSecurityContextHolder. The following example shows how to retrieve the currently logged in user’s message:

For this example to work, the return type of the method must be a org.reactivestreams.Publisher (that is, a Mono or a Flux). This is necessary to integrate with Reactor’s Context.

EnableReactiveMethodSecurity with AuthorizationManager

In Spring Security 5.8, we can enable annotation-based security using the @EnableReactiveMethodSecurity(useAuthorizationManager=true) annotation on any @Configuration instance.

This improves upon @EnableReactiveMethodSecurity in a number of ways. @EnableReactiveMethodSecurity(useAuthorizationManager=true):

  1. Uses the simplified AuthorizationManager API instead of metadata sources, config attributes, decision managers, and voters. This simplifies reuse and customization.

  2. Supports reactive return types including Kotlin coroutines.

  3. Is built using native Spring AOP, removing abstractions and allowing you to use Spring AOP building blocks to customize

  4. Checks for conflicting annotations to ensure an unambiguous security configuration

  5. Complies with JSR-250

For earlier versions, please read about similar support with @EnableReactiveMethodSecurity.

For example, the following would enable Spring Security’s @PreAuthorize annotation:

Method Security Configuration
  • Java

@EnableReactiveMethodSecurity(useAuthorizationManager=true)
public class MethodSecurityConfig {
	// ...
}

Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly. Spring Security’s native annotation support defines a set of attributes for the method. These will be passed to the various method interceptors, like AuthorizationManagerBeforeReactiveMethodInterceptor, for it to make the actual decision:

Method Security Annotation Usage
  • Java

public interface BankService {
	@PreAuthorize("hasRole('USER')")
	Mono<Account> readAccount(Long id);

	@PreAuthorize("hasRole('USER')")
	Flux<Account> findAccounts();

	@PreAuthorize("@func.apply(#account)")
	Mono<Account> post(Account account, Double amount);
}

In this case hasRole refers to the method found in SecurityExpressionRoot that gets invoked by the SpEL evaluation engine.

@bean refers to a custom component you have defined, where apply can return Boolean or Mono<Boolean> to indicate the authorization decision. A bean like that might look something like this:

Method Security Reactive Boolean Expression
  • Java

@Bean
public Function<Account, Mono<Boolean>> func() {
    return (account) -> Mono.defer(() -> Mono.just(account.getId().equals(12)));
}

Method authorization is a combination of before- and after-method authorization.

Before-method authorization is performed before the method is invoked. If that authorization denies access, the method is not invoked, and an AccessDeniedException is thrown. After-method authorization is performed after the method is invoked, but before the method returns to the caller. If that authorization denies access, the value is not returned, and an AccessDeniedException is thrown

To recreate what adding @EnableReactiveMethodSecurity(useAuthorizationManager=true) does by default, you would publish the following configuration:

Full Pre-post Method Security Configuration
  • Java

@Configuration
class MethodSecurityConfig {
	@Bean
	BeanDefinitionRegistryPostProcessor aopConfig() {
		return AopConfigUtils::registerAutoProxyCreatorIfNecessary;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	PreFilterAuthorizationReactiveMethodInterceptor preFilterInterceptor() {
		return new PreFilterAuthorizationReactiveMethodInterceptor();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeInterceptor() {
		return AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeInterceptor() {
		return AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	PostFilterAuthorizationReactiveMethodInterceptor postFilterInterceptor() {
		return new PostFilterAuthorizationReactiveMethodInterceptor();
	}
}

Notice that Spring Security’s method security is built using Spring AOP.

Customizing Authorization

Spring Security’s @PreAuthorize, @PostAuthorize, @PreFilter, and @PostFilter ship with rich expression-based support.

Also, for role-based authorization, Spring Security adds a default ROLE_ prefix, which is uses when evaluating expressions like hasRole. You can configure the authorization rules to use a different prefix by exposing a