move security code
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package com.baeldung.reactive.actuator;
|
||||
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
public class DownstreamServiceReactiveHealthIndicator implements ReactiveHealthIndicator {
|
||||
|
||||
@Override
|
||||
public Mono<Health> health() {
|
||||
return checkDownstreamServiceHealth().onErrorResume(
|
||||
ex -> Mono.just(new Health.Builder().down(ex).build())
|
||||
);
|
||||
}
|
||||
|
||||
private Mono<Health> checkDownstreamServiceHealth() {
|
||||
// we could use WebClient to check health reactively
|
||||
return Mono.just(new Health.Builder().up().build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.baeldung.reactive.actuator;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Component
|
||||
@Endpoint(id = "features", enableByDefault = true)
|
||||
public class FeaturesEndpoint {
|
||||
|
||||
private Map<String, Feature> features = new ConcurrentHashMap<>();
|
||||
|
||||
@ReadOperation
|
||||
public Map<String, Feature> features() {
|
||||
return features;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Feature feature(@Selector String name) {
|
||||
return features.get(name);
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public void configureFeature(@Selector String name, Feature feature) {
|
||||
features.put(name, feature);
|
||||
}
|
||||
|
||||
@DeleteOperation
|
||||
public void deleteFeature(@Selector String name) {
|
||||
features.remove(name);
|
||||
}
|
||||
|
||||
public static class Feature {
|
||||
private Boolean enabled;
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.baeldung.reactive.actuator;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
|
||||
import org.springframework.boot.actuate.info.InfoEndpoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@EndpointWebExtension(endpoint = InfoEndpoint.class)
|
||||
public class InfoWebEndpointExtension {
|
||||
|
||||
private final InfoEndpoint delegate;
|
||||
|
||||
public InfoWebEndpointExtension(InfoEndpoint infoEndpoint) {
|
||||
this.delegate = infoEndpoint;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<Map> info() {
|
||||
Map<String, Object> info = this.delegate.info();
|
||||
Integer status = getStatus(info);
|
||||
return new WebEndpointResponse<>(info, status);
|
||||
}
|
||||
|
||||
private Integer getStatus(Map<String, Object> info) {
|
||||
// return 5xx if this is a snapshot
|
||||
return 200;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.baeldung.reactive.actuator;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.security.reactive.EndpointRequest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
public class WebSecurityConfig {
|
||||
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain securitygWebFilterChain(
|
||||
ServerHttpSecurity http) {
|
||||
return http
|
||||
|
||||
.authorizeExchange()
|
||||
.matchers(EndpointRequest.to(
|
||||
FeaturesEndpoint.class
|
||||
)).permitAll().anyExchange().permitAll().and().csrf().disable().build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.baeldung.reactive.security;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
@RestController
|
||||
public class GreetController {
|
||||
|
||||
private GreetService greetService;
|
||||
|
||||
public GreetController(GreetService greetService) {
|
||||
this.greetService = greetService;
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public Mono<String> greet(Mono<Principal> principal) {
|
||||
return principal
|
||||
.map(Principal::getName)
|
||||
.map(name -> String.format("Hello, %s", name));
|
||||
}
|
||||
|
||||
@GetMapping("/admin")
|
||||
public Mono<String> greetAdmin(Mono<Principal> principal) {
|
||||
return principal
|
||||
.map(Principal::getName)
|
||||
.map(name -> String.format("Admin access: %s", name));
|
||||
}
|
||||
|
||||
@GetMapping("/greetService")
|
||||
public Mono<String> greetService() {
|
||||
return greetService.greet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.baeldung.reactive.security;
|
||||
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Service
|
||||
public class GreetService {
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public Mono<String> greet() {
|
||||
return Mono.just("Hello from service!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.baeldung.reactive.security;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.security.reactive.EndpointRequest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
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.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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
import com.baeldung.reactive.actuator.FeaturesEndpoint;
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
@EnableReactiveMethodSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
|
||||
return http.authorizeExchange()
|
||||
.pathMatchers("/", "/admin")
|
||||
.hasAuthority("ROLE_ADMIN")
|
||||
.matchers(EndpointRequest.to(FeaturesEndpoint.class))
|
||||
.permitAll()
|
||||
.anyExchange()
|
||||
.permitAll()
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.csrf()
|
||||
.disable()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService userDetailsService() {
|
||||
UserDetails user = User
|
||||
.withUsername("user")
|
||||
.password(passwordEncoder().encode("password"))
|
||||
.roles("USER")
|
||||
.build();
|
||||
|
||||
UserDetails admin = User
|
||||
.withUsername("admin")
|
||||
.password(passwordEncoder().encode("password"))
|
||||
.roles("ADMIN")
|
||||
.build();
|
||||
|
||||
return new MapReactiveUserDetailsService(user, admin);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baeldung.reactive.security;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
import reactor.ipc.netty.NettyContext;
|
||||
import reactor.ipc.netty.http.server.HttpServer;
|
||||
|
||||
@ComponentScan(basePackages = {"com.baeldung.reactive.security"})
|
||||
@EnableWebFlux
|
||||
public class SpringSecurity5Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try (AnnotationConfigApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(SpringSecurity5Application.class)) {
|
||||
context.getBean(NettyContext.class).onClose().block();
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NettyContext nettyContext(ApplicationContext context) {
|
||||
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context)
|
||||
.build();
|
||||
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
|
||||
HttpServer httpServer = HttpServer.create("localhost", 8080);
|
||||
return httpServer.newHandler(adapter).block();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.baeldung.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
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.baeldung.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<String, WebSocketHandler> 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.baeldung.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<Employee> getEmployeeById(@PathVariable String id) {
|
||||
return employeeRepository.findEmployeeById(id);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
private Flux<Employee> getAllEmployees() {
|
||||
return employeeRepository.findAllEmployees();
|
||||
}
|
||||
|
||||
@PostMapping("/update")
|
||||
private Mono<Employee> updateEmployee(@RequestBody Employee employee) {
|
||||
return employeeRepository.updateEmployee(employee);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.baeldung.webflux;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class EmployeeCreationEvent {
|
||||
private String employeeId;
|
||||
private String creationTime;
|
||||
public EmployeeCreationEvent(String employeeId, String creationTime) {
|
||||
super();
|
||||
this.employeeId = employeeId;
|
||||
this.creationTime = creationTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.baeldung.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<String,Employee> employeeData;
|
||||
|
||||
static Map<String,String> 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<Employee> findEmployeeById(String id)
|
||||
{
|
||||
return Mono.just(employeeData.get(id));
|
||||
}
|
||||
|
||||
public Flux<Employee> findAllEmployees()
|
||||
{
|
||||
return Flux.fromIterable(employeeData.values());
|
||||
}
|
||||
|
||||
public Mono<Employee> updateEmployee(Employee employee)
|
||||
{
|
||||
Employee existingEmployee=employeeData.get(employee.getId());
|
||||
if(existingEmployee!=null)
|
||||
{
|
||||
existingEmployee.setName(employee.getName());
|
||||
}
|
||||
return Mono.just(existingEmployee);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.baeldung.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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.baeldung.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<Employee> employeeMono = client.get()
|
||||
.uri("/employees/{id}", "1")
|
||||
.retrieve()
|
||||
.bodyToMono(Employee.class);
|
||||
|
||||
employeeMono.subscribe(System.out::println);
|
||||
|
||||
Flux<Employee> employeeFlux = client.get()
|
||||
.uri("/employees")
|
||||
.retrieve()
|
||||
.bodyToFlux(Employee.class);
|
||||
|
||||
employeeFlux.subscribe(System.out::println);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.baeldung.webflux;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
public class EmployeeWebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService userDetailsService() {
|
||||
UserDetails user = User
|
||||
.withUsername("admin")
|
||||
.password(passwordEncoder().encode("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();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.baeldung.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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.baeldung.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("EmployeeWebSocketHandler")
|
||||
public class EmployeeWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession webSocketSession) {
|
||||
|
||||
Flux<String> 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)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
logging.level.root=INFO
|
||||
|
||||
management.endpoints.web.exposure.include.=*
|
||||
|
||||
info.app.name=Spring Boot 2 actuator Application
|
||||
@@ -0,0 +1 @@
|
||||
hello
|
||||
@@ -0,0 +1 @@
|
||||
test
|
||||
13
spring-5-reactive-security/src/main/resources/logback.xml
Normal file
13
spring-5-reactive-security/src/main/resources/logback.xml
Normal 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>
|
||||
@@ -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>
|
||||
21
spring-5-reactive-security/src/main/webapp/WEB-INF/web.xml
Normal file
21
spring-5-reactive-security/src/main/webapp/WEB-INF/web.xml
Normal 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>
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.baeldung.reactive.actuator;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class)
|
||||
public class ActuatorInfoIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Test
|
||||
public void whenGetInfo_thenReturns200() throws IOException {
|
||||
final ResponseEntity<String> responseEntity = this.restTemplate.getForEntity("/actuator/info", String.class);
|
||||
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenFeatures_thenReturns200() throws IOException {
|
||||
final ResponseEntity<String> responseEntity = this.restTemplate.getForEntity("/actuator/features", String.class);
|
||||
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
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 com.baeldung.reactive.actuator.Spring5ReactiveApplication;
|
||||
import com.baeldung.webflux.Employee;
|
||||
import com.baeldung.webflux.EmployeeRepository;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes=Spring5ReactiveApplication.class)
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
public class EmployeeControllerIntegrationTest {
|
||||
|
||||
@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<Employee> 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<Employee> employeeFlux = Flux.fromIterable(employeeList);
|
||||
|
||||
given(employeeRepository.findAllEmployees()).willReturn(employeeFlux);
|
||||
testClient.get()
|
||||
.uri("/employees")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk()
|
||||
.expectBodyList(Employee.class)
|
||||
.hasSize(3)
|
||||
.isEqualTo(employeeList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.baeldung.security;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import com.baeldung.reactive.security.SpringSecurity5Application;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = SpringSecurity5Application.class)
|
||||
public class SecurityIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext context;
|
||||
|
||||
private WebTestClient rest;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.rest = WebTestClient.bindToApplicationContext(this.context).configureClient().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoCredentials_thenRedirectToLogin() {
|
||||
this.rest.get().uri("/").exchange().expectStatus().is3xxRedirection();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
@WithMockUser
|
||||
public void whenHasCredentials_thenSeesGreeting() {
|
||||
this.rest.get().uri("/").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("Hello, user");
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Reference in New Issue
Block a user