Merge pull request #4999 from lor6/BAEL-2061

Bael 2061
This commit is contained in:
Loredana Crusoveanu 2018-08-19 07:39:08 +03:00 committed by GitHub
commit c1f4486a27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 628 additions and 399 deletions

View File

@ -442,6 +442,7 @@
<module>spring-4</module> <module>spring-4</module>
<module>spring-5</module> <module>spring-5</module>
<module>spring-5-reactive</module> <module>spring-5-reactive</module>
<module>spring-5-reactive-security</module>
<module>spring-5-reactive-client</module> <module>spring-5-reactive-client</module>
<module>spring-5-mvc</module> <module>spring-5-mvc</module>
<module>spring-5-security</module> <module>spring-5-security</module>
@ -699,6 +700,7 @@
<module>spring-4</module> <module>spring-4</module>
<module>spring-5</module> <module>spring-5</module>
<module>spring-5-reactive</module> <module>spring-5-reactive</module>
<module>spring-5-reactive-security</module>
<module>spring-5-reactive-client</module> <module>spring-5-reactive-client</module>
<module>spring-5-mvc</module> <module>spring-5-mvc</module>
<module>spring-5-security</module> <module>spring-5-security</module>
@ -980,6 +982,7 @@
<module>spark-java</module> <module>spark-java</module>
<module>spring-4</module> <module>spring-4</module>
<module>spring-5-reactive</module> <module>spring-5-reactive</module>
<module>spring-5-reactive-security</module>
<module>spring-5-reactive-client</module> <module>spring-5-reactive-client</module>
<module>spring-5-mvc</module> <module>spring-5-mvc</module>
<module>spring-5-security</module> <module>spring-5-security</module>

