Skip to content

Commit 78dee30

Browse files
committed
Merge pull request #11055 from vpavic/session-webflux-sample
* pr/11055: Polish Spring Session WebFlux sample Add Spring Session WebFlux sample
2 parents 31025d9 + 4412285 commit 78dee30

File tree

6 files changed

+275
-0
lines changed

6 files changed

+275
-0
lines changed

spring-boot-samples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
<module>spring-boot-sample-secure-webflux</module>
6868
<module>spring-boot-sample-servlet</module>
6969
<module>spring-boot-sample-session</module>
70+
<module>spring-boot-sample-session-webflux</module>
7071
<module>spring-boot-sample-simple</module>
7172
<module>spring-boot-sample-test</module>
7273
<module>spring-boot-sample-test-nomockito</module>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
= Spring Boot Spring Session Sample
2+
3+
This sample demonstrates the Spring Session WebFlux auto-configuration support. Spring
4+
Session supports multiple reactive session store types, including:
5+
6+
* `Redis`
7+
* `MongoDB`
8+
9+
10+
11+
== Using a different session store
12+
Initially, the project uses MongoDB session store backed by an embedded MongoDB. You can
13+
try out your favorite session store as explained below.
14+
15+
16+
17+
=== Redis
18+
Add `org.springframework.session:spring-session-data-redis` and
19+
`spring-boot-starter-data-redis-reactive` dependencies to the project and make sure it is
20+
configured properly (by default, a Redis instance with the default settings is expected
21+
on your local box).
22+
23+
TIP: Run sample application using Redis session store using
24+
`$mvn spring-boot:run -Predis`.
25+
26+
27+
28+
=== MongoDB
29+
Add `org.springframework.session:spring-session-data-mongodb` and
30+
`spring-boot-starter-data-mongodb-reactive` and
31+
`de.flapdoodle.embed:de.flapdoodle.embed.mongo` dependencies to the project. An embedded
32+
MongoDB is automatically configured.
33+
34+
TIP: Run sample application using MongoDB session store using
35+
`$mvn spring-boot:run -Pmongodb`.
36+
37+
Note that this profile is active by default.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<!-- Your own application should inherit from spring-boot-starter-parent -->
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-samples</artifactId>
8+
<version>${revision}</version>
9+
</parent>
10+
<artifactId>spring-boot-sample-session-webflux</artifactId>
11+
<name>Spring Boot Session WebFlux Sample</name>
12+
<description>Spring Boot Session WebFlux Sample</description>
13+
<properties>
14+
<main.basedir>${basedir}/../..</main.basedir>
15+
</properties>
16+
<dependencies>
17+
<!-- Compile -->
18+
<dependency>
19+
<groupId>org.springframework.boot</groupId>
20+
<artifactId>spring-boot-starter-webflux</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-security</artifactId>
25+
</dependency>
26+
<!-- Test -->
27+
<dependency>
28+
<groupId>org.springframework.boot</groupId>
29+
<artifactId>spring-boot-starter-test</artifactId>
30+
<scope>test</scope>
31+
</dependency>
32+
</dependencies>
33+
<build>
34+
<plugins>
35+
<plugin>
36+
<groupId>org.springframework.boot</groupId>
37+
<artifactId>spring-boot-maven-plugin</artifactId>
38+
</plugin>
39+
</plugins>
40+
</build>
41+
<profiles>
42+
<profile>
43+
<id>redis</id>
44+
<dependencies>
45+
<dependency>
46+
<groupId>org.springframework.session</groupId>
47+
<artifactId>spring-session-data-redis</artifactId>
48+
</dependency>
49+
<dependency>
50+
<groupId>org.springframework.boot</groupId>
51+
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
52+
</dependency>
53+
</dependencies>
54+
</profile>
55+
<profile>
56+
<id>mongodb</id>
57+
<activation>
58+
<activeByDefault>true</activeByDefault>
59+
</activation>
60+
<dependencies>
61+
<dependency>
62+
<groupId>org.springframework.session</groupId>
63+
<artifactId>spring-session-data-mongodb</artifactId>
64+
</dependency>
65+
<dependency>
66+
<groupId>org.springframework.boot</groupId>
67+
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
68+
</dependency>
69+
<dependency>
70+
<groupId>de.flapdoodle.embed</groupId>
71+
<artifactId>de.flapdoodle.embed.mongo</artifactId>
72+
</dependency>
73+
</dependencies>
74+
</profile>
75+
</profiles>
76+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample.session;
18+
19+
import org.springframework.web.bind.annotation.GetMapping;
20+
import org.springframework.web.bind.annotation.RestController;
21+
import org.springframework.web.server.WebSession;
22+
23+
@RestController
24+
public class HelloRestController {
25+
26+
@GetMapping("/")
27+
String sessionId(WebSession session) {
28+
return session.getId();
29+
}
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample.session;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
import org.springframework.context.annotation.Bean;
22+
import org.springframework.security.config.web.server.ServerHttpSecurity;
23+
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
24+
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
25+
import org.springframework.security.core.userdetails.User;
26+
import org.springframework.security.web.server.SecurityWebFilterChain;
27+
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
28+
29+
@SpringBootApplication
30+
public class SampleSessionWebFluxApplication {
31+
32+
public static void main(String[] args) {
33+
SpringApplication.run(SampleSessionWebFluxApplication.class);
34+
}
35+
36+
@Bean
37+
public ReactiveUserDetailsService userDetailsRepository() {
38+
return new MapReactiveUserDetailsService(User.withDefaultPasswordEncoder()
39+
.username("user").password("password").roles("USER").build());
40+
}
41+
42+
@Bean
43+
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
44+
// @formatter:off
45+
return http
46+
.authorizeExchange()
47+
.anyExchange().authenticated()
48+
.and()
49+
.httpBasic().securityContextRepository(new WebSessionServerSecurityContextRepository())
50+
.and()
51+
.formLogin()
52+
.and()
53+
.build();
54+
// @formatter:on
55+
}
56+
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample.session;
18+
19+
import java.util.Base64;
20+
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
24+
import org.springframework.beans.factory.annotation.Autowired;
25+
import org.springframework.boot.test.context.SpringBootTest;
26+
import org.springframework.boot.web.server.LocalServerPort;
27+
import org.springframework.http.HttpStatus;
28+
import org.springframework.http.ResponseCookie;
29+
import org.springframework.test.context.junit4.SpringRunner;
30+
import org.springframework.web.reactive.function.client.ClientResponse;
31+
import org.springframework.web.reactive.function.client.WebClient;
32+
33+
import static org.assertj.core.api.Assertions.assertThat;
34+
35+
/**
36+
* Integration tests for {@link SampleSessionWebFluxApplication}.
37+
*
38+
* @author Vedran Pavic
39+
*/
40+
@RunWith(SpringRunner.class)
41+
@SpringBootTest(properties = "server.session.timeout:1", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
42+
public class SampleSessionWebFluxApplicationTests {
43+
44+
@LocalServerPort
45+
private int port;
46+
47+
@Autowired
48+
private WebClient.Builder webClientBuilder;
49+
50+
@Test
51+
public void userDefinedMappingsSecureByDefault() throws Exception {
52+
WebClient webClient = this.webClientBuilder
53+
.baseUrl("http://localhost:" + this.port + "/").build();
54+
ClientResponse response = webClient.get().header("Authorization", getBasicAuth())
55+
.exchange().block();
56+
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
57+
ResponseCookie sessionCookie = response.cookies().getFirst("SESSION");
58+
String sessionId = response.bodyToMono(String.class).block();
59+
response = webClient.get().cookie("SESSION", sessionCookie.getValue()).exchange()
60+
.block();
61+
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
62+
assertThat(response.bodyToMono(String.class).block()).isEqualTo(sessionId);
63+
Thread.sleep(1000);
64+
response = webClient.get().cookie("SESSION", sessionCookie.getValue()).exchange()
65+
.block();
66+
assertThat(response.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
67+
}
68+
69+
private String getBasicAuth() {
70+
return "Basic " + Base64.getEncoder().encodeToString("user:password".getBytes());
71+
}
72+
73+
}

0 commit comments

Comments
 (0)