diff --git a/spring-5-reactive-security/pom.xml b/spring-5-reactive-security/pom.xml
index 72a73a86ce..1a7dba2b9e 100644
--- a/spring-5-reactive-security/pom.xml
+++ b/spring-5-reactive-security/pom.xml
@@ -129,6 +129,7 @@
1.0
4.1
3.1.6.RELEASE
+ 2.2.1.RELEASE
diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverApplication.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverApplication.java
new file mode 100644
index 0000000000..bad5768c20
--- /dev/null
+++ b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.reactive.authresolver;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.reactive.config.EnableWebFlux;
+
+@EnableWebFlux
+@SpringBootApplication
+public class AuthResolverApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AuthResolverApplication.class, args);
+ }
+
+}
diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverController.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverController.java
new file mode 100644
index 0000000000..fdce66380b
--- /dev/null
+++ b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverController.java
@@ -0,0 +1,25 @@
+package com.baeldung.reactive.authresolver;
+
+import java.security.Principal;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+@RestController
+public class AuthResolverController {
+
+ @GetMapping("/customer/welcome")
+ public Mono sayWelcomeToCustomer(Mono principal) {
+ return principal
+ .map(Principal::getName)
+ .map(name -> String.format("Welcome to our site, %s!", name));
+ }
+
+ @GetMapping("/employee/welcome")
+ public Mono sayWelcomeToEmployee(Mono principal) {
+ return principal
+ .map(Principal::getName)
+ .map(name -> String.format("Welcome to our company, %s!", name));
+ }
+
+}
diff --git a/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverSecurityConfig.java b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverSecurityConfig.java
new file mode 100644
index 0000000000..65ee47ecc4
--- /dev/null
+++ b/spring-5-reactive-security/src/main/java/com/baeldung/reactive/authresolver/AuthResolverSecurityConfig.java
@@ -0,0 +1,86 @@
+package com.baeldung.reactive.authresolver;
+
+import java.util.Collections;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.security.authentication.ReactiveAuthenticationManager;
+import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
+import reactor.core.publisher.Mono;
+
+@EnableWebFluxSecurity
+@EnableReactiveMethodSecurity
+public class AuthResolverSecurityConfig {
+
+ @Bean
+ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
+ return http
+ .authorizeExchange()
+ .pathMatchers("/**")
+ .authenticated()
+ .and()
+ .httpBasic()
+ .disable()
+ .addFilterAfter(authenticationWebFilter(), SecurityWebFiltersOrder.REACTOR_CONTEXT)
+ .build();
+ }
+
+ public AuthenticationWebFilter authenticationWebFilter() {
+ AuthenticationWebFilter filter = new AuthenticationWebFilter(authenticationManagerResolver());
+ return filter;
+ }
+
+ public ReactiveAuthenticationManagerResolver authenticationManagerResolver() {
+ return request -> {
+ if (request
+ .getPath()
+ .subPath(0)
+ .value()
+ .startsWith("/employee")) return Mono.just(employeesAuthenticationManager());
+ return Mono.just(customersAuthenticationManager());
+ };
+ }
+
+ public ReactiveAuthenticationManager customersAuthenticationManager() {
+ return authentication -> customer(authentication)
+ .switchIfEmpty(Mono.error(new UsernameNotFoundException(authentication
+ .getPrincipal()
+ .toString())))
+ .map(b -> new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))));
+ }
+
+ public ReactiveAuthenticationManager employeesAuthenticationManager() {
+ return authentication -> employee(authentication)
+ .switchIfEmpty(Mono.error(new UsernameNotFoundException(authentication
+ .getPrincipal()
+ .toString())))
+ .map(b -> new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))));
+ }
+
+ public Mono customer(Authentication authentication) {
+ return Mono.justOrEmpty(authentication
+ .getPrincipal()
+ .toString()
+ .startsWith("customer") ? authentication
+ .getPrincipal()
+ .toString() : null);
+ }
+
+ public Mono employee(Authentication authentication) {
+ return Mono.justOrEmpty(authentication
+ .getPrincipal()
+ .toString()
+ .startsWith("employee") ? authentication
+ .getPrincipal()
+ .toString() : null);
+ }
+}
diff --git a/spring-5-reactive-security/src/test/java/com/baeldung/reactive/authresolver/AuthResolverIntegrationTest.java b/spring-5-reactive-security/src/test/java/com/baeldung/reactive/authresolver/AuthResolverIntegrationTest.java
new file mode 100644
index 0000000000..21c25a6111
--- /dev/null
+++ b/spring-5-reactive-security/src/test/java/com/baeldung/reactive/authresolver/AuthResolverIntegrationTest.java
@@ -0,0 +1,62 @@
+package com.baeldung.reactive.authresolver;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.util.Base64Utils;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AuthResolverApplication.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AuthResolverIntegrationTest {
+ @Autowired private WebTestClient testClient;
+
+ @Test
+ public void givenCustomerCredential_whenWelcomeCustomer_thenExpectOk() {
+ testClient
+ .get()
+ .uri("/customer/welcome")
+ .header("Authorization", "Basic " + Base64Utils.encodeToString("customer1:pass1".getBytes()))
+ .exchange()
+ .expectStatus()
+ .isOk();
+ }
+
+ @Test
+ public void givenEmployeeCredential_whenWelcomeCustomer_thenExpect401Status() {
+ testClient
+ .get()
+ .uri("/customer/welcome")
+ .header("Authorization", "Basic " + Base64Utils.encodeToString("employee1:pass1".getBytes()))
+ .exchange()
+ .expectStatus()
+ .isUnauthorized();
+ }
+
+ @Test
+ public void givenEmployeeCredential_whenWelcomeEmployee_thenExpectOk() {
+ testClient
+ .get()
+ .uri("/employee/welcome")
+ .header("Authorization", "Basic " + Base64Utils.encodeToString("employee1:pass1".getBytes()))
+ .exchange()
+ .expectStatus()
+ .isOk();
+ }
+
+ @Test
+ public void givenCustomerCredential_whenWelcomeEmployee_thenExpect401Status() {
+ testClient
+ .get()
+ .uri("/employee/welcome")
+ .header("Authorization", "Basic " + Base64Utils.encodeToString("customer1:pass1".getBytes()))
+ .exchange()
+ .expectStatus()
+ .isUnauthorized();
+ }
+}
diff --git a/spring-5-security/pom.xml b/spring-5-security/pom.xml
index 413337633f..943bfbdc1f 100644
--- a/spring-5-security/pom.xml
+++ b/spring-5-security/pom.xml
@@ -1,5 +1,5 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
com.baeldung
spring-5-security
@@ -60,5 +60,8 @@
+
+ 2.2.1.RELEASE
+
diff --git a/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverApplication.java b/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverApplication.java
new file mode 100644
index 0000000000..96ee674b15
--- /dev/null
+++ b/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverApplication.java
@@ -0,0 +1,11 @@
+package com.baeldung.authresolver;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AuthResolverApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(AuthResolverApplication.class, args);
+ }
+}
diff --git a/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverController.java b/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverController.java
new file mode 100644
index 0000000000..7dc6900b5a
--- /dev/null
+++ b/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverController.java
@@ -0,0 +1,18 @@
+package com.baeldung.authresolver;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class AuthResolverController {
+ @GetMapping("/customer/welcome")
+ public String sayWelcomeToCustomer(Authentication authentication) {
+ return String.format("Welcome to our site, %s!", authentication.getPrincipal());
+ }
+
+ @GetMapping("/employee/welcome")
+ public String sayWelcomeToEmployee(Authentication authentication) {
+ return String.format("Welcome to our company, %s!", authentication.getPrincipal());
+ }
+}
diff --git a/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverWebSecurityConfigurer.java b/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverWebSecurityConfigurer.java
new file mode 100644
index 0000000000..6f8931eb7a
--- /dev/null
+++ b/spring-5-security/src/main/java/com/baeldung/authresolver/AuthResolverWebSecurityConfigurer.java
@@ -0,0 +1,92 @@
+package com.baeldung.authresolver;
+
+import java.util.Arrays;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationManagerResolver;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.web.authentication.AuthenticationConverter;
+import org.springframework.security.web.authentication.AuthenticationFilter;
+import org.springframework.security.web.authentication.www.BasicAuthenticationConverter;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+
+@Configuration
+public class AuthResolverWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
+
+ public AuthenticationConverter authenticationConverter() {
+ return new BasicAuthenticationConverter();
+ }
+
+ public AuthenticationManagerResolver authenticationManagerResolver() {
+ return request -> {
+ if (request
+ .getPathInfo()
+ .startsWith("/employee")) return employeesAuthenticationManager();
+ return customersAuthenticationManager();
+ };
+ }
+
+ public AuthenticationManager customersAuthenticationManager() {
+ return authentication -> {
+ if (isCustomer(authentication)) {
+ return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
+ }
+ throw new UsernameNotFoundException(authentication
+ .getPrincipal()
+ .toString());
+ };
+ }
+
+ public boolean isCustomer(Authentication authentication) {
+ return (authentication
+ .getPrincipal()
+ .toString()
+ .startsWith("customer"));
+ }
+
+ public boolean isEmployee(Authentication authentication) {
+ return (authentication
+ .getPrincipal()
+ .toString()
+ .startsWith("employee"));
+ }
+
+ public AuthenticationFilter authenticationFilter(AuthenticationManagerResolver resolver, AuthenticationConverter converter) {
+ AuthenticationFilter ret = new AuthenticationFilter(resolver, converter);
+ ret.setSuccessHandler((httpServletRequest, httpServletResponse, authentication) -> {
+ });
+ return ret;
+ }
+
+ public AuthenticationManager employeesAuthenticationManager() {
+ return authentication -> {
+ if (isEmployee(authentication)) {
+ return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
+ }
+ throw new UsernameNotFoundException(authentication
+ .getPrincipal()
+ .toString());
+ };
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .addFilterBefore(
+ authenticationFilter(
+ authenticationManagerResolver(), authenticationConverter()),
+ BasicAuthenticationFilter.class);
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ super.configure(auth);
+ }
+}
diff --git a/spring-5-security/src/test/java/com/baeldung/authresolver/AuthResolverIntegrationTest.java b/spring-5-security/src/test/java/com/baeldung/authresolver/AuthResolverIntegrationTest.java
new file mode 100644
index 0000000000..0b0289e9e5
--- /dev/null
+++ b/spring-5-security/src/test/java/com/baeldung/authresolver/AuthResolverIntegrationTest.java
@@ -0,0 +1,80 @@
+package com.baeldung.authresolver;
+
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.web.FilterChainProxy;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.util.Base64Utils;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AuthResolverApplication.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AuthResolverIntegrationTest {
+
+ @Autowired private FilterChainProxy springSecurityFilterChain;
+
+ @Autowired private WebApplicationContext wac;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setup() {
+ this.mockMvc = MockMvcBuilders
+ .webAppContextSetup(wac)
+ .apply(springSecurity(springSecurityFilterChain))
+ .build();
+ }
+
+ @Test
+ public void givenCustomerCredential_whenWelcomeCustomer_thenExpectOk() throws Exception {
+ this.mockMvc
+ .perform(get("/customer/welcome")
+ .header(
+ "Authorization", String.format("Basic %s", Base64Utils.encodeToString("customer1:pass1".getBytes()))
+ )
+ )
+ .andExpect(status().is2xxSuccessful());
+ }
+
+ @Test
+ public void givenEmployeeCredential_whenWelcomeCustomer_thenExpect401Status() throws Exception {
+ this.mockMvc
+ .perform(get("/customer/welcome")
+ .header(
+ "Authorization", "Basic " + Base64Utils.encodeToString("employee1:pass1".getBytes()))
+ )
+ .andExpect(status().isUnauthorized());
+ }
+
+ @Test
+ public void givenEmployeeCredential_whenWelcomeEmployee_thenExpectOk() throws Exception {
+ this.mockMvc
+ .perform(get("/employee/welcome")
+ .header(
+ "Authorization", "Basic " + Base64Utils.encodeToString("employee1:pass1".getBytes()))
+ )
+ .andExpect(status().is2xxSuccessful());
+ }
+
+ @Test
+ public void givenCustomerCredential_whenWelcomeEmployee_thenExpect401Status() throws Exception {
+ this.mockMvc
+ .perform(get("/employee/welcome")
+ .header(
+ "Authorization", "Basic " + Base64Utils.encodeToString("customer1:pass1".getBytes()))
+ )
+ .andExpect(status().isUnauthorized());
+ }
+}