12
spring-5-reactive-security/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
#folders#
.idea
/target
/neoDb*
/data
/src/main/webapp/WEB-INF/classes
*/META-INF/*
# Packaged files #
*.jar
*.war
*.ear

View File

@ -0,0 +1,11 @@
## Spring 5 Reactive Security Examples
### The Course
The "REST With Spring" Classes: http://bit.ly/restwithspring
### Relevant Articles
- [Spring Boot Actuator](http://www.baeldung.com/spring-boot-actuators)
- [Spring Security 5 for Reactive Applications](http://www.baeldung.com/spring-security-5-reactive)
- [Guide to Spring 5 WebFlux](http://www.baeldung.com/spring-webflux)

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung</groupId>
<artifactId>spring-5-reactive-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-5-reactive-security</name>
<description>spring 5 security sample project about new features</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-boot-2</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectreactor</groupId>
<artifactId>reactor-spring</artifactId>
<version>${reactor-spring.version}</version>
</dependency>
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-json_1.1_spec</artifactId>
<version>${geronimo-json_1.1_spec.version}</version>
</dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
</dependency>
<!-- utils -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- runtime and test scoped -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxjava</artifactId>
<version>${rxjava-version}</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>${project-reactor-test}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.baeldung.webflux.EmployeeSpringApplication</mainClass>
<layout>JAR</layout>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<reactor-spring.version>1.0.1.RELEASE</reactor-spring.version>
<rxjava-version>2.1.12</rxjava-version>
<johnzon.version>1.1.3</johnzon.version>
<jsonb-api.version>1.0</jsonb-api.version>
<geronimo-json_1.1_spec.version>1.0</geronimo-json_1.1_spec.version>
<commons-collections4.version>4.1</commons-collections4.version>
<project-reactor-test>3.1.6.RELEASE</project-reactor-test>
</properties>
</project>

View File

@ -0,0 +1,15 @@
package com.baeldung.reactive.actuator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Spring5ReactiveApplication{
public static void main(String[] args) {
SpringApplication.run(Spring5ReactiveApplication.class, args);
}
}

View File

@ -22,7 +22,7 @@ public class WebSecurityConfig {
.authorizeExchange() .authorizeExchange()
.matchers(EndpointRequest.to( .matchers(EndpointRequest.to(
FeaturesEndpoint.class FeaturesEndpoint.class
)).permitAll().and().csrf().disable().build(); )).permitAll().anyExchange().permitAll().and().csrf().disable().build();
} }
} }

View File

@ -8,6 +8,8 @@ import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain;
import com.baeldung.reactive.actuator.FeaturesEndpoint; import com.baeldung.reactive.actuator.FeaturesEndpoint;
@ -35,19 +37,24 @@ public class SecurityConfig {
@Bean @Bean
public MapReactiveUserDetailsService userDetailsService() { public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder() UserDetails user = User
.username("user") .withUsername("user")
.password("password") .password(passwordEncoder().encode("password"))
.roles("USER") .roles("USER")
.build(); .build();
UserDetails admin = User.withDefaultPasswordEncoder() UserDetails admin = User
.username("admin") .withUsername("admin")
.password("password") .password(passwordEncoder().encode("password"))
.roles("ADMIN") .roles("ADMIN")
.build(); .build();
return new MapReactiveUserDetailsService(user, admin); return new MapReactiveUserDetailsService(user, admin);
} }
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
} }

View File

@ -1,4 +1,4 @@
package com.baeldung.reactive; package com.baeldung.reactive.security;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;

View File

@ -1,17 +1,17 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class Employee { public class Employee {
private String id; private String id;
private String name; private String name;
// standard getters and setters // standard getters and setters
} }

View File

@ -1,33 +1,33 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.HandlerMapping; import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.config.EnableWebFlux; import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler; import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
@Configuration @Configuration
@EnableWebFlux @EnableWebFlux
public class EmployeeConfig { public class EmployeeConfig {
@Bean @Bean
public HandlerMapping handlerMapping() { public HandlerMapping handlerMapping() {
Map<String, WebSocketHandler> map = new HashMap<>(); Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/employee-feed", new EmployeeWebSocketHandler()); map.put("/employee-feed", new EmployeeWebSocketHandler());
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(map); mapping.setUrlMap(map);
mapping.setOrder(10); mapping.setOrder(10);
return mapping; return mapping;
} }
@Bean @Bean
public WebSocketHandlerAdapter handlerAdapter() { public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter(); return new WebSocketHandlerAdapter();
} }
} }

View File

@ -1,38 +1,38 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@RestController @RestController
@RequestMapping("/employees") @RequestMapping("/employees")
public class EmployeeController { public class EmployeeController {
private EmployeeRepository employeeRepository; private EmployeeRepository employeeRepository;
public EmployeeController(EmployeeRepository employeeRepository) { public EmployeeController(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository; this.employeeRepository = employeeRepository;
} }
@GetMapping("/{id}") @GetMapping("/{id}")
private Mono<Employee> getEmployeeById(@PathVariable String id) { private Mono<Employee> getEmployeeById(@PathVariable String id) {
return employeeRepository.findEmployeeById(id); return employeeRepository.findEmployeeById(id);
} }
@GetMapping @GetMapping
private Flux<Employee> getAllEmployees() { private Flux<Employee> getAllEmployees() {
return employeeRepository.findAllEmployees(); return employeeRepository.findAllEmployees();
} }
@PostMapping("/update") @PostMapping("/update")
private Mono<Employee> updateEmployee(@RequestBody Employee employee) { private Mono<Employee> updateEmployee(@RequestBody Employee employee) {
return employeeRepository.updateEmployee(employee); return employeeRepository.updateEmployee(employee);
} }
} }

View File

@ -1,4 +1,4 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View File

@ -1,64 +1,64 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@Repository @Repository
public class EmployeeRepository { public class EmployeeRepository {
static Map<String,Employee> employeeData; static Map<String,Employee> employeeData;
static Map<String,String> employeeAccessData; static Map<String,String> employeeAccessData;
static static
{ {
employeeData = new HashMap<>(); employeeData = new HashMap<>();
employeeData.put("1",new Employee("1","Employee 1")); employeeData.put("1",new Employee("1","Employee 1"));
employeeData.put("2",new Employee("2","Employee 2")); employeeData.put("2",new Employee("2","Employee 2"));
employeeData.put("3",new Employee("3","Employee 3")); employeeData.put("3",new Employee("3","Employee 3"));
employeeData.put("4",new Employee("4","Employee 4")); employeeData.put("4",new Employee("4","Employee 4"));
employeeData.put("5",new Employee("5","Employee 5")); employeeData.put("5",new Employee("5","Employee 5"));
employeeData.put("6",new Employee("6","Employee 6")); employeeData.put("6",new Employee("6","Employee 6"));
employeeData.put("7",new Employee("7","Employee 7")); employeeData.put("7",new Employee("7","Employee 7"));
employeeData.put("8",new Employee("8","Employee 8")); employeeData.put("8",new Employee("8","Employee 8"));
employeeData.put("9",new Employee("9","Employee 9")); employeeData.put("9",new Employee("9","Employee 9"));
employeeData.put("10",new Employee("10","Employee 10")); employeeData.put("10",new Employee("10","Employee 10"));
employeeAccessData=new HashMap<>(); employeeAccessData=new HashMap<>();
employeeAccessData.put("1", "Employee 1 Access Key"); employeeAccessData.put("1", "Employee 1 Access Key");
employeeAccessData.put("2", "Employee 2 Access Key"); employeeAccessData.put("2", "Employee 2 Access Key");
employeeAccessData.put("3", "Employee 3 Access Key"); employeeAccessData.put("3", "Employee 3 Access Key");
employeeAccessData.put("4", "Employee 4 Access Key"); employeeAccessData.put("4", "Employee 4 Access Key");
employeeAccessData.put("5", "Employee 5 Access Key"); employeeAccessData.put("5", "Employee 5 Access Key");
employeeAccessData.put("6", "Employee 6 Access Key"); employeeAccessData.put("6", "Employee 6 Access Key");
employeeAccessData.put("7", "Employee 7 Access Key"); employeeAccessData.put("7", "Employee 7 Access Key");
employeeAccessData.put("8", "Employee 8 Access Key"); employeeAccessData.put("8", "Employee 8 Access Key");
employeeAccessData.put("9", "Employee 9 Access Key"); employeeAccessData.put("9", "Employee 9 Access Key");
employeeAccessData.put("10", "Employee 10 Access Key"); employeeAccessData.put("10", "Employee 10 Access Key");
} }
public Mono<Employee> findEmployeeById(String id) public Mono<Employee> findEmployeeById(String id)
{ {
return Mono.just(employeeData.get(id)); return Mono.just(employeeData.get(id));
} }
public Flux<Employee> findAllEmployees() public Flux<Employee> findAllEmployees()
{ {
return Flux.fromIterable(employeeData.values()); return Flux.fromIterable(employeeData.values());
} }
public Mono<Employee> updateEmployee(Employee employee) public Mono<Employee> updateEmployee(Employee employee)
{ {
Employee existingEmployee=employeeData.get(employee.getId()); Employee existingEmployee=employeeData.get(employee.getId());
if(existingEmployee!=null) if(existingEmployee!=null)
{ {
existingEmployee.setName(employee.getName()); existingEmployee.setName(employee.getName());
} }
return Mono.just(existingEmployee); return Mono.just(existingEmployee);
} }
} }

View File

@ -1,17 +1,17 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
public class EmployeeSpringApplication { public class EmployeeSpringApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(EmployeeSpringApplication.class, args); SpringApplication.run(EmployeeSpringApplication.class, args);
EmployeeWebClient employeeWebClient = new EmployeeWebClient(); EmployeeWebClient employeeWebClient = new EmployeeWebClient();
employeeWebClient.consume(); employeeWebClient.consume();
} }
} }

View File

@ -1,28 +1,28 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
public class EmployeeWebClient { public class EmployeeWebClient {
WebClient client = WebClient.create("http://localhost:8080"); WebClient client = WebClient.create("http://localhost:8080");
public void consume() { public void consume() {
Mono<Employee> employeeMono = client.get() Mono<Employee> employeeMono = client.get()
.uri("/employees/{id}", "1") .uri("/employees/{id}", "1")
.retrieve() .retrieve()
.bodyToMono(Employee.class); .bodyToMono(Employee.class);
employeeMono.subscribe(System.out::println); employeeMono.subscribe(System.out::println);
Flux<Employee> employeeFlux = client.get() Flux<Employee> employeeFlux = client.get()
.uri("/employees") .uri("/employees")
.retrieve() .retrieve()
.bodyToFlux(Employee.class); .bodyToFlux(Employee.class);
employeeFlux.subscribe(System.out::println); employeeFlux.subscribe(System.out::println);
} }
} }

View File

@ -1,38 +1,46 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import org.springframework.context.annotation.Bean; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod; import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.http.HttpMethod;
import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebFluxSecurity import org.springframework.security.crypto.password.PasswordEncoder;
public class EmployeeWebSecurityConfig { import org.springframework.security.web.server.SecurityWebFilterChain;
@Bean @EnableWebFluxSecurity
public MapReactiveUserDetailsService userDetailsService() { public class EmployeeWebSecurityConfig {
UserDetails user = User.withDefaultPasswordEncoder()
.username("admin") @Bean
.password("password") public MapReactiveUserDetailsService userDetailsService() {
.roles("ADMIN") UserDetails user = User
.build(); .withUsername("admin")
return new MapReactiveUserDetailsService(user); .password(passwordEncoder().encode("password"))
} .roles("ADMIN")
.build();
@Bean return new MapReactiveUserDetailsService(user);
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { }
http.csrf()
.disable() @Bean
.authorizeExchange() public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
.pathMatchers(HttpMethod.POST, "/employees/update") http.csrf()
.hasRole("ADMIN") .disable()
.pathMatchers("/**") .authorizeExchange()
.permitAll() .pathMatchers(HttpMethod.POST, "/employees/update")
.and() .hasRole("ADMIN")
.httpBasic(); .pathMatchers("/**")
return http.build(); .permitAll()
} .and()
} .httpBasic();
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -1,4 +1,4 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import java.net.URI; import java.net.URI;

View File

@ -1,39 +1,39 @@
package com.baeldung.reactive.webflux; package com.baeldung.webflux;
import static java.time.LocalDateTime.now; import static java.time.LocalDateTime.now;
import static java.util.UUID.randomUUID; import static java.util.UUID.randomUUID;
import java.time.Duration; import java.time.Duration;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.reactive.socket.WebSocketHandler; import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketSession; import org.springframework.web.reactive.socket.WebSocketSession;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@Component("EmployeeWebSocketHandler") @Component("EmployeeWebSocketHandler")
public class EmployeeWebSocketHandler implements WebSocketHandler { public class EmployeeWebSocketHandler implements WebSocketHandler {
ObjectMapper om = new ObjectMapper(); ObjectMapper om = new ObjectMapper();
@Override @Override
public Mono<Void> handle(WebSocketSession webSocketSession) { public Mono<Void> handle(WebSocketSession webSocketSession) {
Flux<String> employeeCreationEvent = Flux.generate(sink -> { Flux<String> employeeCreationEvent = Flux.generate(sink -> {
EmployeeCreationEvent event = new EmployeeCreationEvent(randomUUID().toString(), now().toString()); EmployeeCreationEvent event = new EmployeeCreationEvent(randomUUID().toString(), now().toString());
try { try {
sink.next(om.writeValueAsString(event)); sink.next(om.writeValueAsString(event));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
sink.error(e); sink.error(e);
} }
}); });
return webSocketSession.send(employeeCreationEvent return webSocketSession.send(employeeCreationEvent
.map(webSocketSession::textMessage) .map(webSocketSession::textMessage)
.delayElements(Duration.ofSeconds(1))); .delayElements(Duration.ofSeconds(1)));
} }
} }

View File

@ -0,0 +1,5 @@
logging.level.root=INFO
management.endpoints.web.exposure.include.=*
info.app.name=Spring Boot 2 actuator Application

View File

@ -0,0 +1 @@
hello

View File

@ -0,0 +1 @@
test

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Baeldung: Spring 5 Reactive Client WebSocket (Browser)</title>
</head>
<body>
<div class="events"></div>
<script>
var clientWebSocket = new WebSocket("ws://localhost:8080/event-emitter");
clientWebSocket.onopen = function() {
console.log("clientWebSocket.onopen", clientWebSocket);
console.log("clientWebSocket.readyState", "websocketstatus");
clientWebSocket.send("event-me-from-browser");
}
clientWebSocket.onclose = function(error) {
console.log("clientWebSocket.onclose", clientWebSocket, error);
events("Closing connection");
}
clientWebSocket.onerror = function(error) {
console.log("clientWebSocket.onerror", clientWebSocket, error);
events("An error occured");
}
clientWebSocket.onmessage = function(error) {
console.log("clientWebSocket.onmessage", clientWebSocket, error);
events(error.data);
}
function events(responseEvent) {
document.querySelector(".events").innerHTML += responseEvent + "<br>";
}
</script>
</body>
</html>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Spring Functional Application</display-name>
<servlet>
<servlet-name>functional</servlet-name>
<servlet-class>com.baeldung.functional.RootServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>functional</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -9,8 +9,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.reactive.Spring5ReactiveApplication;
import java.io.IOException; import java.io.IOException;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View File

@ -1,74 +1,76 @@
package com.baeldung.reactive.webflux; package com.baeldung.reactive.webflux;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.reactive.server.WebTestClient;
import com.baeldung.reactive.Spring5ReactiveApplication; import com.baeldung.reactive.actuator.Spring5ReactiveApplication;
import com.baeldung.webflux.Employee;
import reactor.core.publisher.Flux; import com.baeldung.webflux.EmployeeRepository;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
@RunWith(SpringRunner.class) import reactor.core.publisher.Mono;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes=Spring5ReactiveApplication.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @RunWith(SpringRunner.class)
public class EmployeeControllerIntegrationTest { @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes=Spring5ReactiveApplication.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Autowired public class EmployeeControllerIntegrationTest {
private WebTestClient testClient;
@Autowired
@MockBean private WebTestClient testClient;
private EmployeeRepository employeeRepository;
@MockBean
@Test private EmployeeRepository employeeRepository;
public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() {
@Test
Employee employee = new Employee("1", "Employee 1 Name"); public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() {
given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); Employee employee = new Employee("1", "Employee 1 Name");
testClient.get()
.uri("/employees/1") given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee));
.exchange() testClient.get()
.expectStatus() .uri("/employees/1")
.isOk() .exchange()
.expectBody(Employee.class) .expectStatus()
.isEqualTo(employee); .isOk()
} .expectBody(Employee.class)
.isEqualTo(employee);
@Test }
public void whenGetAllEmployees_thenCorrectEmployees() {
@Test
List<Employee> employeeList = new ArrayList<>(); public void whenGetAllEmployees_thenCorrectEmployees() {
Employee employee1 = new Employee("1", "Employee 1 Name"); List<Employee> employeeList = new ArrayList<>();
Employee employee2 = new Employee("2", "Employee 2 Name");
Employee employee3 = new Employee("3", "Employee 3 Name"); Employee employee1 = new Employee("1", "Employee 1 Name");
Employee employee2 = new Employee("2", "Employee 2 Name");
employeeList.add(employee1); Employee employee3 = new Employee("3", "Employee 3 Name");
employeeList.add(employee2);
employeeList.add(employee3); employeeList.add(employee1);
employeeList.add(employee2);
Flux<Employee> employeeFlux = Flux.fromIterable(employeeList); employeeList.add(employee3);
given(employeeRepository.findAllEmployees()).willReturn(employeeFlux); Flux<Employee> employeeFlux = Flux.fromIterable(employeeList);
testClient.get()
.uri("/employees") given(employeeRepository.findAllEmployees()).willReturn(employeeFlux);
.exchange() testClient.get()
.expectStatus() .uri("/employees")
.isOk() .exchange()
.expectBodyList(Employee.class) .expectStatus()
.hasSize(3) .isOk()
.isEqualTo(employeeList); .expectBodyList(Employee.class)
} .hasSize(3)
} .isEqualTo(employeeList);
}
}

View File

@ -1,6 +1,5 @@
package com.baeldung.security; package com.baeldung.security;
import com.baeldung.reactive.SpringSecurity5Application;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -12,6 +11,8 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.reactive.server.WebTestClient;
import com.baeldung.reactive.security.SpringSecurity5Application;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ContextConfiguration(classes = SpringSecurity5Application.class) @ContextConfiguration(classes = SpringSecurity5Application.class)
public class SecurityIntegrationTest { public class SecurityIntegrationTest {

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,4 +1,4 @@
## Spring REST Example Project ## Spring 5 Reactive Project
### The Course ### The Course
The "REST With Spring" Classes: http://bit.ly/restwithspring The "REST With Spring" Classes: http://bit.ly/restwithspring
@ -7,12 +7,9 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Introduction to the Functional Web Framework in Spring 5](http://www.baeldung.com/spring-5-functional-web) - [Introduction to the Functional Web Framework in Spring 5](http://www.baeldung.com/spring-5-functional-web)
- [Spring 5 WebClient](http://www.baeldung.com/spring-5-webclient) - [Spring 5 WebClient](http://www.baeldung.com/spring-5-webclient)
- [Spring Boot Actuator](http://www.baeldung.com/spring-boot-actuators)
- [Exploring the Spring 5 MVC URL Matching Improvements](http://www.baeldung.com/spring-5-mvc-url-matching) - [Exploring the Spring 5 MVC URL Matching Improvements](http://www.baeldung.com/spring-5-mvc-url-matching)
- [Spring Security 5 for Reactive Applications](http://www.baeldung.com/spring-security-5-reactive)
- [Reactive WebSockets with Spring 5](http://www.baeldung.com/spring-5-reactive-websockets) - [Reactive WebSockets with Spring 5](http://www.baeldung.com/spring-5-reactive-websockets)
- [Spring Webflux Filters](http://www.baeldung.com/spring-webflux-filters) - [Spring Webflux Filters](http://www.baeldung.com/spring-webflux-filters)
- [How to Set a Header on a Response with Spring 5](http://www.baeldung.com/spring-response-header) - [How to Set a Header on a Response with Spring 5](http://www.baeldung.com/spring-response-header)
- [Spring Webflux and CORS](http://www.baeldung.com/spring-webflux-cors) - [Spring Webflux and CORS](http://www.baeldung.com/spring-webflux-cors)
- [Handling Errors in Spring WebFlux](http://www.baeldung.com/spring-webflux-errors) - [Handling Errors in Spring WebFlux](http://www.baeldung.com/spring-webflux-errors)
- [Guide to Spring 5 WebFlux](http://www.baeldung.com/spring-webflux)

View File

@ -39,10 +39,6 @@
<groupId>javax.json.bind</groupId> <groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId> <artifactId>javax.json.bind-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
@ -86,10 +82,6 @@
<version>${commons-collections4.version}</version> <version>${commons-collections4.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.reactivex.rxjava2</groupId> <groupId>io.reactivex.rxjava2</groupId>
@ -102,15 +94,6 @@
<version>${project-reactor-test}</version> <version>${project-reactor-test}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -21,9 +21,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.function.server.ServerResponse;
@ -68,18 +65,6 @@ public class FunctionalSpringBootApplication {
return registrationBean; return registrationBean;
} }
@Configuration
@EnableWebSecurity
@Profile("!https")
static class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.permitAll();
}
}
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(FunctionalSpringBootApplication.class, args); SpringApplication.run(FunctionalSpringBootApplication.class, args);
} }

View File

@ -1,5 +1,2 @@
logging.level.root=INFO logging.level.root=INFO
management.endpoints.web.exposure.include.=*
info.app.name=Spring Boot 2 actuator Application