diff --git a/spring-5/pom.xml b/spring-5/pom.xml index e9a65232d2..7e30179f07 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.M3 + 2.0.0.M6 @@ -99,6 +99,11 @@ spring-boot-starter-test test + + org.springframework.security + spring-security-test + test + org.apache.commons @@ -189,7 +194,7 @@ 1.0.0 5.0.0 2.20 - 5.0.0.RELEASE + 5.0.1.RELEASE 1.0.1.RELEASE 1.1.3 1.0 diff --git a/spring-5/src/main/java/com/baeldung/SpringSecurity5Application.java b/spring-5/src/main/java/com/baeldung/SpringSecurity5Application.java new file mode 100644 index 0000000000..02c91a1879 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/SpringSecurity5Application.java @@ -0,0 +1,34 @@ +package com.baeldung; + +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.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(); + } + +} diff --git a/spring-5/src/main/java/com/baeldung/security/GreetController.java b/spring-5/src/main/java/com/baeldung/security/GreetController.java new file mode 100644 index 0000000000..6b69e3bc9b --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/security/GreetController.java @@ -0,0 +1,37 @@ +package com.baeldung.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 greet(Mono principal) { + return principal + .map(Principal::getName) + .map(name -> String.format("Hello, %s", name)); + } + + @GetMapping("/admin") + public Mono greetAdmin(Mono principal) { + return principal + .map(Principal::getName) + .map(name -> String.format("Admin access: %s", name)); + } + + @GetMapping("/greetService") + public Mono greetService() { + return greetService.greet(); + } + +} diff --git a/spring-5/src/main/java/com/baeldung/security/GreetService.java b/spring-5/src/main/java/com/baeldung/security/GreetService.java new file mode 100644 index 0000000000..7622b360be --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/security/GreetService.java @@ -0,0 +1,15 @@ +package com.baeldung.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 greet() { + return Mono.just("Hello from service!"); + } + +} diff --git a/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java b/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java new file mode 100644 index 0000000000..a9e44a2eee --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java @@ -0,0 +1,42 @@ +package com.baeldung.security; + +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.web.server.SecurityWebFilterChain; + +@EnableWebFluxSecurity +@EnableReactiveMethodSecurity +public class SecurityConfig { + + @Bean + public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) { + return http.authorizeExchange() + .pathMatchers("/admin").hasAuthority("ROLE_ADMIN") + .anyExchange().authenticated() + .and().formLogin() + .and().build(); + } + + @Bean + public MapReactiveUserDetailsService userDetailsService() { + UserDetails user = User.withDefaultPasswordEncoder() + .username("user") + .password("password") + .roles("USER") + .build(); + + UserDetails admin = User.withDefaultPasswordEncoder() + .username("admin") + .password("password") + .roles("ADMIN") + .build(); + + return new MapReactiveUserDetailsService(user, admin); + } + +} diff --git a/spring-5/src/main/resources/application.properties b/spring-5/src/main/resources/application.properties index 886ea1978b..ccec014c2b 100644 --- a/spring-5/src/main/resources/application.properties +++ b/spring-5/src/main/resources/application.properties @@ -1,6 +1,3 @@ server.port=8081 -security.user.name=user -security.user.password=pass - logging.level.root=INFO \ No newline at end of file diff --git a/spring-5/src/test/java/com/baeldung/security/SecurityTest.java b/spring-5/src/test/java/com/baeldung/security/SecurityTest.java new file mode 100644 index 0000000000..a1c940a17a --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/security/SecurityTest.java @@ -0,0 +1,48 @@ +package com.baeldung.security; + +import com.baeldung.SpringSecurity5Application; +import org.junit.Before; +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; + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = SpringSecurity5Application.class) +public class SecurityTest { + + @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 + @WithMockUser + public void whenHasCredentials_thenSeesGreeting() { + this.rest.get() + .uri("/") + .exchange() + .expectStatus().isOk() + .expectBody(String.class).isEqualTo("Hello, user"); + } +} diff --git a/spring-5/src/test/java/com/baeldung/web/client/WebTestClientTest.java b/spring-5/src/test/java/com/baeldung/web/client/WebTestClientTest.java index b05f903b4b..43114b5b50 100644 --- a/spring-5/src/test/java/com/baeldung/web/client/WebTestClientTest.java +++ b/spring-5/src/test/java/com/baeldung/web/client/WebTestClientTest.java @@ -53,7 +53,7 @@ public class WebTestClientTest { .uri("/resource") .exchange() .expectStatus() - .is4xxClientError() + .is3xxRedirection() .expectBody(); }