diff --git a/spring-5-reactive/pom.xml b/spring-5-reactive/pom.xml
index 8b40ccee007e..850c0d574319 100644
--- a/spring-5-reactive/pom.xml
+++ b/spring-5-reactive/pom.xml
@@ -147,7 +147,15 @@
${project-reactor-test}
test
-
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java
new file mode 100644
index 000000000000..6a0355565493
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java
@@ -0,0 +1,17 @@
+package com.baeldung.reactive.webflux;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Employee {
+
+ private String id;
+ private String name;
+
+ // standard getters and setters
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeConfig.java
new file mode 100644
index 000000000000..082be6869851
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeConfig.java
@@ -0,0 +1,33 @@
+package com.baeldung.reactive.webflux;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.HandlerMapping;
+import org.springframework.web.reactive.config.EnableWebFlux;
+import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
+import org.springframework.web.reactive.socket.WebSocketHandler;
+import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
+
+@Configuration
+@EnableWebFlux
+public class EmployeeConfig {
+
+ @Bean
+ public HandlerMapping handlerMapping() {
+ Map map = new HashMap<>();
+ map.put("/employee-feed", new EmployeeWebSocketHandler());
+
+ SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
+ mapping.setUrlMap(map);
+ mapping.setOrder(10);
+ return mapping;
+ }
+
+ @Bean
+ public WebSocketHandlerAdapter handlerAdapter() {
+ return new WebSocketHandlerAdapter();
+ }
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java
new file mode 100644
index 000000000000..98b16dafab43
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java
@@ -0,0 +1,38 @@
+package com.baeldung.reactive.webflux;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@RestController
+@RequestMapping("/employees")
+public class EmployeeController {
+
+ private EmployeeRepository employeeRepository;
+
+ public EmployeeController(EmployeeRepository employeeRepository) {
+ this.employeeRepository = employeeRepository;
+ }
+
+ @GetMapping("/{id}")
+ private Mono getEmployeeById(@PathVariable String id) {
+ return employeeRepository.findEmployeeById(id);
+ }
+
+ @GetMapping
+ private Flux getAllEmployees() {
+ return employeeRepository.findAllEmployees();
+ }
+
+ @PostMapping("/update")
+ private Mono updateEmployee(@RequestBody Employee employee) {
+ return employeeRepository.updateEmployee(employee);
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeCreationEvent.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeCreationEvent.java
new file mode 100644
index 000000000000..7be088f073ea
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeCreationEvent.java
@@ -0,0 +1,11 @@
+package com.baeldung.reactive.webflux;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class EmployeeCreationEvent {
+ private String employeeId;
+ private String creationTime;
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java
new file mode 100644
index 000000000000..a407c76fa8d3
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java
@@ -0,0 +1,64 @@
+package com.baeldung.reactive.webflux;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.stereotype.Repository;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@Repository
+public class EmployeeRepository {
+
+ static Map employeeData;
+
+ static Map employeeAccessData;
+
+ static
+ {
+ employeeData = new HashMap<>();
+ employeeData.put("1",new Employee("1","Employee 1"));
+ employeeData.put("2",new Employee("2","Employee 2"));
+ employeeData.put("3",new Employee("3","Employee 3"));
+ employeeData.put("4",new Employee("4","Employee 4"));
+ employeeData.put("5",new Employee("5","Employee 5"));
+ employeeData.put("6",new Employee("6","Employee 6"));
+ employeeData.put("7",new Employee("7","Employee 7"));
+ employeeData.put("8",new Employee("8","Employee 8"));
+ employeeData.put("9",new Employee("9","Employee 9"));
+ employeeData.put("10",new Employee("10","Employee 10"));
+
+ employeeAccessData=new HashMap<>();
+ employeeAccessData.put("1", "Employee 1 Access Key");
+ employeeAccessData.put("2", "Employee 2 Access Key");
+ employeeAccessData.put("3", "Employee 3 Access Key");
+ employeeAccessData.put("4", "Employee 4 Access Key");
+ employeeAccessData.put("5", "Employee 5 Access Key");
+ employeeAccessData.put("6", "Employee 6 Access Key");
+ employeeAccessData.put("7", "Employee 7 Access Key");
+ employeeAccessData.put("8", "Employee 8 Access Key");
+ employeeAccessData.put("9", "Employee 9 Access Key");
+ employeeAccessData.put("10", "Employee 10 Access Key");
+ }
+
+ public Mono findEmployeeById(String id)
+ {
+ return Mono.just(employeeData.get(id));
+ }
+
+ public Flux findAllEmployees()
+ {
+ return Flux.fromIterable(employeeData.values());
+ }
+
+ public Mono updateEmployee(Employee employee)
+ {
+ Employee existingEmployee=employeeData.get(employee.getId());
+ if(existingEmployee!=null)
+ {
+ existingEmployee.setName(employee.getName());
+ }
+ return Mono.just(existingEmployee);
+ }
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeSpringApplication.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeSpringApplication.java
new file mode 100644
index 000000000000..54b23a18de5a
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeSpringApplication.java
@@ -0,0 +1,17 @@
+package com.baeldung.reactive.webflux;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class EmployeeSpringApplication {
+
+ public static void main(String[] args) {
+
+ SpringApplication.run(EmployeeSpringApplication.class, args);
+
+ EmployeeWebClient employeeWebClient = new EmployeeWebClient();
+ employeeWebClient.consume();
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebClient.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebClient.java
new file mode 100644
index 000000000000..45d42ecda99c
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebClient.java
@@ -0,0 +1,28 @@
+package com.baeldung.reactive.webflux;
+
+import org.springframework.web.reactive.function.client.WebClient;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class EmployeeWebClient {
+
+ WebClient client = WebClient.create("http://localhost:8080");
+
+ public void consume() {
+
+ Mono employeeMono = client.get()
+ .uri("/employees/{id}", "1")
+ .retrieve()
+ .bodyToMono(Employee.class);
+
+ employeeMono.subscribe(System.out::println);
+
+ Flux employeeFlux = client.get()
+ .uri("/employees")
+ .retrieve()
+ .bodyToFlux(Employee.class);
+
+ employeeFlux.subscribe(System.out::println);
+ }
+}
\ No newline at end of file
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java
new file mode 100644
index 000000000000..7922e6ba44f2
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java
@@ -0,0 +1,38 @@
+package com.baeldung.reactive.webflux;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+
+@EnableWebFluxSecurity
+public class EmployeeWebSecurityConfig {
+
+ @Bean
+ public MapReactiveUserDetailsService userDetailsService() {
+ UserDetails user = User.withDefaultPasswordEncoder()
+ .username("admin")
+ .password("password")
+ .roles("ADMIN")
+ .build();
+ return new MapReactiveUserDetailsService(user);
+ }
+
+ @Bean
+ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
+ http.csrf()
+ .disable()
+ .authorizeExchange()
+ .pathMatchers(HttpMethod.POST, "/employees/update")
+ .hasRole("ADMIN")
+ .pathMatchers("/**")
+ .permitAll()
+ .and()
+ .httpBasic();
+ return http.build();
+ }
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java
new file mode 100644
index 000000000000..4571cadc47a0
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java
@@ -0,0 +1,21 @@
+package com.baeldung.reactive.webflux;
+
+import java.net.URI;
+
+import org.springframework.web.reactive.socket.WebSocketMessage;
+import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
+import org.springframework.web.reactive.socket.client.WebSocketClient;
+
+public class EmployeeWebSocketClient {
+
+ public static void main(String[] args) {
+
+ WebSocketClient client = new ReactorNettyWebSocketClient();
+
+ client.execute(URI.create("ws://localhost:8080/employee-feed"), session -> session.receive()
+ .map(WebSocketMessage::getPayloadAsText)
+ .doOnNext(System.out::println)
+ .then())
+ .block(); // to subscribe and return the value
+ }
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java
new file mode 100644
index 000000000000..caa2a38b65cb
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java
@@ -0,0 +1,39 @@
+package com.baeldung.reactive.webflux;
+
+import static java.time.LocalDateTime.now;
+import static java.util.UUID.randomUUID;
+
+import java.time.Duration;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.socket.WebSocketHandler;
+import org.springframework.web.reactive.socket.WebSocketSession;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@Component
+public class EmployeeWebSocketHandler implements WebSocketHandler {
+
+ ObjectMapper om = new ObjectMapper();
+
+ @Override
+ public Mono handle(WebSocketSession webSocketSession) {
+
+ Flux employeeCreationEvent = Flux.generate(sink -> {
+ EmployeeCreationEvent event = new EmployeeCreationEvent(randomUUID().toString(), now().toString());
+ try {
+ sink.next(om.writeValueAsString(event));
+ } catch (JsonProcessingException e) {
+ sink.error(e);
+ }
+ });
+
+ return webSocketSession.send(employeeCreationEvent
+ .map(webSocketSession::textMessage)
+ .delayElements(Duration.ofSeconds(1)));
+ }
+}
diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java
new file mode 100644
index 000000000000..640f28c3319a
--- /dev/null
+++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java
@@ -0,0 +1,72 @@
+package com.baeldung.reactive.webflux;
+
+import static org.mockito.BDDMockito.given;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class EmployeeControllerUnitTest {
+
+ @Autowired
+ private WebTestClient testClient;
+
+ @MockBean
+ private EmployeeRepository employeeRepository;
+
+ @Test
+ public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() {
+
+ Employee employee = new Employee("1", "Employee 1 Name");
+
+ given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee));
+ testClient.get()
+ .uri("/employees/1")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody(Employee.class)
+ .isEqualTo(employee);
+ }
+
+ @Test
+ public void whenGetAllEmployees_thenCorrectEmployees() {
+
+ List employeeList = new ArrayList<>();
+
+ Employee employee1 = new Employee("1", "Employee 1 Name");
+ Employee employee2 = new Employee("2", "Employee 2 Name");
+ Employee employee3 = new Employee("3", "Employee 3 Name");
+
+ employeeList.add(employee1);
+ employeeList.add(employee2);
+ employeeList.add(employee3);
+
+ Flux employeeFlux = Flux.fromIterable(employeeList);
+
+ given(employeeRepository.findAllEmployees()).willReturn(employeeFlux);
+ testClient.get()
+ .uri("/employees")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBodyList(Employee.class)
+ .hasSize(3)
+ .isEqualTo(employeeList);
+ }
+}