Skip to content

Commit 8dcc6cb

Browse files
forketyforkpivovarit
authored andcommitted
BAEL-1311: Spring Security 5 - Security for Reactive Applications (eugenp#3056)
1 parent f02ffe9 commit 8dcc6cb

File tree

8 files changed

+184
-6
lines changed

8 files changed

+184
-6
lines changed

spring-5/pom.xml

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<groupId>org.springframework.boot</groupId>
1616
<artifactId>spring-boot-starter-parent</artifactId>
17-
<version>2.0.0.M3</version>
17+
<version>2.0.0.M6</version>
1818
<relativePath /> <!-- lookup parent from repository -->
1919
</parent>
2020

@@ -99,6 +99,11 @@
9999
<artifactId>spring-boot-starter-test</artifactId>
100100
<scope>test</scope>
101101
</dependency>
102+
<dependency>
103+
<groupId>org.springframework.security</groupId>
104+
<artifactId>spring-security-test</artifactId>
105+
<scope>test</scope>
106+
</dependency>
102107

103108
<dependency>
104109
<groupId>org.apache.commons</groupId>
@@ -189,7 +194,7 @@
189194
<junit.platform.version>1.0.0</junit.platform.version>
190195
<junit.jupiter.version>5.0.0</junit.jupiter.version>
191196
<maven-surefire-plugin.version>2.20</maven-surefire-plugin.version>
192-
<spring.version>5.0.0.RELEASE</spring.version>
197+
<spring.version>5.0.1.RELEASE</spring.version>
193198
<reactor-spring.version>1.0.1.RELEASE</reactor-spring.version>
194199
<johnzon.version>1.1.3</johnzon.version>
195200
<jsonb-api.version>1.0</jsonb-api.version>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.baeldung;
2+
3+
import org.springframework.context.ApplicationContext;
4+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.ComponentScan;
7+
import org.springframework.http.server.reactive.HttpHandler;
8+
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
9+
import org.springframework.web.reactive.config.EnableWebFlux;
10+
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
11+
import reactor.ipc.netty.NettyContext;
12+
import reactor.ipc.netty.http.server.HttpServer;
13+
14+
@ComponentScan(basePackages = {"com.baeldung.security"})
15+
@EnableWebFlux
16+
public class SpringSecurity5Application {
17+
18+
public static void main(String[] args) {
19+
try (AnnotationConfigApplicationContext context =
20+
new AnnotationConfigApplicationContext(SpringSecurity5Application.class)) {
21+
context.getBean(NettyContext.class).onClose().block();
22+
}
23+
}
24+
25+
@Bean
26+
public NettyContext nettyContext(ApplicationContext context) {
27+
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context)
28+
.build();
29+
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
30+
HttpServer httpServer = HttpServer.create("localhost", 8080);
31+
return httpServer.newHandler(adapter).block();
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.baeldung.security;
2+
3+
import org.springframework.web.bind.annotation.GetMapping;
4+
import org.springframework.web.bind.annotation.RestController;
5+
import reactor.core.publisher.Mono;
6+
7+
import java.security.Principal;
8+
9+
@RestController
10+
public class GreetController {
11+
12+
private GreetService greetService;
13+
14+
public GreetController(GreetService greetService) {
15+
this.greetService = greetService;
16+
}
17+
18+
@GetMapping("/")
19+
public Mono<String> greet(Mono<Principal> principal) {
20+
return principal
21+
.map(Principal::getName)
22+
.map(name -> String.format("Hello, %s", name));
23+
}
24+
25+
@GetMapping("/admin")
26+
public Mono<String> greetAdmin(Mono<Principal> principal) {
27+
return principal
28+
.map(Principal::getName)
29+
.map(name -> String.format("Admin access: %s", name));
30+
}
31+
32+
@GetMapping("/greetService")
33+
public Mono<String> greetService() {
34+
return greetService.greet();
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.baeldung.security;
2+
3+
import org.springframework.security.access.prepost.PreAuthorize;
4+
import org.springframework.stereotype.Service;
5+
import reactor.core.publisher.Mono;
6+
7+
@Service
8+
public class GreetService {
9+
10+
@PreAuthorize("hasRole('ADMIN')")
11+
public Mono<String> greet() {
12+
return Mono.just("Hello from service!");
13+
}
14+
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.baeldung.security;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
5+
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
6+
import org.springframework.security.config.web.server.ServerHttpSecurity;
7+
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
8+
import org.springframework.security.core.userdetails.User;
9+
import org.springframework.security.core.userdetails.UserDetails;
10+
import org.springframework.security.web.server.SecurityWebFilterChain;
11+
12+
@EnableWebFluxSecurity
13+
@EnableReactiveMethodSecurity
14+
public class SecurityConfig {
15+
16+
@Bean
17+
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
18+
return http.authorizeExchange()
19+
.pathMatchers("/admin").hasAuthority("ROLE_ADMIN")
20+
.anyExchange().authenticated()
21+
.and().formLogin()
22+
.and().build();
23+
}
24+
25+
@Bean
26+
public MapReactiveUserDetailsService userDetailsService() {
27+
UserDetails user = User.withDefaultPasswordEncoder()
28+
.username("user")
29+
.password("password")
30+
.roles("USER")
31+
.build();
32+
33+
UserDetails admin = User.withDefaultPasswordEncoder()
34+
.username("admin")
35+
.password("password")
36+
.roles("ADMIN")
37+
.build();
38+
39+
return new MapReactiveUserDetailsService(user, admin);
40+
}
41+
42+
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
server.port=8081
22

3-
security.user.name=user
4-
security.user.password=pass
5-
63
logging.level.root=INFO
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.baeldung.security;
2+
3+
import com.baeldung.SpringSecurity5Application;
4+
import org.junit.Before;
5+
import org.junit.Test;
6+
import org.junit.runner.RunWith;
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.context.ApplicationContext;
9+
import org.springframework.security.test.context.support.WithMockUser;
10+
import org.springframework.test.context.ContextConfiguration;
11+
import org.springframework.test.context.junit4.SpringRunner;
12+
import org.springframework.test.web.reactive.server.WebTestClient;
13+
14+
@RunWith(SpringRunner.class)
15+
@ContextConfiguration(classes = SpringSecurity5Application.class)
16+
public class SecurityTest {
17+
18+
@Autowired
19+
ApplicationContext context;
20+
21+
private WebTestClient rest;
22+
23+
@Before
24+
public void setup() {
25+
this.rest = WebTestClient
26+
.bindToApplicationContext(this.context)
27+
.configureClient()
28+
.build();
29+
}
30+
31+
@Test
32+
public void whenNoCredentials_thenRedirectToLogin() {
33+
this.rest.get()
34+
.uri("/")
35+
.exchange()
36+
.expectStatus().is3xxRedirection();
37+
}
38+
39+
@Test
40+
@WithMockUser
41+
public void whenHasCredentials_thenSeesGreeting() {
42+
this.rest.get()
43+
.uri("/")
44+
.exchange()
45+
.expectStatus().isOk()
46+
.expectBody(String.class).isEqualTo("Hello, user");
47+
}
48+
}

spring-5/src/test/java/com/baeldung/web/client/WebTestClientTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public void testWebTestClientWithServerURL() {
5353
.uri("/resource")
5454
.exchange()
5555
.expectStatus()
56-
.is4xxClientError()
56+
.is3xxRedirection()
5757
.expectBody();
5858
}
5959

0 commit comments

Comments
 (0)