Compare commits
238 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77e062b2cd | ||
|
|
2a3b76fee5 | ||
|
|
d5b75228fc | ||
|
|
3d7c668e57 | ||
|
|
e83cf082c4 | ||
|
|
cbd1c66c13 | ||
|
|
9f7a969a6e | ||
|
|
f51310ee64 | ||
|
|
5f2523e211 | ||
|
|
26986a6b7d | ||
|
|
6cdb77378d | ||
|
|
238416ec23 | ||
|
|
4b99428267 | ||
|
|
058e4e46a5 | ||
|
|
fd3609c6f0 | ||
|
|
a0d03adbe1 | ||
|
|
b229103d8c | ||
|
|
94b441c676 | ||
|
|
b2f10c6752 | ||
|
|
b3d228eb2e | ||
|
|
19dd3d8be1 | ||
|
|
1aaffb28fc | ||
|
|
75af61ca6c | ||
|
|
2fff593423 | ||
|
|
6a381d3226 | ||
|
|
cd628fe5af | ||
|
|
ee4df64bb1 | ||
|
|
d850762bce | ||
|
|
f71d1d6ca4 | ||
|
|
e5eeacec5f | ||
|
|
62ec64310b | ||
|
|
25d810eaa3 | ||
|
|
82db55c3f8 | ||
|
|
1f38a937bd | ||
|
|
e027d2091b | ||
|
|
bcbe53c3dd | ||
|
|
72742b52e3 | ||
|
|
1889a4c64e | ||
|
|
3000f3198f | ||
|
|
82a12afe93 | ||
|
|
d2da662c3f | ||
|
|
95e2c9e42a | ||
|
|
ff8672b1d5 | ||
|
|
31d4a766eb | ||
|
|
ce332022de | ||
|
|
5c2ca6d5ac | ||
|
|
bc93d80a17 | ||
|
|
fd835fd316 | ||
|
|
4e3ea616ba | ||
|
|
08654749ae | ||
|
|
88306d037d | ||
|
|
de32e4c501 | ||
|
|
668f85788a | ||
|
|
fc81049cbe | ||
|
|
fbb94b40e8 | ||
|
|
009cb5b592 | ||
|
|
525b841ad6 | ||
|
|
185ea87ff4 | ||
|
|
aebe5ece6f | ||
|
|
30b8e68c67 | ||
|
|
c2f8428df9 | ||
|
|
b5197b8665 | ||
|
|
2406ec8302 | ||
|
|
6d74cf5f35 | ||
|
|
da8e5fbbac | ||
|
|
4b34f35b82 | ||
|
|
954a40f5d1 | ||
|
|
fb66cf3150 | ||
|
|
a23090e7e5 | ||
|
|
beaca53d44 | ||
|
|
baed403a6a | ||
|
|
cd51c36dc0 | ||
|
|
7144e626cc | ||
|
|
0e0d0007e6 | ||
|
|
573424f2a1 | ||
|
|
8e6e9dec53 | ||
|
|
8216be69cc | ||
|
|
0e1914b380 | ||
|
|
ec32a825d9 | ||
|
|
8319435f7c | ||
|
|
45448c3075 | ||
|
|
efd18a9fe9 | ||
|
|
8cf71b27f9 | ||
|
|
0ea5ed709d | ||
|
|
a57774bff6 | ||
|
|
106848fcf7 | ||
|
|
a1b87ce1e9 | ||
|
|
cb75ee603d | ||
|
|
2eb10a6e58 | ||
|
|
5bdf5ed780 | ||
|
|
1ccd1a1eea | ||
|
|
27e90b2e59 | ||
|
|
b8d660712e | ||
|
|
f65f84a129 | ||
|
|
0a0e34e248 | ||
|
|
bb8b9444e6 | ||
|
|
da1fc676ae | ||
|
|
cb6f5c9588 | ||
|
|
4e311939c2 | ||
|
|
e050a92fad | ||
|
|
0c1dbc7355 | ||
|
|
8e8de48614 | ||
|
|
8c49d5993f | ||
|
|
d257f1779e | ||
|
|
a81918116d | ||
|
|
9524730ab1 | ||
|
|
7856113608 | ||
|
|
820c055e7e | ||
|
|
2817d309ac | ||
|
|
2191c6f296 | ||
|
|
87bb3df543 | ||
|
|
38a87e093a | ||
|
|
75ab31d459 | ||
|
|
9041ffb29e | ||
|
|
b20b08e020 | ||
|
|
de57f2cd14 | ||
|
|
b85ec4de1a | ||
|
|
812ac239c0 | ||
|
|
cc4a15db79 | ||
|
|
65b994cad1 | ||
|
|
374ea4a432 | ||
|
|
abce2eb555 | ||
|
|
dc2e93f198 | ||
|
|
00d248b0b5 | ||
|
|
13b0e5cc8a | ||
|
|
dd0edeaef8 | ||
|
|
7fac9efc89 | ||
|
|
8553b52431 | ||
|
|
69d8fda4cc | ||
|
|
a9f7a35ef3 | ||
|
|
679b6e57df | ||
|
|
58813e9d5a | ||
|
|
c66ee750f9 | ||
|
|
9062db3f55 | ||
|
|
4bf15cda3b | ||
|
|
7f8c0387a7 | ||
|
|
3bc15c4259 | ||
|
|
c9add24c77 | ||
|
|
aaed973d27 | ||
|
|
55c4fcfd3f | ||
|
|
2a5d6b4d2e | ||
|
|
42aa10bfe8 | ||
|
|
2ce570cbdc | ||
|
|
221b48094a | ||
|
|
d43b48bbb0 | ||
|
|
840907993b | ||
|
|
e1dea5e0a8 | ||
|
|
4db41c1caf | ||
|
|
23a32acd56 | ||
|
|
dc8cca5351 | ||
|
|
2369b2cfb3 | ||
|
|
e6ce56ec8b | ||
|
|
36939c1b02 | ||
|
|
0a84f9d544 | ||
|
|
f4840e98a2 | ||
|
|
8c99c9f904 | ||
|
|
f70f1f20f9 | ||
|
|
ac1a77e5fe | ||
|
|
214a556dd4 | ||
|
|
cace484fbe | ||
|
|
a5ec1ccf1f | ||
|
|
7fc0ae47d5 | ||
|
|
ce2e644e04 | ||
|
|
24c198fe98 | ||
|
|
8800fb9816 | ||
|
|
b8e94948ae | ||
|
|
8d2276341f | ||
|
|
2c1b79375d | ||
|
|
63f1c7bf6f | ||
|
|
e8e4ee2850 | ||
|
|
9a643c8866 | ||
|
|
282f774e07 | ||
|
|
fba9313c6b | ||
|
|
e6ec5765b8 | ||
|
|
c2288615bf | ||
|
|
ad52fc0297 | ||
|
|
2516a495af | ||
|
|
9be7ac7fa6 | ||
|
|
c335a49924 | ||
|
|
092e6c6607 | ||
|
|
0924c9558a | ||
|
|
0484781541 | ||
|
|
3995f8bf65 | ||
|
|
ad16f17398 | ||
|
|
11aa50e83c | ||
|
|
ab5c727846 | ||
|
|
a0246a61b6 | ||
|
|
8f20fa328a | ||
|
|
44ff959c59 | ||
|
|
ad67a3775b | ||
|
|
75c60b27bd | ||
|
|
12ce8de84e | ||
|
|
81bd6bd261 | ||
|
|
2e8c4292fd | ||
|
|
315b9c9929 | ||
|
|
a70abd90bd | ||
|
|
f13df5aa2f | ||
|
|
6fd68e093f | ||
|
|
653d820290 | ||
|
|
950ac50234 | ||
|
|
bda72c074f | ||
|
|
fb1362aa2c | ||
|
|
d5dac6629d | ||
|
|
04b4b9ba17 | ||
|
|
97308bdbf4 | ||
|
|
4ba62c71dd | ||
|
|
265099c586 | ||
|
|
2f4a0110ab | ||
|
|
7a1cb66dae | ||
|
|
9bad2afa14 | ||
|
|
dba22292a3 | ||
|
|
c79173879f | ||
|
|
2c065c0241 | ||
|
|
a08c721118 | ||
|
|
70e0c6d22d | ||
|
|
61bf2eca49 | ||
|
|
59923121f0 | ||
|
|
b5f8e29585 | ||
|
|
307a1f0dde | ||
|
|
ced2d8421c | ||
|
|
d98ff97e1a | ||
|
|
757175516f | ||
|
|
8ae55b7ee4 | ||
|
|
1eb53ead9d | ||
|
|
72159794f4 | ||
|
|
aaf122f3a6 | ||
|
|
6f823805f2 | ||
|
|
7807aa9f3c | ||
|
|
76924bc923 | ||
|
|
134f89dd41 | ||
|
|
33812f7197 | ||
|
|
bcf17ba3b7 | ||
|
|
98f656ad46 | ||
|
|
7832942752 | ||
|
|
722069a5f8 | ||
|
|
addbdbc1a2 | ||
|
|
004466ed07 | ||
|
|
aeb5bc545c |
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
* text eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.jar binary
|
||||
*.jpg binary
|
||||
*.png binary
|
||||
*.mmdb binary
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,3 +14,5 @@ out
|
||||
!etc/eclipse/.checkstyle
|
||||
!**/src/**/build
|
||||
.DS_Store
|
||||
spring-session-docs/package-lock.json
|
||||
spring-session-docs/node_modules/
|
||||
|
||||
@@ -63,7 +63,7 @@ Compile and test; build all jars, distribution zips, and docs
|
||||
|
||||
You can find the documentation, samples, and guides for using Spring Session on the https://projects.spring.io/spring-session/[Spring Session project site].
|
||||
|
||||
For more in depth information, visit the https://docs.spring.io/spring-session/docs/current/reference/html5/[Spring Session Reference].
|
||||
For more in depth information, visit the https://docs.spring.io/spring-session/reference/[Spring Session Reference].
|
||||
|
||||
== Code of Conduct
|
||||
|
||||
|
||||
@@ -45,4 +45,6 @@ subprojects {
|
||||
|
||||
nohttp {
|
||||
source.exclude "buildSrc/build/**"
|
||||
source.exclude "spring-session-docs/.gradle/nodejs/**"
|
||||
source.exclude "spring-session-docs/modules/ROOT/examples/**/build/**"
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@ dependencies {
|
||||
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
|
||||
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
|
||||
implementation 'com.github.spullara.mustache.java:compiler:0.9.10'
|
||||
implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15'
|
||||
implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.34'
|
||||
implementation 'io.spring.nohttp:nohttp-gradle:0.0.9'
|
||||
implementation 'net.sourceforge.htmlunit:htmlunit:2.37.0'
|
||||
implementation 'org.hidetake:gradle-ssh-plugin:2.10.1'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.24.20'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.29.0'
|
||||
implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.8.1')
|
||||
|
||||
@@ -35,6 +35,7 @@ public abstract class AbstractSpringJavaPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public final void apply(Project project) {
|
||||
initialPlugins(project);
|
||||
PluginManager pluginManager = project.getPluginManager();
|
||||
pluginManager.apply(JavaPlugin.class);
|
||||
pluginManager.apply(ManagementConfigurationPlugin.class)
|
||||
@@ -69,5 +70,7 @@ public abstract class AbstractSpringJavaPlugin implements Plugin<Project> {
|
||||
additionalPlugins(project);
|
||||
}
|
||||
|
||||
protected void initialPlugins(Project project) {}
|
||||
|
||||
protected abstract void additionalPlugins(Project project);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.spring.gradle.convention
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
||||
|
||||
class ArtifactoryPlugin implements Plugin<Project> {
|
||||
|
||||
@@ -37,8 +38,14 @@ class ArtifactoryPlugin implements Plugin<Project> {
|
||||
password = artifactoryPassword
|
||||
}
|
||||
}
|
||||
defaults {
|
||||
publications('mavenJava')
|
||||
}
|
||||
}
|
||||
project.plugins.withType(MavenPublishPlugin) {
|
||||
project.artifactory {
|
||||
publish {
|
||||
defaults {
|
||||
publications('mavenJava')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class RootProjectPlugin implements Plugin<Project> {
|
||||
pluginManager.apply(SchemaPlugin)
|
||||
pluginManager.apply(NoHttpPlugin)
|
||||
pluginManager.apply(SpringNexusPublishPlugin)
|
||||
pluginManager.apply(ArtifactoryPlugin)
|
||||
pluginManager.apply("org.sonarqube")
|
||||
|
||||
project.repositories.mavenCentral()
|
||||
|
||||
@@ -30,4 +30,19 @@ public class SpringSamplePlugin extends AbstractSpringJavaPlugin {
|
||||
project.sonarqube.skipProject = true
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialPlugins(Project project) {
|
||||
if (project.hasProperty('springBootVersion')) {
|
||||
String springBootVersion = project.springBootVersion
|
||||
|
||||
if (Utils.isSnapshot(springBootVersion)) {
|
||||
project.ext.forceMavenRepositories = 'snapshot'
|
||||
}
|
||||
else if (Utils.isMilestone(springBootVersion)) {
|
||||
project.ext.forceMavenRepositories = 'milestone'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,16 +14,29 @@ public class Utils {
|
||||
|
||||
static boolean isSnapshot(Project project) {
|
||||
String projectVersion = projectVersion(project)
|
||||
return projectVersion.matches('^.*([.-]BUILD)?-SNAPSHOT$')
|
||||
return isSnapshot(projectVersion)
|
||||
}
|
||||
|
||||
static boolean isMilestone(Project project) {
|
||||
String projectVersion = projectVersion(project)
|
||||
return projectVersion.matches('^.*[.-]M\\d+$') || projectVersion.matches('^.*[.-]RC\\d+$')
|
||||
return isMilestone(projectVersion)
|
||||
}
|
||||
|
||||
static boolean isRelease(Project project) {
|
||||
return !(isSnapshot(project) || isMilestone(project))
|
||||
String projectVersion = projectVersion(project)
|
||||
return isRelease(projectVersion)
|
||||
}
|
||||
|
||||
static boolean isSnapshot(String projectVersion) {
|
||||
return projectVersion.matches('^.*([.-]BUILD)?-SNAPSHOT$')
|
||||
}
|
||||
|
||||
static boolean isMilestone(String projectVersion) {
|
||||
return projectVersion.matches('^.*[.-]M\\d+$') || projectVersion.matches('^.*[.-]RC\\d+$')
|
||||
}
|
||||
|
||||
static boolean isRelease(String projectVersion) {
|
||||
return !(isSnapshot(projectVersion) || isMilestone(projectVersion))
|
||||
}
|
||||
|
||||
private static String projectVersion(Project project) {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.parallel=true
|
||||
version=3.0.0-M2
|
||||
version=3.0.0
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'io.projectreactor:reactor-bom:2020.0.19'
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.13.3'
|
||||
mavenBom 'org.junit:junit-bom:5.8.2'
|
||||
mavenBom 'org.springframework:spring-framework-bom:6.0.0-M4'
|
||||
mavenBom 'org.springframework.data:spring-data-bom:2022.0.0-M3'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:6.0.0-M5'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.16.2'
|
||||
mavenBom 'io.projectreactor:reactor-bom:2022.0.0'
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.13.4.20221013'
|
||||
mavenBom 'org.junit:junit-bom:5.9.1'
|
||||
mavenBom 'org.mockito:mockito-bom:4.8.1'
|
||||
mavenBom 'org.springframework:spring-framework-bom:6.0.0'
|
||||
mavenBom 'org.springframework.data:spring-data-bom:2022.0.0'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:6.0.0'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.17.3'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependency 'com.hazelcast:hazelcast:5.0.2'
|
||||
dependency 'org.aspectj:aspectjweaver:1.9.7'
|
||||
dependency 'ch.qos.logback:logback-core:1.2.10'
|
||||
dependency 'com.hazelcast:hazelcast:5.1.4'
|
||||
dependency 'org.aspectj:aspectjweaver:1.9.9.1'
|
||||
dependency 'ch.qos.logback:logback-core:1.4.4'
|
||||
dependency 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
dependency 'com.h2database:h2:2.1.212'
|
||||
dependency 'com.ibm.db2:jcc:11.5.6.0'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:9.4.1.jre8'
|
||||
dependency 'com.oracle.database.jdbc:ojdbc8:21.4.0.0.1'
|
||||
dependency 'com.zaxxer:HikariCP:3.4.5'
|
||||
dependency 'com.h2database:h2:2.1.214'
|
||||
dependency 'com.ibm.db2:jcc:11.5.7.0'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:11.2.1.jre17'
|
||||
dependency 'com.oracle.database.jdbc:ojdbc8:21.7.0.0'
|
||||
dependency 'com.zaxxer:HikariCP:5.0.1'
|
||||
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
|
||||
dependency 'io.lettuce:lettuce-core:6.1.6.RELEASE'
|
||||
dependency 'jakarta.annotation:jakarta.annotation-api:2.0.0'
|
||||
dependency 'jakarta.servlet:jakarta.servlet-api:5.0.0'
|
||||
dependency 'mysql:mysql-connector-java:8.0.27'
|
||||
dependency 'org.apache.derby:derby:10.14.2.0'
|
||||
dependency 'org.assertj:assertj-core:3.22.0'
|
||||
dependency 'org.hamcrest:hamcrest:2.2'
|
||||
dependency 'org.hsqldb:hsqldb:2.6.1'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.7.4'
|
||||
dependencySet(group: 'org.mockito', version: '4.2.0') {
|
||||
entry 'mockito-core'
|
||||
entry 'mockito-junit-jupiter'
|
||||
dependency 'io.lettuce:lettuce-core:6.2.1.RELEASE'
|
||||
dependency 'jakarta.servlet:jakarta.servlet-api:6.0.0'
|
||||
dependency 'jakarta.websocket:jakarta.websocket-api:2.1.0'
|
||||
dependency 'jakarta.websocket:jakarta.websocket-client-api:2.1.0'
|
||||
dependency 'mysql:mysql-connector-java:8.0.30'
|
||||
dependencySet(group: 'org.apache.derby', version: '10.16.1.1') {
|
||||
entry 'derby'
|
||||
entry 'derbytools'
|
||||
}
|
||||
|
||||
dependencySet(group: 'org.mongodb', version: '4.6.0') {
|
||||
dependency 'org.assertj:assertj-core:3.23.1'
|
||||
dependency 'org.hamcrest:hamcrest:2.2'
|
||||
dependency 'org.hsqldb:hsqldb:2.7.0'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:3.0.7'
|
||||
dependencySet(group: 'org.mongodb', version: '4.8.0-beta0') {
|
||||
entry 'mongodb-driver-core'
|
||||
entry 'mongodb-driver-sync'
|
||||
entry 'mongodb-driver-reactivestreams'
|
||||
}
|
||||
dependency 'org.postgresql:postgresql:42.3.1'
|
||||
dependency 'org.postgresql:postgresql:42.5.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
6
gradlew
vendored
6
gradlew
vendored
@@ -205,6 +205,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
180
gradlew.bat
vendored
180
gradlew.bat
vendored
@@ -1,89 +1,91 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
@@ -6,12 +6,13 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.9"
|
||||
id "com.gradle.enterprise" version "3.11.2"
|
||||
id "io.spring.ge.conventions" version "0.0.7"
|
||||
}
|
||||
|
||||
rootProject.name = 'spring-session-build'
|
||||
|
||||
include 'spring-session-bom'
|
||||
include 'spring-session-core'
|
||||
include 'spring-session-data-mongodb'
|
||||
include 'spring-session-data-redis'
|
||||
@@ -19,10 +20,10 @@ include 'spring-session-docs'
|
||||
include 'spring-session-hazelcast'
|
||||
include 'spring-session-jdbc'
|
||||
|
||||
//file('spring-session-samples').eachDirMatch(~/spring-session-sample-.*/) { dir ->
|
||||
// include dir.name
|
||||
// project(":$dir.name").projectDir = dir
|
||||
//}
|
||||
file('spring-session-samples').eachDirMatch(~/spring-session-sample-.*/) { dir ->
|
||||
include dir.name
|
||||
project(":$dir.name").projectDir = dir
|
||||
}
|
||||
|
||||
rootProject.children.each { project ->
|
||||
project.buildFileName = "${project.name}.gradle"
|
||||
|
||||
15
spring-session-bom/spring-session-bom.gradle
Normal file
15
spring-session-bom/spring-session-bom.gradle
Normal file
@@ -0,0 +1,15 @@
|
||||
import io.spring.gradle.convention.SpringModulePlugin
|
||||
|
||||
plugins {
|
||||
id("io.spring.convention.bom")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
constraints {
|
||||
project.rootProject.allprojects { project ->
|
||||
project.plugins.withType(SpringModulePlugin) {
|
||||
api(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ dependencies {
|
||||
api "org.springframework:spring-jcl"
|
||||
|
||||
optional "io.projectreactor:reactor-core"
|
||||
optional "jakarta.annotation:jakarta.annotation-api"
|
||||
optional "jakarta.servlet:jakarta.servlet-api"
|
||||
optional "org.springframework:spring-context"
|
||||
optional "org.springframework:spring-jdbc"
|
||||
@@ -19,6 +18,8 @@ dependencies {
|
||||
|
||||
testImplementation "io.projectreactor:reactor-test"
|
||||
testImplementation "org.mockito:mockito-core"
|
||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||
testImplementation "org.mockito:mockito-inline"
|
||||
testImplementation "edu.umd.cs.mtc:multithreadedtc"
|
||||
testImplementation "org.springframework:spring-test"
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,10 +49,16 @@ import java.util.UUID;
|
||||
public final class MapSession implements Session, Serializable {
|
||||
|
||||
/**
|
||||
* Default {@link #setMaxInactiveInterval(Duration)} (30 minutes).
|
||||
* Default {@link #setMaxInactiveInterval(Duration)} (30 minutes) in seconds.
|
||||
*/
|
||||
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS = 1800;
|
||||
|
||||
/**
|
||||
* Default {@link #setMaxInactiveInterval(Duration)} (30 minutes).
|
||||
*/
|
||||
public static final Duration DEFAULT_MAX_INACTIVE_INTERVAL = Duration
|
||||
.ofSeconds(DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
private String id;
|
||||
|
||||
private final String originalId;
|
||||
@@ -66,7 +72,7 @@ public final class MapSession implements Session, Serializable {
|
||||
/**
|
||||
* Defaults to 30 minutes.
|
||||
*/
|
||||
private Duration maxInactiveInterval = Duration.ofSeconds(DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
private Duration maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL;
|
||||
|
||||
/**
|
||||
* Creates a new instance with a secure randomly generated identifier.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link SessionRepository} backed by a {@link java.util.Map} and that uses a
|
||||
@@ -38,11 +39,7 @@ import org.springframework.session.events.SessionExpiredEvent;
|
||||
*/
|
||||
public class MapSessionRepository implements SessionRepository<MapSession> {
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
private final Map<String, Session> sessions;
|
||||
|
||||
@@ -59,12 +56,13 @@ public class MapSessionRepository implements SessionRepository<MapSession> {
|
||||
}
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 30 minutes.
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
public void setDefaultMaxInactiveInterval(Duration defaultMaxInactiveInterval) {
|
||||
Assert.notNull(defaultMaxInactiveInterval, "defaultMaxInactiveInterval must not be null");
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
@@ -97,9 +95,7 @@ public class MapSessionRepository implements SessionRepository<MapSession> {
|
||||
@Override
|
||||
public MapSession createSession() {
|
||||
MapSession result = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
result.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
result.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveSessionRepository} backed by a {@link Map} and that uses a
|
||||
@@ -40,11 +41,7 @@ import org.springframework.session.events.SessionExpiredEvent;
|
||||
*/
|
||||
public class ReactiveMapSessionRepository implements ReactiveSessionRepository<MapSession> {
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
private final Map<String, Session> sessions;
|
||||
|
||||
@@ -61,12 +58,13 @@ public class ReactiveMapSessionRepository implements ReactiveSessionRepository<M
|
||||
}
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 30 minutes.
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
public void setDefaultMaxInactiveInterval(Duration defaultMaxInactiveInterval) {
|
||||
Assert.notNull(defaultMaxInactiveInterval, "defaultMaxInactiveInterval must not be null");
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
@@ -99,9 +97,7 @@ public class ReactiveMapSessionRepository implements ReactiveSessionRepository<M
|
||||
public Mono<MapSession> createSession() {
|
||||
return Mono.defer(() -> {
|
||||
MapSession result = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
result.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
result.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
|
||||
return Mono.just(result);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.aot.hint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeHintsRegistrar} for common session hints.
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class CommonSessionRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
Arrays.asList(TypeReference.of(String.class), TypeReference.of(ArrayList.class),
|
||||
TypeReference.of(TreeSet.class), TypeReference.of(Number.class), TypeReference.of(Long.class),
|
||||
TypeReference.of(Integer.class), TypeReference.of(StackTraceElement.class),
|
||||
TypeReference.of(Throwable.class), TypeReference.of(Exception.class),
|
||||
TypeReference.of(RuntimeException.class),
|
||||
TypeReference.of("java.util.Collections$UnmodifiableCollection"),
|
||||
TypeReference.of("java.util.Collections$UnmodifiableList"),
|
||||
TypeReference.of("java.util.Collections$EmptyList"),
|
||||
TypeReference.of("java.util.Collections$UnmodifiableRandomAccessList"),
|
||||
TypeReference.of("java.util.Collections$UnmodifiableSet")).forEach(hints.serialization()::registerType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.aot.hint;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeHintsRegistrar} for common session security hints.
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class CommonSessionSecurityRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
registerSecurityHintsIfNeeded(hints);
|
||||
registerOAuth2ClientHintsIfNeeded(hints);
|
||||
registerOAuth2ResourceServerHintsIfNeeded(hints);
|
||||
}
|
||||
|
||||
private void registerSecurityHintsIfNeeded(RuntimeHints hints) {
|
||||
Arrays.asList(TypeReference.of("org.springframework.security.core.context.SecurityContextImpl"),
|
||||
TypeReference.of("org.springframework.security.core.authority.SimpleGrantedAuthority"),
|
||||
TypeReference.of("org.springframework.security.core.userdetails.User"),
|
||||
TypeReference.of("org.springframework.security.authentication.AbstractAuthenticationToken"),
|
||||
TypeReference.of("org.springframework.security.authentication.UsernamePasswordAuthenticationToken"),
|
||||
TypeReference.of("org.springframework.security.core.AuthenticationException"),
|
||||
TypeReference.of("org.springframework.security.authentication.BadCredentialsException"),
|
||||
TypeReference.of("org.springframework.security.core.userdetails.UsernameNotFoundException"),
|
||||
TypeReference.of("org.springframework.security.authentication.AccountExpiredException"),
|
||||
TypeReference.of("org.springframework.security.authentication.ProviderNotFoundException"),
|
||||
TypeReference.of("org.springframework.security.authentication.DisabledException"),
|
||||
TypeReference.of("org.springframework.security.authentication.LockedException"),
|
||||
TypeReference.of("org.springframework.security.authentication.AuthenticationServiceException"),
|
||||
TypeReference.of("org.springframework.security.authentication.CredentialsExpiredException"),
|
||||
TypeReference.of("org.springframework.security.authentication.InsufficientAuthenticationException"),
|
||||
TypeReference
|
||||
.of("org.springframework.security.web.authentication.session.SessionAuthenticationException"),
|
||||
TypeReference.of(
|
||||
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException"),
|
||||
TypeReference.of("org.springframework.security.core.userdetails.User$AuthorityComparator"))
|
||||
.forEach((type) -> hints.serialization().registerType(type, (hint) -> hint.onReachableType(
|
||||
TypeReference.of("org.springframework.security.core.context.SecurityContextImpl"))));
|
||||
}
|
||||
|
||||
private void registerOAuth2ResourceServerHintsIfNeeded(RuntimeHints hints) {
|
||||
Arrays.asList(
|
||||
TypeReference.of("org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken"),
|
||||
TypeReference.of(
|
||||
"org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken"),
|
||||
TypeReference.of("org.springframework.security.oauth2.core.OAuth2AuthenticationException"))
|
||||
.forEach((type) -> hints.serialization().registerType(type, (hint) -> hint.onReachableType(TypeReference
|
||||
.of("org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken"))));
|
||||
}
|
||||
|
||||
private void registerOAuth2ClientHintsIfNeeded(RuntimeHints hints) {
|
||||
Arrays.asList(
|
||||
TypeReference.of("org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken"),
|
||||
TypeReference
|
||||
.of("org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken"),
|
||||
TypeReference.of(
|
||||
"org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken"),
|
||||
TypeReference.of("org.springframework.security.oauth2.core.OAuth2AuthenticationException"))
|
||||
.forEach((type) -> hints.serialization().registerType(type, (hint) -> hint.onReachableType(TypeReference
|
||||
.of("org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken"))));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.aot.hint.server;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.security.web.server.csrf.DefaultCsrfToken;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} for Reactive Session hints.
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class WebSessionSecurityRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
if (!ClassUtils.isPresent("org.springframework.web.server.WebSession", classLoader) || !ClassUtils
|
||||
.isPresent("org.springframework.security.web.server.csrf.DefaultCsrfToken", classLoader)) {
|
||||
return;
|
||||
}
|
||||
hints.serialization().registerType(DefaultCsrfToken.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.aot.hint.servlet;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
import org.springframework.security.web.csrf.DefaultCsrfToken;
|
||||
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
import org.springframework.security.web.savedrequest.SavedCookie;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} for Servlet Session hints.
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class HttpSessionSecurityRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
if (!ClassUtils.isPresent("jakarta.servlet.http.HttpSession", classLoader)
|
||||
|| !ClassUtils.isPresent("org.springframework.security.web.csrf.DefaultCsrfToken", classLoader)) {
|
||||
return;
|
||||
}
|
||||
Arrays.asList(TypeReference.of(TreeMap.class), TypeReference.of(Locale.class),
|
||||
TypeReference.of(DefaultSavedRequest.class), TypeReference.of(DefaultCsrfToken.class),
|
||||
TypeReference.of(WebAuthenticationDetails.class), TypeReference.of(SavedCookie.class),
|
||||
TypeReference.of("java.lang.String$CaseInsensitiveComparator"))
|
||||
.forEach(hints.serialization()::registerType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,6 @@ import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
@@ -34,7 +33,7 @@ import org.springframework.session.events.SessionDestroyedEvent;
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* {@literal @Configuration}
|
||||
* {@literal @Configuration(proxyBeanMethods = false)}
|
||||
* {@literal @EnableSpringHttpSession}
|
||||
* public class SpringHttpSessionConfig {
|
||||
*
|
||||
@@ -74,7 +73,6 @@ import org.springframework.session.events.SessionDestroyedEvent;
|
||||
@Target({ java.lang.annotation.ElementType.TYPE })
|
||||
@Documented
|
||||
@Import(SpringHttpSessionConfiguration.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableSpringHttpSession {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,6 @@ package org.springframework.session.config.annotation.web.http;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.SessionCookieConfig;
|
||||
import jakarta.servlet.http.HttpSessionListener;
|
||||
@@ -28,6 +27,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
@@ -91,7 +91,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @see EnableSpringHttpSession
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
public class SpringHttpSessionConfiguration implements InitializingBean, ApplicationContextAware {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@@ -107,8 +107,8 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
|
||||
private List<HttpSessionListener> httpSessionListeners = new ArrayList<>();
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
CookieSerializer cookieSerializer = (this.cookieSerializer != null) ? this.cookieSerializer
|
||||
: createDefaultCookieSerializer();
|
||||
this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,7 +20,6 @@ import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
@@ -31,7 +30,7 @@ import org.springframework.context.annotation.Import;
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* {@literal @Configuration}
|
||||
* {@literal @Configuration(proxyBeanMethods = false)}
|
||||
* {@literal @EnableSpringWebSession}
|
||||
* public class SpringWebFluxConfig {
|
||||
*
|
||||
@@ -50,7 +49,6 @@ import org.springframework.context.annotation.Import;
|
||||
@Target({ java.lang.annotation.ElementType.TYPE })
|
||||
@Documented
|
||||
@Import(SpringWebSessionConfiguration.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableSpringWebSession {
|
||||
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ public class HeaderHttpSessionIdResolver implements HttpSessionIdResolver {
|
||||
@Override
|
||||
public List<String> resolveSessionIds(HttpServletRequest request) {
|
||||
String headerValue = request.getHeader(this.headerName);
|
||||
System.out.println(headerValue);
|
||||
return (headerValue != null) ? Collections.singletonList(headerValue) : Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,14 +19,11 @@ package org.springframework.session.web.http;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import jakarta.servlet.http.HttpSessionBindingEvent;
|
||||
import jakarta.servlet.http.HttpSessionBindingListener;
|
||||
import jakarta.servlet.http.HttpSessionContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -41,12 +38,11 @@ import org.springframework.session.Session;
|
||||
* @author Vedran Pavic
|
||||
* @since 1.1
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(HttpSessionAdapter.class);
|
||||
|
||||
private S session;
|
||||
private final S session;
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
@@ -101,35 +97,18 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
return (int) this.session.getMaxInactiveInterval().getSeconds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSessionContext getSessionContext() {
|
||||
return NOOP_SESSION_CONTEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name) {
|
||||
checkState();
|
||||
return this.session.getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(String name) {
|
||||
return getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
checkState();
|
||||
return Collections.enumeration(this.session.getAttributeNames());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getValueNames() {
|
||||
checkState();
|
||||
Set<String> attrs = this.session.getAttributeNames();
|
||||
return attrs.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Object value) {
|
||||
checkState();
|
||||
@@ -156,11 +135,6 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putValue(String name, Object value) {
|
||||
setAttribute(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
checkState();
|
||||
@@ -176,11 +150,6 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeValue(String name) {
|
||||
removeAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
checkState();
|
||||
@@ -203,32 +172,4 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
}
|
||||
}
|
||||
|
||||
private static final HttpSessionContext NOOP_SESSION_CONTEXT = new HttpSessionContext() {
|
||||
|
||||
@Override
|
||||
public HttpSession getSession(String sessionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getIds() {
|
||||
return EMPTY_ENUMERATION;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static final Enumeration<String> EMPTY_ENUMERATION = new Enumeration<String>() {
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nextElement() {
|
||||
throw new NoSuchElementException("a");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -75,6 +75,7 @@ import org.springframework.session.SessionRepository;
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @author Josh Cummings
|
||||
* @author Yanming Zhou
|
||||
* @since 1.0
|
||||
*/
|
||||
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
|
||||
@@ -222,10 +223,11 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
else {
|
||||
S session = wrappedSession.getSession();
|
||||
String requestedSessionId = getRequestedSessionId();
|
||||
clearRequestedSessionCache();
|
||||
SessionRepositoryFilter.this.sessionRepository.save(session);
|
||||
String sessionId = session.getId();
|
||||
if (!isRequestedSessionIdValid() || !sessionId.equals(getRequestedSessionId())) {
|
||||
if (!isRequestedSessionIdValid() || !sessionId.equals(requestedSessionId)) {
|
||||
SessionRepositoryFilter.this.httpSessionIdResolver.setSessionId(this, this.response, sessionId);
|
||||
}
|
||||
}
|
||||
@@ -265,14 +267,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
if (requestedSession != null) {
|
||||
requestedSession.setLastAccessedTime(Instant.now());
|
||||
}
|
||||
return isRequestedSessionIdValid(requestedSession);
|
||||
}
|
||||
return this.requestedSessionIdValid;
|
||||
}
|
||||
|
||||
private boolean isRequestedSessionIdValid(S session) {
|
||||
if (this.requestedSessionIdValid == null) {
|
||||
this.requestedSessionIdValid = session != null;
|
||||
this.requestedSessionIdValid = (requestedSession != null);
|
||||
}
|
||||
return this.requestedSessionIdValid;
|
||||
}
|
||||
@@ -356,7 +351,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
S session = SessionRepositoryFilter.this.sessionRepository.findById(sessionId);
|
||||
if (session != null) {
|
||||
this.requestedSession = session;
|
||||
this.requestedSessionId = sessionId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=\
|
||||
org.springframework.session.aot.hint.CommonSessionRuntimeHints,\
|
||||
org.springframework.session.aot.hint.CommonSessionSecurityRuntimeHints,\
|
||||
org.springframework.session.aot.hint.servlet.HttpSessionSecurityRuntimeHints,\
|
||||
org.springframework.session.aot.hint.server.WebSessionSecurityRuntimeHints
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -60,8 +60,8 @@ class MapSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void createSessionCustomDefaultExpiration() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval().plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval((int) expectedMaxInterval.getSeconds());
|
||||
Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval().plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(expectedMaxInterval);
|
||||
|
||||
Session session = this.repository.createSession();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -103,8 +103,8 @@ class ReactiveMapSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void createSessionWhenCustomMaxInactiveIntervalThenCustomMaxInactiveInterval() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval().plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval((int) expectedMaxInterval.getSeconds());
|
||||
Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval().plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(expectedMaxInterval);
|
||||
|
||||
Session session = this.repository.createSession().block();
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.aot.hint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link CommonSessionRuntimeHints}
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class CommonSessionRuntimeHintsTests {
|
||||
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
private final CommonSessionRuntimeHints commonSessionRuntimeHints = new CommonSessionRuntimeHints();
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getSerializationHintTypes")
|
||||
void commonSessionTypesHasHints(TypeReference typeReference) {
|
||||
this.commonSessionRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
|
||||
assertThat(RuntimeHintsPredicates.serialization().onType(typeReference)).accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aotFactoriesContainsRegistrar() {
|
||||
boolean match = SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories")
|
||||
.load(RuntimeHintsRegistrar.class).stream()
|
||||
.anyMatch((registrar) -> registrar instanceof CommonSessionRuntimeHints);
|
||||
assertThat(match).isTrue();
|
||||
}
|
||||
|
||||
private static Stream<TypeReference> getSerializationHintTypes() {
|
||||
return Stream.of(TypeReference.of(String.class), TypeReference.of(ArrayList.class),
|
||||
TypeReference.of(TreeSet.class), TypeReference.of(Number.class), TypeReference.of(Long.class),
|
||||
TypeReference.of(Integer.class), TypeReference.of(StackTraceElement.class),
|
||||
TypeReference.of(Throwable.class), TypeReference.of(Exception.class),
|
||||
TypeReference.of(RuntimeException.class),
|
||||
TypeReference.of("java.util.Collections$UnmodifiableCollection"),
|
||||
TypeReference.of("java.util.Collections$UnmodifiableList"),
|
||||
TypeReference.of("java.util.Collections$EmptyList"),
|
||||
TypeReference.of("java.util.Collections$UnmodifiableRandomAccessList"),
|
||||
TypeReference.of("java.util.Collections$UnmodifiableSet"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.aot.hint;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link CommonSessionSecurityRuntimeHints}
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class CommonSessionSecurityRuntimeHintsTests {
|
||||
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
private final CommonSessionSecurityRuntimeHints commonSessionSecurityRuntimeHints = new CommonSessionSecurityRuntimeHints();
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getSerializationHintTypes")
|
||||
void commonSecurityTypesHasHints(TypeReference typeReference) {
|
||||
this.commonSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
|
||||
assertThat(RuntimeHintsPredicates.serialization().onType(typeReference)).accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aotFactoriesContainsRegistrar() {
|
||||
boolean match = SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories")
|
||||
.load(RuntimeHintsRegistrar.class).stream()
|
||||
.anyMatch((registrar) -> registrar instanceof CommonSessionSecurityRuntimeHints);
|
||||
assertThat(match).isTrue();
|
||||
}
|
||||
|
||||
private static Stream<TypeReference> getSerializationHintTypes() {
|
||||
return Stream.of(TypeReference.of("org.springframework.security.core.context.SecurityContextImpl"),
|
||||
TypeReference.of("org.springframework.security.core.authority.SimpleGrantedAuthority"),
|
||||
TypeReference.of("org.springframework.security.core.userdetails.User"),
|
||||
TypeReference.of("org.springframework.security.authentication.AbstractAuthenticationToken"),
|
||||
TypeReference.of("org.springframework.security.authentication.UsernamePasswordAuthenticationToken"),
|
||||
TypeReference.of("org.springframework.security.core.AuthenticationException"),
|
||||
TypeReference.of("org.springframework.security.authentication.BadCredentialsException"),
|
||||
TypeReference.of("org.springframework.security.core.userdetails.UsernameNotFoundException"),
|
||||
TypeReference.of("org.springframework.security.authentication.AccountExpiredException"),
|
||||
TypeReference.of("org.springframework.security.authentication.ProviderNotFoundException"),
|
||||
TypeReference.of("org.springframework.security.authentication.DisabledException"),
|
||||
TypeReference.of("org.springframework.security.authentication.LockedException"),
|
||||
TypeReference.of("org.springframework.security.authentication.AuthenticationServiceException"),
|
||||
TypeReference.of("org.springframework.security.authentication.CredentialsExpiredException"),
|
||||
TypeReference.of("org.springframework.security.authentication.InsufficientAuthenticationException"),
|
||||
TypeReference
|
||||
.of("org.springframework.security.web.authentication.session.SessionAuthenticationException"),
|
||||
TypeReference.of(
|
||||
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException"),
|
||||
TypeReference.of("org.springframework.security.core.userdetails.User$AuthorityComparator"),
|
||||
TypeReference.of("org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken"),
|
||||
TypeReference.of(
|
||||
"org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken"),
|
||||
TypeReference.of("org.springframework.security.oauth2.core.OAuth2AuthenticationException"),
|
||||
TypeReference.of("org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken"),
|
||||
TypeReference
|
||||
.of("org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken"),
|
||||
TypeReference.of(
|
||||
"org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken"),
|
||||
TypeReference.of("org.springframework.security.oauth2.core.OAuth2AuthenticationException"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.aot.hint.server;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.security.web.server.csrf.DefaultCsrfToken;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebSessionSecurityRuntimeHints}
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class WebSessionSecurityRuntimeHintsTests {
|
||||
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
private final WebSessionSecurityRuntimeHints webSessionSecurityRuntimeHints = new WebSessionSecurityRuntimeHints();
|
||||
|
||||
@Test
|
||||
void defaultCsrfTokenHasHints() {
|
||||
this.webSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
|
||||
assertThat(RuntimeHintsPredicates.serialization().onType(DefaultCsrfToken.class)).accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerHintsWhenWebSessionMissingThenDoNotRegisterHints() {
|
||||
try (MockedStatic<ClassUtils> classUtilsMock = mockStatic(ClassUtils.class)) {
|
||||
classUtilsMock.when(() -> ClassUtils.isPresent(eq("org.springframework.web.server.WebSession"), any()))
|
||||
.thenReturn(false);
|
||||
this.webSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
|
||||
assertThat(this.hints.serialization().javaSerializationHints()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerHintsWhenDefaultCsrfTokenMissingThenDoNotRegisterHints() {
|
||||
try (MockedStatic<ClassUtils> classUtilsMock = mockStatic(ClassUtils.class)) {
|
||||
classUtilsMock
|
||||
.when(() -> ClassUtils
|
||||
.isPresent(eq("org.springframework.security.web.server.csrf.DefaultCsrfToken"), any()))
|
||||
.thenReturn(false);
|
||||
this.webSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
|
||||
assertThat(this.hints.serialization().javaSerializationHints()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void aotFactoriesContainsRegistrar() {
|
||||
boolean match = SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories")
|
||||
.load(RuntimeHintsRegistrar.class).stream()
|
||||
.anyMatch((registrar) -> registrar instanceof WebSessionSecurityRuntimeHints);
|
||||
assertThat(match).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.aot.hint.servlet;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
import org.springframework.security.web.csrf.DefaultCsrfToken;
|
||||
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
import org.springframework.security.web.savedrequest.SavedCookie;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
/**
|
||||
* Tests for {@link HttpSessionSecurityRuntimeHints}
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class HttpSessionSecurityRuntimeHintsTests {
|
||||
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
private final HttpSessionSecurityRuntimeHints httpSessionSecurityRuntimeHints = new HttpSessionSecurityRuntimeHints();
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getSerializationHintTypes")
|
||||
void httpSessionHasHints(TypeReference typeReference) {
|
||||
this.httpSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
|
||||
assertThat(RuntimeHintsPredicates.serialization().onType(typeReference)).accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerHintsWhenHttpSessionMissingThenDoNotRegisterHints() {
|
||||
try (MockedStatic<ClassUtils> classUtilsMock = mockStatic(ClassUtils.class)) {
|
||||
classUtilsMock.when(() -> ClassUtils.isPresent(eq("jakarta.servlet.http.HttpSession"), any()))
|
||||
.thenReturn(false);
|
||||
this.httpSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
|
||||
assertThat(this.hints.serialization().javaSerializationHints()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerHintsWhenDefaultCsrfTokenMissingThenDoNotRegisterHints() {
|
||||
try (MockedStatic<ClassUtils> classUtilsMock = mockStatic(ClassUtils.class)) {
|
||||
classUtilsMock.when(
|
||||
() -> ClassUtils.isPresent(eq("org.springframework.security.web.csrf.DefaultCsrfToken"), any()))
|
||||
.thenReturn(false);
|
||||
this.httpSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
|
||||
assertThat(this.hints.serialization().javaSerializationHints()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void aotFactoriesContainsRegistrar() {
|
||||
boolean match = SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories")
|
||||
.load(RuntimeHintsRegistrar.class).stream()
|
||||
.anyMatch((registrar) -> registrar instanceof HttpSessionSecurityRuntimeHints);
|
||||
assertThat(match).isTrue();
|
||||
}
|
||||
|
||||
private static Stream<TypeReference> getSerializationHintTypes() {
|
||||
return Stream.of(TypeReference.of(TreeMap.class), TypeReference.of(Locale.class),
|
||||
TypeReference.of(DefaultSavedRequest.class), TypeReference.of(DefaultCsrfToken.class),
|
||||
TypeReference.of(WebAuthenticationDetails.class), TypeReference.of(SavedCookie.class),
|
||||
TypeReference.of("java.lang.String$CaseInsensitiveComparator"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.ReactiveMapSessionRepository;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
@@ -105,6 +106,7 @@ class SpringWebSessionConfigurationTests {
|
||||
/**
|
||||
* A configuration with all the right parts.
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableSpringWebSession
|
||||
static class GoodConfig {
|
||||
|
||||
@@ -122,11 +124,13 @@ class SpringWebSessionConfigurationTests {
|
||||
/**
|
||||
* A configuration where no {@link ReactiveSessionRepository} is defined. It's BAD!
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableSpringWebSession
|
||||
static class BadConfig {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableSpringWebSession
|
||||
static class OverrideSessionIdResolver {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,12 +24,12 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.security.core.AuthenticatedPrincipal;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -44,10 +44,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.mock;
|
||||
import static org.mockito.BDDMockito.verify;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringSessionBackedSessionRegistry}.
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SpringSessionBackedSessionRegistryTest {
|
||||
|
||||
private static final String SESSION_ID = "sessionId";
|
||||
@@ -66,11 +68,6 @@ class SpringSessionBackedSessionRegistryTest {
|
||||
@InjectMocks
|
||||
private SpringSessionBackedSessionRegistry<Session> sessionRegistry;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionInformationForExistingSession() {
|
||||
Session session = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
@@ -157,7 +154,7 @@ class SpringSessionBackedSessionRegistryTest {
|
||||
private Session createSession(String sessionId, String userName, Instant lastAccessed) {
|
||||
MapSession session = new MapSession(sessionId);
|
||||
session.setLastAccessedTime(lastAccessed);
|
||||
Authentication authentication = mock(Authentication.class);
|
||||
Authentication authentication = mock(Authentication.class, withSettings().lenient());
|
||||
given(authentication.getName()).willReturn(userName);
|
||||
SecurityContextImpl securityContext = new SecurityContextImpl();
|
||||
securityContext.setAuthentication(authentication);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,18 +26,20 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class OnCommittedResponseWrapperTests {
|
||||
|
||||
private static final String NL = "\r\n";
|
||||
|
||||
@Mock
|
||||
@Mock(lenient = true)
|
||||
HttpServletResponse delegate;
|
||||
|
||||
@Mock
|
||||
@@ -52,7 +54,6 @@ class OnCommittedResponseWrapperTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.response = new OnCommittedResponseWrapper(this.delegate) {
|
||||
@Override
|
||||
protected void onResponseCommitted() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,10 +25,11 @@ import jakarta.servlet.http.HttpSessionListener;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.session.MapSession;
|
||||
@@ -47,6 +48,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
* @author Rob Winch
|
||||
* @since 1.1
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SessionEventHttpSessionListenerAdapterTests {
|
||||
|
||||
@Mock
|
||||
@@ -69,7 +71,6 @@ class SessionEventHttpSessionListenerAdapterTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.listener = new SessionEventHttpSessionListenerAdapter(Arrays.asList(this.listener1, this.listener2));
|
||||
this.listener.setServletContext(new MockServletContext());
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,7 +24,6 @@ import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -40,13 +39,13 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import jakarta.servlet.http.HttpSessionBindingEvent;
|
||||
import jakarta.servlet.http.HttpSessionBindingListener;
|
||||
import jakarta.servlet.http.HttpSessionContext;
|
||||
|
||||
import org.assertj.core.data.Offset;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
@@ -78,7 +77,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
/**
|
||||
* Tests for {@link SessionRepositoryFilter}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SessionRepositoryFilterTests {
|
||||
|
||||
@Mock
|
||||
@@ -98,7 +97,6 @@ class SessionRepositoryFilterTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.sessions = new HashMap<>();
|
||||
this.sessionRepository = new MapSessionRepository(this.sessions);
|
||||
this.filter = new SessionRepositoryFilter<>(this.sessionRepository);
|
||||
@@ -315,52 +313,6 @@ class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterValue() throws Exception {
|
||||
final String ATTR = "ATTR";
|
||||
final String VALUE = "VALUE";
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
wrappedRequest.getSession().putValue(ATTR, VALUE);
|
||||
assertThat(wrappedRequest.getSession().getValue(ATTR)).isEqualTo(VALUE);
|
||||
assertThat(Arrays.asList(wrappedRequest.getSession().getValueNames())).containsOnly(ATTR);
|
||||
}
|
||||
});
|
||||
|
||||
nextRequest();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
assertThat(wrappedRequest.getSession().getValue(ATTR)).isEqualTo(VALUE);
|
||||
assertThat(Arrays.asList(wrappedRequest.getSession().getValueNames())).containsOnly(ATTR);
|
||||
}
|
||||
});
|
||||
|
||||
nextRequest();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
assertThat(wrappedRequest.getSession().getValue(ATTR)).isEqualTo(VALUE);
|
||||
|
||||
wrappedRequest.getSession().removeValue(ATTR);
|
||||
|
||||
assertThat(wrappedRequest.getSession().getValue(ATTR)).isNull();
|
||||
}
|
||||
});
|
||||
|
||||
nextRequest();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
assertThat(wrappedRequest.getSession().getValue(ATTR)).isNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterIsNewTrue() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@@ -636,27 +588,6 @@ class SessionRepositoryFilterTests {
|
||||
assertThat(session.getSecure()).describedAs("Session Cookie should be marked as Secure").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterSessionContext() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSessionContext sessionContext = wrappedRequest.getSession().getSessionContext();
|
||||
assertThat(sessionContext).isNotNull();
|
||||
assertThat(sessionContext.getSession("a")).isNull();
|
||||
assertThat(sessionContext.getIds()).isNotNull();
|
||||
assertThat(sessionContext.getIds().hasMoreElements()).isFalse();
|
||||
|
||||
try {
|
||||
sessionContext.getIds().nextElement();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (NoSuchElementException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- saving
|
||||
|
||||
@Test
|
||||
@@ -740,23 +671,6 @@ class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateValueIllegalState() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.invalidate();
|
||||
try {
|
||||
session.getValue("attr");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateAttributeNamesIllegalState() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@@ -774,23 +688,6 @@ class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateValueNamesIllegalState() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.invalidate();
|
||||
try {
|
||||
session.getValueNames();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateSetAttributeIllegalState() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@@ -808,23 +705,6 @@ class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidatePutValueIllegalState() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.invalidate();
|
||||
try {
|
||||
session.putValue("a", "b");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateRemoveAttributeIllegalState() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@@ -842,23 +722,6 @@ class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateRemoveValueIllegalState() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.invalidate();
|
||||
try {
|
||||
session.removeValue("name");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateNewIllegalState() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@@ -920,20 +783,6 @@ class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateSessionContext() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.invalidate();
|
||||
|
||||
// no exception
|
||||
session.getSessionContext();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInvalidateMaxInteractiveInterval() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@@ -1326,8 +1175,8 @@ class SessionRepositoryFilterTests {
|
||||
}
|
||||
});
|
||||
|
||||
// 3 invocations expected: initial resolution, after invalidation, after commit
|
||||
verify(sessionRepository, times(3)).findById(eq(session.getId()));
|
||||
// 2 invocations expected: initial resolution, after invalidation
|
||||
verify(sessionRepository, times(2)).findById(eq(session.getId()));
|
||||
verify(sessionRepository).deleteById(eq(session.getId()));
|
||||
verify(sessionRepository).createSession();
|
||||
verify(sessionRepository).save(any());
|
||||
@@ -1338,8 +1187,9 @@ class SessionRepositoryFilterTests {
|
||||
|
||||
@Test
|
||||
void order() {
|
||||
assertThat(AnnotationAwareOrderComparator.INSTANCE.compare(this.filter,
|
||||
new SessionRepositoryFilterDefaultOrder()));
|
||||
assertThat(
|
||||
AnnotationAwareOrderComparator.INSTANCE.compare(this.filter, new SessionRepositoryFilterDefaultOrder()))
|
||||
.isZero();
|
||||
}
|
||||
|
||||
// We want the filter to work without any dependencies on Spring
|
||||
@@ -1551,7 +1401,7 @@ class SessionRepositoryFilterTests {
|
||||
|
||||
}
|
||||
|
||||
private abstract class DoInFilter {
|
||||
private abstract static class DoInFilter {
|
||||
|
||||
void doFilter(HttpServletRequest wrappedRequest, HttpServletResponse wrappedResponse)
|
||||
throws ServletException, IOException {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,8 +23,9 @@ import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
@@ -43,9 +44,10 @@ import static org.mockito.Mockito.verify;
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
|
||||
@Mock
|
||||
@Mock(lenient = true)
|
||||
private ReactiveSessionRepository<S> sessionRepository;
|
||||
|
||||
@Mock
|
||||
@@ -58,7 +60,6 @@ class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.webSessionStore = new SpringSessionWebSessionStore<>(this.sessionRepository);
|
||||
given(this.sessionRepository.findById(any())).willReturn(Mono.just(this.findByIdSession));
|
||||
given(this.sessionRepository.createSession()).willReturn(Mono.just(this.createSession));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,10 +18,11 @@ package org.springframework.session.web.socket.handler;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@@ -35,6 +36,7 @@ import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WebSocketConnectHandlerDecoratorFactoryTests {
|
||||
|
||||
@Mock
|
||||
@@ -53,7 +55,6 @@ class WebSocketConnectHandlerDecoratorFactoryTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.factory = new WebSocketConnectHandlerDecoratorFactory(this.eventPublisher);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,8 +22,9 @@ import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
@@ -44,15 +45,16 @@ import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WebSocketRegistryListenerTests {
|
||||
|
||||
@Mock
|
||||
@Mock(lenient = true)
|
||||
private WebSocketSession wsSession;
|
||||
|
||||
@Mock
|
||||
@Mock(lenient = true)
|
||||
private WebSocketSession wsSession2;
|
||||
|
||||
@Mock
|
||||
@Mock(lenient = true)
|
||||
private Message<byte[]> message;
|
||||
|
||||
@Mock
|
||||
@@ -74,7 +76,6 @@ class WebSocketRegistryListenerTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
String sessionId = "session-id";
|
||||
MapSession session = new MapSession(sessionId);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,9 +27,10 @@ import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -51,9 +52,10 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SessionRepositoryMessageInterceptorTests {
|
||||
|
||||
@Mock
|
||||
@Mock(lenient = true)
|
||||
SessionRepository<Session> sessionRepository;
|
||||
|
||||
@Mock
|
||||
@@ -70,7 +72,6 @@ class SessionRepositoryMessageInterceptorTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.interceptor = new SessionRepositoryMessageInterceptor<>(this.sessionRepository);
|
||||
this.headers = SimpMessageHeaderAccessor.create();
|
||||
this.headers.setSessionId("session");
|
||||
|
||||
@@ -18,6 +18,8 @@ dependencies {
|
||||
optional "org.mongodb:mongodb-driver-core"
|
||||
testImplementation "org.mongodb:mongodb-driver-sync"
|
||||
testImplementation "org.mongodb:mongodb-driver-reactivestreams"
|
||||
testImplementation 'jakarta.websocket:jakarta.websocket-api'
|
||||
testImplementation 'jakarta.websocket:jakarta.websocket-client-api'
|
||||
integrationTestCompile "org.testcontainers:mongodb"
|
||||
|
||||
// Everything else
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo.integration;
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
@@ -25,9 +25,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.serializer.DefaultDeserializer;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.Assert;
|
||||
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
@@ -49,8 +46,7 @@ public abstract class AbstractClassLoaderTest<T> extends AbstractITest {
|
||||
|
||||
Field mongoSessionConverterField = ReflectionUtils.findField(this.sessionRepository.getClass(),
|
||||
"mongoSessionConverter");
|
||||
ReflectionUtils.makeAccessible(
|
||||
Assert.requireNonNull(mongoSessionConverterField, "mongoSessionConverter must not be null!"));
|
||||
ReflectionUtils.makeAccessible(mongoSessionConverterField);
|
||||
AbstractMongoSessionConverter sessionConverter = (AbstractMongoSessionConverter) ReflectionUtils
|
||||
.getField(mongoSessionConverterField, this.sessionRepository);
|
||||
|
||||
@@ -70,7 +66,7 @@ public abstract class AbstractClassLoaderTest<T> extends AbstractITest {
|
||||
private static Object extractField(Class<?> clazz, String fieldName, Object obj) {
|
||||
|
||||
Field field = ReflectionUtils.findField(clazz, fieldName);
|
||||
ReflectionUtils.makeAccessible(Assert.requireNonNull(field, fieldName + " must not be null!"));
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return ReflectionUtils.getField(field, obj);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo.integration;
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo.integration;
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -37,8 +37,6 @@ import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.mongo.MongoIndexedSessionRepository;
|
||||
import org.springframework.session.data.mongo.MongoSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -389,18 +387,20 @@ public abstract class AbstractMongoRepositoryITest extends AbstractITest {
|
||||
|
||||
protected static class BaseConfig {
|
||||
|
||||
private static final String DOCKER_IMAGE = "mongo:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "mongo:5.0.11";
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "stop")
|
||||
public MongoDBContainer mongoContainer() {
|
||||
return new MongoDBContainer(DOCKER_IMAGE).withExposedPorts(27017);
|
||||
@Bean
|
||||
public MongoDBContainer mongoDbContainer() {
|
||||
MongoDBContainer mongoDbContainer = new MongoDBContainer(DOCKER_IMAGE);
|
||||
mongoDbContainer.start();
|
||||
return mongoDbContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MongoOperations mongoOperations(MongoDBContainer mongoContainer) {
|
||||
|
||||
MongoClient mongo = MongoClients.create(
|
||||
"mongodb://" + mongoContainer.getContainerIpAddress() + ":" + mongoContainer.getFirstMappedPort());
|
||||
MongoClient mongo = MongoClients
|
||||
.create("mongodb://" + mongoContainer.getHost() + ":" + mongoContainer.getFirstMappedPort());
|
||||
return new MongoTemplate(mongo, "test");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo.integration;
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
@@ -41,8 +41,6 @@ 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.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JacksonMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.config.annotation.web.reactive.EnableMongoWebSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
@@ -137,6 +135,7 @@ public class MongoDbDeleteJacksonSessionVerificationTest {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebFluxSecurity
|
||||
static class SecurityConfig {
|
||||
|
||||
@@ -156,9 +155,8 @@ public class MongoDbDeleteJacksonSessionVerificationTest {
|
||||
|
||||
@Bean
|
||||
MapReactiveUserDetailsService userDetailsService() {
|
||||
return new MapReactiveUserDetailsService(User.withDefaultPasswordEncoder() //
|
||||
.username("admin") //
|
||||
.password("password") //
|
||||
return new MapReactiveUserDetailsService(User.withUsername("admin") //
|
||||
.password("{noop}password") //
|
||||
.roles("USER,ADMIN") //
|
||||
.build());
|
||||
}
|
||||
@@ -175,18 +173,20 @@ public class MongoDbDeleteJacksonSessionVerificationTest {
|
||||
@EnableMongoWebSession
|
||||
static class Config {
|
||||
|
||||
private static final String DOCKER_IMAGE = "mongo:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "mongo:5.0.11";
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "stop")
|
||||
MongoDBContainer mongoContainer() {
|
||||
return new MongoDBContainer(DOCKER_IMAGE).withExposedPorts(27017);
|
||||
@Bean
|
||||
MongoDBContainer mongoDbContainer() {
|
||||
MongoDBContainer mongoDbContainer = new MongoDBContainer(DOCKER_IMAGE);
|
||||
mongoDbContainer.start();
|
||||
return mongoDbContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveMongoOperations mongoOperations(MongoDBContainer mongoContainer) {
|
||||
|
||||
MongoClient mongo = MongoClients.create(
|
||||
"mongodb://" + mongoContainer.getContainerIpAddress() + ":" + mongoContainer.getFirstMappedPort());
|
||||
MongoClient mongo = MongoClients
|
||||
.create("mongodb://" + mongoContainer.getHost() + ":" + mongoContainer.getFirstMappedPort());
|
||||
return new ReactiveMongoTemplate(mongo, "DB_Name_DeleteJacksonSessionVerificationTest");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo.integration;
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
@@ -135,6 +135,7 @@ public class MongoDbLogoutVerificationTest {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebFluxSecurity
|
||||
static class SecurityConfig {
|
||||
|
||||
@@ -156,9 +157,8 @@ public class MongoDbLogoutVerificationTest {
|
||||
@Bean
|
||||
MapReactiveUserDetailsService userDetailsService() {
|
||||
|
||||
return new MapReactiveUserDetailsService(User.withDefaultPasswordEncoder() //
|
||||
.username("admin") //
|
||||
.password("password") //
|
||||
return new MapReactiveUserDetailsService(User.withUsername("admin") //
|
||||
.password("{noop}password") //
|
||||
.roles("USER,ADMIN") //
|
||||
.build());
|
||||
}
|
||||
@@ -170,18 +170,20 @@ public class MongoDbLogoutVerificationTest {
|
||||
@EnableMongoWebSession
|
||||
static class Config {
|
||||
|
||||
private static final String DOCKER_IMAGE = "mongo:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "mongo:5.0.11";
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "stop")
|
||||
MongoDBContainer mongoContainer() {
|
||||
return new MongoDBContainer(DOCKER_IMAGE).withExposedPorts(27017);
|
||||
@Bean
|
||||
MongoDBContainer mongoDbContainer() {
|
||||
MongoDBContainer mongoDbContainer = new MongoDBContainer(DOCKER_IMAGE);
|
||||
mongoDbContainer.start();
|
||||
return mongoDbContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveMongoOperations mongoOperations(MongoDBContainer mongoContainer) {
|
||||
|
||||
MongoClient mongo = MongoClients.create(
|
||||
"mongodb://" + mongoContainer.getContainerIpAddress() + ":" + mongoContainer.getFirstMappedPort());
|
||||
MongoClient mongo = MongoClients
|
||||
.create("mongodb://" + mongoContainer.getHost() + ":" + mongoContainer.getFirstMappedPort());
|
||||
return new ReactiveMongoTemplate(mongo, "test");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo.integration;
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
@@ -25,9 +25,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.geo.GeoModule;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JacksonMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.MongoSession;
|
||||
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo.integration;
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
@@ -23,9 +23,6 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.MongoSession;
|
||||
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,6 +36,8 @@ import org.springframework.session.DelegatingIndexResolver;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.PrincipalNameIndexResolver;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for serializing and deserializing session objects. To create custom
|
||||
@@ -54,8 +56,7 @@ public abstract class AbstractMongoSessionConverter implements GenericConverter
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
private IndexResolver<MongoSession> indexResolver = new DelegatingIndexResolver<>(
|
||||
new PrincipalNameIndexResolver<>());
|
||||
private IndexResolver<Session> indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
|
||||
|
||||
/**
|
||||
* Returns query to be executed to return sessions based on a particular index.
|
||||
@@ -122,8 +123,9 @@ public abstract class AbstractMongoSessionConverter implements GenericConverter
|
||||
|
||||
protected abstract MongoSession convert(Document sessionWrapper);
|
||||
|
||||
public void setIndexResolver(IndexResolver<MongoSession> indexResolver) {
|
||||
this.indexResolver = Assert.requireNonNull(indexResolver, "indexResolver must not be null!");
|
||||
public void setIndexResolver(IndexResolver<Session> indexResolver) {
|
||||
Assert.notNull(indexResolver, "indexResolver must not be null");
|
||||
this.indexResolver = indexResolver;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Utility to verify non null fields.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
public final class Assert {
|
||||
|
||||
private Assert() {
|
||||
}
|
||||
|
||||
public static <T> T requireNonNull(@Nullable T item, String message) {
|
||||
|
||||
if (item == null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -168,18 +168,18 @@ public class JacksonMongoSessionConverter extends AbstractMongoSessionConverter
|
||||
|
||||
}
|
||||
|
||||
private static class MongoIdNamingStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase {
|
||||
private static class MongoIdNamingStrategy extends PropertyNamingStrategies.NamingBase {
|
||||
|
||||
@Override
|
||||
public String translate(String propertyName) {
|
||||
|
||||
switch (propertyName) {
|
||||
case "id":
|
||||
return "_id";
|
||||
case "_id":
|
||||
return "id";
|
||||
default:
|
||||
return propertyName;
|
||||
case "id":
|
||||
return "_id";
|
||||
case "_id":
|
||||
return "id";
|
||||
default:
|
||||
return propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.bson.Document;
|
||||
@@ -34,9 +35,11 @@ import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Session repository implementation which stores sessions in Mongo. Uses
|
||||
@@ -46,6 +49,7 @@ import org.springframework.session.events.SessionExpiredEvent;
|
||||
*
|
||||
* @author Jakub Kubrynski
|
||||
* @author Greg Turnquist
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class MongoIndexedSessionRepository
|
||||
@@ -53,8 +57,11 @@ public class MongoIndexedSessionRepository
|
||||
|
||||
/**
|
||||
* The default time period in seconds in which a session will expire.
|
||||
* @deprecated since 3.0.0 in favor of
|
||||
* {@link MapSession#DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS}
|
||||
*/
|
||||
public static final int DEFAULT_INACTIVE_INTERVAL = 1800;
|
||||
@Deprecated
|
||||
public static final int DEFAULT_INACTIVE_INTERVAL = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
/**
|
||||
* the default collection name for storing session.
|
||||
@@ -65,12 +72,12 @@ public class MongoIndexedSessionRepository
|
||||
|
||||
private final MongoOperations mongoOperations;
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = DEFAULT_INACTIVE_INTERVAL;
|
||||
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
private String collectionName = DEFAULT_COLLECTION_NAME;
|
||||
|
||||
private AbstractMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(
|
||||
Duration.ofSeconds(this.maxInactiveIntervalInSeconds));
|
||||
this.defaultMaxInactiveInterval);
|
||||
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@@ -83,9 +90,7 @@ public class MongoIndexedSessionRepository
|
||||
|
||||
MongoSession session = new MongoSession();
|
||||
|
||||
if (this.maxInactiveIntervalInSeconds != null) {
|
||||
session.setMaxInactiveInterval(Duration.ofSeconds(this.maxInactiveIntervalInSeconds));
|
||||
}
|
||||
session.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
|
||||
|
||||
publishEvent(new SessionCreatedEvent(this, session));
|
||||
|
||||
@@ -94,9 +99,9 @@ public class MongoIndexedSessionRepository
|
||||
|
||||
@Override
|
||||
public void save(MongoSession session) {
|
||||
this.mongoOperations
|
||||
.save(Assert.requireNonNull(MongoSessionUtils.convertToDBObject(this.mongoSessionConverter, session),
|
||||
"convertToDBObject must not null!"), this.collectionName);
|
||||
DBObject dbObject = MongoSessionUtils.convertToDBObject(this.mongoSessionConverter, session);
|
||||
Assert.notNull(dbObject, "dbObject must not be null");
|
||||
this.mongoOperations.save(dbObject, this.collectionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,8 +183,29 @@ public class MongoIndexedSessionRepository
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(final Integer maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
/**
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 30 minutes.
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(Duration defaultMaxInactiveInterval) {
|
||||
org.springframework.util.Assert.notNull(defaultMaxInactiveInterval,
|
||||
"defaultMaxInactiveInterval must not be null");
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval in seconds
|
||||
* @deprecated since 3.0.0, in favor of
|
||||
* {@link #setDefaultMaxInactiveInterval(Duration)}
|
||||
*/
|
||||
@Deprecated(since = "3.0.0")
|
||||
public void setMaxInactiveIntervalInSeconds(Integer defaultMaxInactiveInterval) {
|
||||
setDefaultMaxInactiveInterval(Duration.ofSeconds(defaultMaxInactiveInterval));
|
||||
}
|
||||
|
||||
public void setCollectionName(final String collectionName) {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
|
||||
/**
|
||||
* This {@link org.springframework.session.FindByIndexNameSessionRepository}
|
||||
* implementation is kept to support backwards compatibility.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.2
|
||||
* @deprecated since 2.2.0 in favor of {@link MongoIndexedSessionRepository}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class MongoOperationsSessionRepository extends MongoIndexedSessionRepository {
|
||||
|
||||
public MongoOperationsSessionRepository(MongoOperations mongoOperations) {
|
||||
super(mongoOperations);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
|
||||
/**
|
||||
@@ -36,7 +37,7 @@ import org.springframework.session.Session;
|
||||
* @author Greg Turnquist
|
||||
* @since 1.2
|
||||
*/
|
||||
public class MongoSession implements Session {
|
||||
class MongoSession implements Session {
|
||||
|
||||
/**
|
||||
* Mongo doesn't support {@literal dot} in field names. We replace it with a unicode
|
||||
@@ -65,15 +66,15 @@ public class MongoSession implements Session {
|
||||
|
||||
private Map<String, Object> attrs = new HashMap<>();
|
||||
|
||||
public MongoSession() {
|
||||
this(MongoIndexedSessionRepository.DEFAULT_INACTIVE_INTERVAL);
|
||||
MongoSession() {
|
||||
this(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
}
|
||||
|
||||
public MongoSession(long maxInactiveIntervalInSeconds) {
|
||||
MongoSession(long maxInactiveIntervalInSeconds) {
|
||||
this(UUID.randomUUID().toString(), maxInactiveIntervalInSeconds);
|
||||
}
|
||||
|
||||
public MongoSession(String id, long maxInactiveIntervalInSeconds) {
|
||||
MongoSession(String id, long maxInactiveIntervalInSeconds) {
|
||||
|
||||
this.id = id;
|
||||
this.originalSessionId = id;
|
||||
@@ -129,7 +130,7 @@ public class MongoSession implements Session {
|
||||
return Instant.ofEpochMilli(this.createdMillis);
|
||||
}
|
||||
|
||||
public void setCreationTime(long created) {
|
||||
void setCreationTime(long created) {
|
||||
this.createdMillis = created;
|
||||
}
|
||||
|
||||
@@ -183,11 +184,11 @@ public class MongoSession implements Session {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public Date getExpireAt() {
|
||||
Date getExpireAt() {
|
||||
return this.expireAt;
|
||||
}
|
||||
|
||||
public void setExpireAt(final Date expireAt) {
|
||||
void setExpireAt(final Date expireAt) {
|
||||
this.expireAt = expireAt;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,7 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
public final class MongoSessionUtils {
|
||||
final class MongoSessionUtils {
|
||||
|
||||
private MongoSessionUtils() {
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
|
||||
/**
|
||||
* This {@link ReactiveSessionRepository} implementation is kept to support migration to
|
||||
* {@link ReactiveMongoSessionRepository} in a backwards compatible manner.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @deprecated since 2.2.0 in favor of {@link ReactiveMongoSessionRepository}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ReactiveMongoOperationsSessionRepository extends ReactiveMongoSessionRepository {
|
||||
|
||||
public ReactiveMongoOperationsSessionRepository(ReactiveMongoOperations mongoOperations) {
|
||||
super(mongoOperations);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,14 +32,17 @@ import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveSessionRepository} implementation that uses Spring Data MongoDB.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Vedran Pavic
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class ReactiveMongoSessionRepository
|
||||
@@ -47,8 +50,11 @@ public class ReactiveMongoSessionRepository
|
||||
|
||||
/**
|
||||
* The default time period in seconds in which a session will expire.
|
||||
* @deprecated since 3.0.0 in favor of
|
||||
* {@link MapSession#DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS}
|
||||
*/
|
||||
public static final int DEFAULT_INACTIVE_INTERVAL = 1800;
|
||||
@Deprecated
|
||||
public static final int DEFAULT_INACTIVE_INTERVAL = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
/**
|
||||
* The default collection name for storing session.
|
||||
@@ -59,12 +65,12 @@ public class ReactiveMongoSessionRepository
|
||||
|
||||
private final ReactiveMongoOperations mongoOperations;
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = DEFAULT_INACTIVE_INTERVAL;
|
||||
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
private String collectionName = DEFAULT_COLLECTION_NAME;
|
||||
|
||||
private AbstractMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(
|
||||
Duration.ofSeconds(this.maxInactiveIntervalInSeconds));
|
||||
this.defaultMaxInactiveInterval);
|
||||
|
||||
private MongoOperations blockingMongoOperations;
|
||||
|
||||
@@ -88,7 +94,7 @@ public class ReactiveMongoSessionRepository
|
||||
@Override
|
||||
public Mono<MongoSession> createSession() {
|
||||
|
||||
return Mono.justOrEmpty(this.maxInactiveIntervalInSeconds) //
|
||||
return Mono.justOrEmpty(this.defaultMaxInactiveInterval.toSeconds()) //
|
||||
.map(MongoSession::new) //
|
||||
.doOnNext((mongoSession) -> publishEvent(new SessionCreatedEvent(this, mongoSession))) //
|
||||
.switchIfEmpty(Mono.just(new MongoSession()));
|
||||
@@ -170,12 +176,28 @@ public class ReactiveMongoSessionRepository
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getMaxInactiveIntervalInSeconds() {
|
||||
return this.maxInactiveIntervalInSeconds;
|
||||
/**
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 30 minutes.
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(Duration defaultMaxInactiveInterval) {
|
||||
Assert.notNull(defaultMaxInactiveInterval, "defaultMaxInactiveInterval must not be null");
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(final Integer maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
/**
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval in seconds
|
||||
* @deprecated since 3.0.0, in favor of
|
||||
* {@link #setDefaultMaxInactiveInterval(Duration)}
|
||||
*/
|
||||
@Deprecated(since = "3.0.0")
|
||||
public void setMaxInactiveIntervalInSeconds(Integer defaultMaxInactiveInterval) {
|
||||
setDefaultMaxInactiveInterval(Duration.ofSeconds(defaultMaxInactiveInterval));
|
||||
}
|
||||
|
||||
public String getCollectionName() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,8 +22,8 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.data.mongo.MongoIndexedSessionRepository;
|
||||
|
||||
/**
|
||||
@@ -34,6 +34,7 @@ import org.springframework.session.data.mongo.MongoIndexedSessionRepository;
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* {@literal @Configuration(proxyBeanMethods = false)}
|
||||
* {@literal @EnableMongoHttpSession}
|
||||
* public class MongoHttpSessionConfig {
|
||||
*
|
||||
@@ -52,14 +53,13 @@ import org.springframework.session.data.mongo.MongoIndexedSessionRepository;
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(MongoHttpSessionConfiguration.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableMongoHttpSession {
|
||||
|
||||
/**
|
||||
* The maximum time a session will be kept if it is inactive.
|
||||
* @return default max inactive interval in seconds
|
||||
*/
|
||||
int maxInactiveIntervalInSeconds() default MongoIndexedSessionRepository.DEFAULT_INACTIVE_INTERVAL;
|
||||
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
/**
|
||||
* The collection name to use.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
@@ -33,12 +34,13 @@ import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.MongoIndexedSessionRepository;
|
||||
import org.springframework.session.data.mongo.MongoSession;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
@@ -51,12 +53,12 @@ import org.springframework.util.StringValueResolver;
|
||||
* @since 1.2
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
@Import(SpringHttpSessionConfiguration.class)
|
||||
public class MongoHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
|
||||
private AbstractMongoSessionConverter mongoSessionConverter;
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds;
|
||||
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
|
||||
|
||||
private String collectionName;
|
||||
|
||||
@@ -66,13 +68,13 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private IndexResolver<MongoSession> indexResolver;
|
||||
private IndexResolver<Session> indexResolver;
|
||||
|
||||
@Bean
|
||||
public MongoIndexedSessionRepository mongoSessionRepository(MongoOperations mongoOperations) {
|
||||
|
||||
MongoIndexedSessionRepository repository = new MongoIndexedSessionRepository(mongoOperations);
|
||||
repository.setMaxInactiveIntervalInSeconds(this.maxInactiveIntervalInSeconds);
|
||||
repository.setDefaultMaxInactiveInterval(this.maxInactiveInterval);
|
||||
|
||||
if (this.mongoSessionConverter != null) {
|
||||
repository.setMongoSessionConverter(this.mongoSessionConverter);
|
||||
@@ -84,7 +86,7 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
else {
|
||||
JdkMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(new SerializingConverter(),
|
||||
new DeserializingConverter(this.classLoader),
|
||||
Duration.ofSeconds(MongoIndexedSessionRepository.DEFAULT_INACTIVE_INTERVAL));
|
||||
Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS));
|
||||
|
||||
if (this.indexResolver != null) {
|
||||
mongoSessionConverter.setIndexResolver(this.indexResolver);
|
||||
@@ -107,8 +109,13 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
this.collectionName = collectionName;
|
||||
}
|
||||
|
||||
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
|
||||
this.maxInactiveInterval = maxInactiveInterval;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
setMaxInactiveInterval(Duration.ofSeconds(maxInactiveIntervalInSeconds));
|
||||
}
|
||||
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
@@ -117,10 +124,8 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
.fromMap(importMetadata.getAnnotationAttributes(EnableMongoHttpSession.class.getName()));
|
||||
|
||||
if (attributes != null) {
|
||||
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||
}
|
||||
else {
|
||||
this.maxInactiveIntervalInSeconds = MongoIndexedSessionRepository.DEFAULT_INACTIVE_INTERVAL;
|
||||
this.maxInactiveInterval = Duration
|
||||
.ofSeconds(attributes.<Integer>getNumber("maxInactiveIntervalInSeconds"));
|
||||
}
|
||||
|
||||
String collectionNameValue = (attributes != null) ? attributes.getString("collectionName") : "";
|
||||
@@ -151,7 +156,7 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setIndexResolver(IndexResolver<MongoSession> indexResolver) {
|
||||
public void setIndexResolver(IndexResolver<Session> indexResolver) {
|
||||
this.indexResolver = indexResolver;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,8 @@ import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoSessionRepository;
|
||||
|
||||
/**
|
||||
@@ -32,7 +32,7 @@ import org.springframework.session.data.mongo.ReactiveMongoSessionRepository;
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* {@literal @Configuration}
|
||||
* {@literal @Configuration(proxyBeanMethods = false)}
|
||||
* {@literal @EnableMongoWebSession}
|
||||
* public class SpringWebFluxConfig {
|
||||
*
|
||||
@@ -45,21 +45,20 @@ import org.springframework.session.data.mongo.ReactiveMongoSessionRepository;
|
||||
* </code> </pre>
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Vedran Pavić
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@Target({ java.lang.annotation.ElementType.TYPE })
|
||||
@Documented
|
||||
@Import(ReactiveMongoWebSessionConfiguration.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableMongoWebSession {
|
||||
|
||||
/**
|
||||
* The maximum time a session will be kept if it is inactive.
|
||||
* @return default max inactive interval in seconds
|
||||
*/
|
||||
int maxInactiveIntervalInSeconds() default ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL;
|
||||
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
/**
|
||||
* The collection name to use.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
@@ -34,11 +35,12 @@ import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.MongoSession;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoSessionRepository;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
@@ -48,15 +50,16 @@ import org.springframework.util.StringValueResolver;
|
||||
* {@link ReactiveMongoOperations}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Vedran Pavić
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
@Import(SpringWebSessionConfiguration.class)
|
||||
public class ReactiveMongoWebSessionConfiguration
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
|
||||
private AbstractMongoSessionConverter mongoSessionConverter;
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds;
|
||||
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
|
||||
|
||||
private String collectionName;
|
||||
|
||||
@@ -69,7 +72,7 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private IndexResolver<MongoSession> indexResolver;
|
||||
private IndexResolver<Session> indexResolver;
|
||||
|
||||
@Bean
|
||||
public ReactiveMongoSessionRepository reactiveMongoSessionRepository(ReactiveMongoOperations operations) {
|
||||
@@ -87,7 +90,7 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig
|
||||
else {
|
||||
JdkMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(new SerializingConverter(),
|
||||
new DeserializingConverter(this.classLoader),
|
||||
Duration.ofSeconds(ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL));
|
||||
Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS));
|
||||
|
||||
if (this.indexResolver != null) {
|
||||
mongoSessionConverter.setIndexResolver(this.indexResolver);
|
||||
@@ -96,9 +99,7 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig
|
||||
repository.setMongoSessionConverter(mongoSessionConverter);
|
||||
}
|
||||
|
||||
if (this.maxInactiveIntervalInSeconds != null) {
|
||||
repository.setMaxInactiveIntervalInSeconds(this.maxInactiveIntervalInSeconds);
|
||||
}
|
||||
repository.setDefaultMaxInactiveInterval(this.maxInactiveInterval);
|
||||
|
||||
if (this.collectionName != null) {
|
||||
repository.setCollectionName(this.collectionName);
|
||||
@@ -126,10 +127,8 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig
|
||||
.fromMap(importMetadata.getAnnotationAttributes(EnableMongoWebSession.class.getName()));
|
||||
|
||||
if (attributes != null) {
|
||||
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||
}
|
||||
else {
|
||||
this.maxInactiveIntervalInSeconds = ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL;
|
||||
this.maxInactiveInterval = Duration
|
||||
.ofSeconds(attributes.<Integer>getNumber("maxInactiveIntervalInSeconds"));
|
||||
}
|
||||
|
||||
String collectionNameValue = (attributes != null) ? attributes.getString("collectionName") : "";
|
||||
@@ -149,12 +148,17 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig
|
||||
this.embeddedValueResolver = embeddedValueResolver;
|
||||
}
|
||||
|
||||
public Integer getMaxInactiveIntervalInSeconds() {
|
||||
return this.maxInactiveIntervalInSeconds;
|
||||
public Duration getMaxInactiveInterval() {
|
||||
return this.maxInactiveInterval;
|
||||
}
|
||||
|
||||
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
|
||||
this.maxInactiveInterval = maxInactiveInterval;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
setMaxInactiveInterval(Duration.ofSeconds(maxInactiveIntervalInSeconds));
|
||||
}
|
||||
|
||||
public String getCollectionName() {
|
||||
@@ -172,7 +176,7 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setIndexResolver(IndexResolver<MongoSession> indexResolver) {
|
||||
public void setIndexResolver(IndexResolver<Session> indexResolver) {
|
||||
this.indexResolver = indexResolver;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,7 +49,7 @@ public abstract class AbstractMongoSessionConverterTest {
|
||||
MongoSession deserialized = convertToSession(dbObject);
|
||||
|
||||
// then
|
||||
assertThat(deserialized).isEqualToComparingFieldByField(toSerialize);
|
||||
assertThat(deserialized).usingRecursiveComparison().isEqualTo(toSerialize);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,14 +67,12 @@ public abstract class AbstractMongoSessionConverterTest {
|
||||
MongoSession deserialized = convertToSession(serialized);
|
||||
|
||||
// then
|
||||
assertThat(deserialized).isEqualToComparingOnlyGivenFields(toSerialize, "id", "createdMillis", "accessedMillis",
|
||||
"intervalSeconds", "expireAt");
|
||||
assertThat(deserialized).usingRecursiveComparison().isEqualTo(toSerialize);
|
||||
|
||||
SecurityContextImpl springSecurityContextBefore = toSerialize.getAttribute("SPRING_SECURITY_CONTEXT");
|
||||
SecurityContextImpl springSecurityContextAfter = deserialized.getAttribute("SPRING_SECURITY_CONTEXT");
|
||||
|
||||
assertThat(springSecurityContextBefore).isEqualToComparingOnlyGivenFields(springSecurityContextAfter,
|
||||
"authentication.principal", "authentication.authorities", "authentication.authenticated");
|
||||
assertThat(springSecurityContextBefore).usingRecursiveComparison().isEqualTo(springSecurityContextAfter);
|
||||
assertThat(springSecurityContextAfter.getAuthentication().getPrincipal()).isEqualTo("john_the_springer");
|
||||
assertThat(springSecurityContextAfter.getAuthentication().getCredentials()).isNull();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,7 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -76,20 +77,7 @@ public class MongoIndexedSessionRepositoryTest {
|
||||
// then
|
||||
assertThat(session.getId()).isNotEmpty();
|
||||
assertThat(session.getMaxInactiveInterval().getSeconds())
|
||||
.isEqualTo(MongoIndexedSessionRepository.DEFAULT_INACTIVE_INTERVAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateSessionWhenMaxInactiveIntervalNotDefined() {
|
||||
|
||||
// when
|
||||
this.repository.setMaxInactiveIntervalInSeconds(null);
|
||||
MongoSession session = this.repository.createSession();
|
||||
|
||||
// then
|
||||
assertThat(session.getId()).isNotEmpty();
|
||||
assertThat(session.getMaxInactiveInterval().getSeconds())
|
||||
.isEqualTo(MongoIndexedSessionRepository.DEFAULT_INACTIVE_INTERVAL);
|
||||
.isEqualTo(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -164,8 +152,7 @@ public class MongoIndexedSessionRepositoryTest {
|
||||
Document sessionDocument = new Document();
|
||||
sessionDocument.put("id", sessionId);
|
||||
|
||||
MongoSession mongoSession = new MongoSession(sessionId,
|
||||
MongoIndexedSessionRepository.DEFAULT_INACTIVE_INTERVAL);
|
||||
MongoSession mongoSession = new MongoSession(sessionId, MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
given(this.converter.convert(sessionDocument, TypeDescriptor.valueOf(Document.class),
|
||||
TypeDescriptor.valueOf(MongoSession.class))).willReturn(mongoSession);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.data.mongo;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -35,6 +36,7 @@ import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -84,26 +86,8 @@ public class ReactiveMongoSessionRepositoryTest {
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextMatches((mongoSession) -> {
|
||||
assertThat(mongoSession.getId()).isNotEmpty();
|
||||
assertThat(mongoSession.getMaxInactiveInterval().getSeconds())
|
||||
.isEqualTo(ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL);
|
||||
return true;
|
||||
}) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateSessionWhenMaxInactiveIntervalNotDefined() {
|
||||
|
||||
// when
|
||||
this.repository.setMaxInactiveIntervalInSeconds(null);
|
||||
|
||||
// then
|
||||
this.repository.createSession() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNextMatches((mongoSession) -> {
|
||||
assertThat(mongoSession.getId()).isNotEmpty();
|
||||
assertThat(mongoSession.getMaxInactiveInterval().getSeconds())
|
||||
.isEqualTo(ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL);
|
||||
assertThat(mongoSession.getMaxInactiveInterval())
|
||||
.isEqualTo(Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS));
|
||||
return true;
|
||||
}) //
|
||||
.verifyComplete();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.session.data.mongo.config.annotation.web.http;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
@@ -32,11 +33,11 @@ import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JacksonMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.MongoIndexedSessionRepository;
|
||||
import org.springframework.session.data.mongo.MongoSession;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -112,9 +113,8 @@ public class MongoHttpSessionConfigurationTest {
|
||||
|
||||
MongoIndexedSessionRepository repository = this.context.getBean(MongoIndexedSessionRepository.class);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(repository, "maxInactiveIntervalInSeconds"))
|
||||
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
assertThat(repository).extracting("defaultMaxInactiveInterval")
|
||||
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,9 +124,8 @@ public class MongoHttpSessionConfigurationTest {
|
||||
|
||||
MongoIndexedSessionRepository repository = this.context.getBean(MongoIndexedSessionRepository.class);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(repository, "maxInactiveIntervalInSeconds"))
|
||||
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
assertThat(repository).extracting("defaultMaxInactiveInterval")
|
||||
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -161,7 +160,7 @@ public class MongoHttpSessionConfigurationTest {
|
||||
|
||||
MongoIndexedSessionRepository sessionRepository = this.context.getBean(MongoIndexedSessionRepository.class);
|
||||
|
||||
assertThat(sessionRepository).hasFieldOrPropertyWithValue("maxInactiveIntervalInSeconds", 10000);
|
||||
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ofSeconds(10000));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,7 +170,7 @@ public class MongoHttpSessionConfigurationTest {
|
||||
CustomIndexResolverConfigurationWithDefaultMongoSessionConverter.class);
|
||||
|
||||
MongoIndexedSessionRepository repository = this.context.getBean(MongoIndexedSessionRepository.class);
|
||||
IndexResolver<MongoSession> indexResolver = this.context.getBean(IndexResolver.class);
|
||||
IndexResolver<Session> indexResolver = this.context.getBean(IndexResolver.class);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(indexResolver).isNotNull();
|
||||
@@ -186,7 +185,7 @@ public class MongoHttpSessionConfigurationTest {
|
||||
CustomIndexResolverConfigurationWithProvidedMongoSessionConverter.class);
|
||||
|
||||
MongoIndexedSessionRepository repository = this.context.getBean(MongoIndexedSessionRepository.class);
|
||||
IndexResolver<MongoSession> indexResolver = this.context.getBean(IndexResolver.class);
|
||||
IndexResolver<Session> indexResolver = this.context.getBean(IndexResolver.class);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(indexResolver).isNotNull();
|
||||
@@ -194,6 +193,13 @@ public class MongoHttpSessionConfigurationTest {
|
||||
indexResolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
void importConfigAndCustomize() {
|
||||
registerAndRefresh(ImportConfigAndCustomizeConfiguration.class);
|
||||
MongoIndexedSessionRepository sessionRepository = this.context.getBean(MongoIndexedSessionRepository.class);
|
||||
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ZERO);
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
|
||||
this.context.register(annotatedClasses);
|
||||
@@ -259,7 +265,7 @@ public class MongoHttpSessionConfigurationTest {
|
||||
static class CustomMaxInactiveIntervalInSecondsSetConfiguration extends MongoHttpSessionConfiguration {
|
||||
|
||||
CustomMaxInactiveIntervalInSecondsSetConfiguration() {
|
||||
setMaxInactiveIntervalInSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS);
|
||||
setMaxInactiveInterval(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -286,19 +292,20 @@ public class MongoHttpSessionConfigurationTest {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoHttpSession
|
||||
static class SessionRepositoryCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
SessionRepositoryCustomizer<MongoIndexedSessionRepository> sessionRepositoryCustomizerOne() {
|
||||
return (sessionRepository) -> sessionRepository.setMaxInactiveIntervalInSeconds(0);
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
SessionRepositoryCustomizer<MongoIndexedSessionRepository> sessionRepositoryCustomizerTwo() {
|
||||
return (sessionRepository) -> sessionRepository.setMaxInactiveIntervalInSeconds(10000);
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ofSeconds(10000));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -309,7 +316,7 @@ public class MongoHttpSessionConfigurationTest {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
IndexResolver<MongoSession> indexResolver() {
|
||||
IndexResolver<Session> indexResolver() {
|
||||
return mock(IndexResolver.class);
|
||||
}
|
||||
|
||||
@@ -326,10 +333,21 @@ public class MongoHttpSessionConfigurationTest {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
IndexResolver<MongoSession> indexResolver() {
|
||||
IndexResolver<Session> indexResolver() {
|
||||
return mock(IndexResolver.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(MongoHttpSessionConfiguration.class)
|
||||
static class ImportConfigAndCustomizeConfiguration extends BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
SessionRepositoryCustomizer<MongoIndexedSessionRepository> sessionRepositoryCustomizer() {
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.session.data.mongo.config.annotation.web.reactive;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@@ -26,17 +27,19 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperations;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
|
||||
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JacksonMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
|
||||
import org.springframework.session.data.mongo.MongoSession;
|
||||
import org.springframework.session.data.mongo.ReactiveMongoSessionRepository;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
@@ -54,7 +57,7 @@ import static org.mockito.BDDMockito.verify;
|
||||
* Verify various configurations through {@link EnableSpringWebSession}.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Vedran Pavić
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
@@ -126,7 +129,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void overridingIntervalAndCollectionNameThroughAnnotationShouldWork() throws IllegalAccessException {
|
||||
void overridingIntervalAndCollectionNameThroughAnnotationShouldWork() {
|
||||
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
this.context.register(OverrideMongoParametersConfig.class);
|
||||
@@ -134,17 +137,8 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
ReactiveMongoSessionRepository repository = this.context.getBean(ReactiveMongoSessionRepository.class);
|
||||
|
||||
Field inactiveField = ReflectionUtils.findField(ReactiveMongoSessionRepository.class,
|
||||
"maxInactiveIntervalInSeconds");
|
||||
ReflectionUtils.makeAccessible(inactiveField);
|
||||
Integer inactiveSeconds = (Integer) inactiveField.get(repository);
|
||||
|
||||
Field collectionNameField = ReflectionUtils.findField(ReactiveMongoSessionRepository.class, "collectionName");
|
||||
ReflectionUtils.makeAccessible(collectionNameField);
|
||||
String collectionName = (String) collectionNameField.get(repository);
|
||||
|
||||
assertThat(inactiveSeconds).isEqualTo(123);
|
||||
assertThat(collectionName).isEqualTo("test-case");
|
||||
assertThat(repository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ofSeconds(123));
|
||||
assertThat(repository).extracting("collectionName").isEqualTo("test-case");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -172,7 +166,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
ReactiveMongoSessionRepository repository = this.context.getBean(ReactiveMongoSessionRepository.class);
|
||||
|
||||
assertThat(repository.getCollectionName()).isEqualTo("custom-collection");
|
||||
assertThat(repository.getMaxInactiveIntervalInSeconds()).isEqualTo(123);
|
||||
assertThat(repository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ofSeconds(123));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -184,7 +178,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
ReactiveMongoSessionRepository repository = this.context.getBean(ReactiveMongoSessionRepository.class);
|
||||
|
||||
assertThat(repository).hasFieldOrPropertyWithValue("maxInactiveIntervalInSeconds", 10000);
|
||||
assertThat(repository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ofSeconds(10000));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -195,7 +189,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
this.context.refresh();
|
||||
|
||||
ReactiveMongoSessionRepository repository = this.context.getBean(ReactiveMongoSessionRepository.class);
|
||||
IndexResolver<MongoSession> indexResolver = this.context.getBean(IndexResolver.class);
|
||||
IndexResolver<Session> indexResolver = this.context.getBean(IndexResolver.class);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(indexResolver).isNotNull();
|
||||
@@ -211,7 +205,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
this.context.refresh();
|
||||
|
||||
ReactiveMongoSessionRepository repository = this.context.getBean(ReactiveMongoSessionRepository.class);
|
||||
IndexResolver<MongoSession> indexResolver = this.context.getBean(IndexResolver.class);
|
||||
IndexResolver<Session> indexResolver = this.context.getBean(IndexResolver.class);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(indexResolver).isNotNull();
|
||||
@@ -219,6 +213,15 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
indexResolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
void importConfigAndCustomize() {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
this.context.register(ImportConfigAndCustomizeConfiguration.class);
|
||||
this.context.refresh();
|
||||
ReactiveMongoSessionRepository sessionRepository = this.context.getBean(ReactiveMongoSessionRepository.class);
|
||||
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflectively extract the {@link AbstractMongoSessionConverter} from the
|
||||
* {@link ReactiveMongoSessionRepository}. This is to avoid expanding the surface area
|
||||
@@ -239,6 +242,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
/**
|
||||
* A configuration with all the right parts.
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoWebSession
|
||||
static class GoodConfig {
|
||||
|
||||
@@ -252,11 +256,13 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
/**
|
||||
* A configuration where no {@link ReactiveMongoOperations} is defined. It's BAD!
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoWebSession
|
||||
static class BadConfig {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoWebSession
|
||||
static class OverrideSessionConverterConfig {
|
||||
|
||||
@@ -272,6 +278,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoWebSession(maxInactiveIntervalInSeconds = 123, collectionName = "test-case")
|
||||
static class OverrideMongoParametersConfig {
|
||||
|
||||
@@ -282,6 +289,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoWebSession
|
||||
static class ConfigWithReactiveAndImperativeMongoOperations {
|
||||
|
||||
@@ -308,13 +316,14 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableSpringWebSession
|
||||
static class CustomizedReactiveConfiguration extends ReactiveMongoWebSessionConfiguration {
|
||||
|
||||
CustomizedReactiveConfiguration() {
|
||||
|
||||
this.setCollectionName("custom-collection");
|
||||
this.setMaxInactiveIntervalInSeconds(123);
|
||||
this.setMaxInactiveInterval(Duration.ofSeconds(123));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -324,6 +333,7 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoWebSession
|
||||
static class SessionRepositoryCustomizerConfiguration {
|
||||
|
||||
@@ -335,17 +345,18 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
@Bean
|
||||
@Order(0)
|
||||
ReactiveSessionRepositoryCustomizer<ReactiveMongoSessionRepository> sessionRepositoryCustomizerOne() {
|
||||
return (sessionRepository) -> sessionRepository.setMaxInactiveIntervalInSeconds(0);
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
ReactiveSessionRepositoryCustomizer<ReactiveMongoSessionRepository> sessionRepositoryCustomizerTwo() {
|
||||
return (sessionRepository) -> sessionRepository.setMaxInactiveIntervalInSeconds(10000);
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ofSeconds(10000));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoWebSession
|
||||
static class CustomIndexResolverConfigurationWithDefaultMongoSessionConverter {
|
||||
|
||||
@@ -356,12 +367,13 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
IndexResolver<MongoSession> indexResolver() {
|
||||
IndexResolver<Session> indexResolver() {
|
||||
return mock(IndexResolver.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableMongoWebSession
|
||||
static class CustomIndexResolverConfigurationWithProvidedtMongoSessionConverter {
|
||||
|
||||
@@ -377,10 +389,26 @@ public class ReactiveMongoWebSessionConfigurationTest {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
IndexResolver<MongoSession> indexResolver() {
|
||||
IndexResolver<Session> indexResolver() {
|
||||
return mock(IndexResolver.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(ReactiveMongoWebSessionConfiguration.class)
|
||||
static class ImportConfigAndCustomizeConfiguration {
|
||||
|
||||
@Bean
|
||||
ReactiveMongoOperations operations() {
|
||||
return mock(ReactiveMongoOperations.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveSessionRepositoryCustomizer<ReactiveMongoSessionRepository> sessionRepositoryCustomizer() {
|
||||
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ dependencies {
|
||||
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.mockito:mockito-core"
|
||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||
testImplementation "org.springframework:spring-test"
|
||||
testImplementation "io.projectreactor:reactor-test"
|
||||
testImplementation "jakarta.servlet:jakarta.servlet-api"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
*/
|
||||
public abstract class AbstractRedisITests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:7.0.4-alpine";
|
||||
|
||||
protected static class BaseConfig {
|
||||
|
||||
@@ -42,8 +42,8 @@ public abstract class AbstractRedisITests {
|
||||
|
||||
@Bean
|
||||
public LettuceConnectionFactory redisConnectionFactory() {
|
||||
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(
|
||||
redisContainer().getContainerIpAddress(), redisContainer().getFirstMappedPort());
|
||||
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisContainer().getHost(),
|
||||
redisContainer().getFirstMappedPort());
|
||||
return new LettuceConnectionFactory(configuration);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.springframework.session.Session;
|
||||
import org.springframework.session.data.SessionEventRegistry;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository.RedisSession;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisIndexedHttpSession;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDestroyedEvent;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -111,10 +111,10 @@ class RedisIndexedSessionRepositoryITests extends AbstractRedisITests {
|
||||
this.repository.save(toSave);
|
||||
|
||||
assertThat(this.registry.receivedEvent(toSave.getId())).isTrue();
|
||||
assertThat(this.registry.<SessionCreatedEvent>getEvent(toSave.getId())).isInstanceOf(SessionCreatedEvent.class);
|
||||
assertThat(this.redis.boundSetOps(usernameSessionKey).members()).contains(toSave.getId());
|
||||
|
||||
Session session = this.repository.findById(toSave.getId());
|
||||
SessionCreatedEvent createdEvent = this.registry.getEvent(toSave.getId());
|
||||
Session session = createdEvent.getSession();
|
||||
|
||||
assertThat(session.getId()).isEqualTo(toSave.getId());
|
||||
assertThat(session.getAttributeNames()).isEqualTo(toSave.getAttributeNames());
|
||||
@@ -691,7 +691,7 @@ class RedisIndexedSessionRepositoryITests extends AbstractRedisITests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRedisHttpSession(redisNamespace = "RedisIndexedSessionRepositoryITests", enableIndexingAndEvents = true)
|
||||
@EnableRedisIndexedHttpSession(redisNamespace = "RedisIndexedSessionRepositoryITests")
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -44,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session> extends AbstractRedisITests {
|
||||
class EnableRedisIndexedHttpSessionExpireSessionDestroyedTests<S extends Session> extends AbstractRedisITests {
|
||||
|
||||
@Autowired
|
||||
private SessionRepository<S> repository;
|
||||
@@ -113,7 +113,7 @@ class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session> exten
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1, enableIndexingAndEvents = true)
|
||||
@EnableRedisIndexedHttpSession(maxInactiveIntervalInSeconds = 1)
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@Bean
|
||||
@@ -32,7 +32,7 @@ import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.session.data.redis.AbstractRedisITests;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisIndexedHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@@ -101,7 +101,7 @@ class RedisListenerContainerTaskExecutorITests extends AbstractRedisITests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRedisHttpSession(redisNamespace = "RedisListenerContainerTaskExecutorITests", enableIndexingAndEvents = true)
|
||||
@EnableRedisIndexedHttpSession(redisNamespace = "RedisListenerContainerTaskExecutorITests")
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This {@link ReactiveSessionRepository} implementation is kept in order to support
|
||||
* migration to {@link ReactiveRedisSessionRepository} in a backwards compatible manner.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0.0
|
||||
* @deprecated since 2.2.0 in favor of {@link ReactiveRedisSessionRepository}
|
||||
*/
|
||||
@Deprecated
|
||||
public class ReactiveRedisOperationsSessionRepository extends ReactiveRedisSessionRepository {
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveRedisOperationsSessionRepository} instance.
|
||||
* @param sessionRedisOperations the {@link ReactiveRedisOperations} to use for
|
||||
* managing sessions
|
||||
* @see ReactiveRedisSessionRepository#ReactiveRedisSessionRepository(ReactiveRedisOperations)
|
||||
*/
|
||||
public ReactiveRedisOperationsSessionRepository(ReactiveRedisOperations<String, Object> sessionRedisOperations) {
|
||||
super(sessionRedisOperations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redis flush mode. Default flush mode is {@link RedisFlushMode#ON_SAVE}.
|
||||
* @param redisFlushMode the new redis flush mode
|
||||
* @deprecated since 2.2.0 as support {@code IMMEDIATE} is removed
|
||||
*/
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -55,11 +55,7 @@ public class ReactiveRedisSessionRepository
|
||||
*/
|
||||
private String namespace = DEFAULT_NAMESPACE + ":";
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override the default value for
|
||||
* {@link RedisSession#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
@@ -79,16 +75,29 @@ public class ReactiveRedisSessionRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum inactive interval in seconds between requests before newly created
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* timeout. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
* time out. The default is 30 minutes.
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
public void setDefaultMaxInactiveInterval(Duration defaultMaxInactiveInterval) {
|
||||
Assert.notNull(defaultMaxInactiveInterval, "defaultMaxInactiveInterval must not be null");
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval in seconds
|
||||
* @deprecated since 3.0.0, in favor of
|
||||
* {@link #setDefaultMaxInactiveInterval(Duration)}
|
||||
*/
|
||||
@Deprecated(since = "3.0.0")
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
setDefaultMaxInactiveInterval(Duration.ofSeconds(defaultMaxInactiveInterval));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the save mode.
|
||||
* @param saveMode the save mode
|
||||
@@ -110,9 +119,7 @@ public class ReactiveRedisSessionRepository
|
||||
public Mono<RedisSession> createSession() {
|
||||
return Mono.defer(() -> {
|
||||
MapSession cached = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
|
||||
RedisSession session = new RedisSession(cached, true);
|
||||
return Mono.just(session);
|
||||
});
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
/**
|
||||
* Specifies when to write to the backing Redis instance.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.1
|
||||
* @deprecated since 2.2.0 in favor of {@link FlushMode}
|
||||
*/
|
||||
@Deprecated
|
||||
public enum RedisFlushMode {
|
||||
|
||||
/**
|
||||
* Only writes to Redis when
|
||||
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In
|
||||
* a web environment this is typically done as soon as the HTTP response is committed.
|
||||
*/
|
||||
ON_SAVE(FlushMode.ON_SAVE),
|
||||
|
||||
/**
|
||||
* Writes to Redis as soon as possible. For example
|
||||
* {@link SessionRepository#createSession()} will write the session to Redis. Another
|
||||
* example is that setting an attribute on the session will also write to Redis
|
||||
* immediately.
|
||||
*/
|
||||
IMMEDIATE(FlushMode.IMMEDIATE);
|
||||
|
||||
private final FlushMode flushMode;
|
||||
|
||||
RedisFlushMode(FlushMode flushMode) {
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
public FlushMode getFlushMode() {
|
||||
return this.flushMode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,8 @@ import java.util.Set;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.core.NestedExceptionUtils;
|
||||
@@ -38,6 +40,10 @@ import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.session.DelegatingIndexResolver;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.FlushMode;
|
||||
@@ -249,12 +255,18 @@ import org.springframework.util.Assert;
|
||||
* @since 2.2.0
|
||||
*/
|
||||
public class RedisIndexedSessionRepository
|
||||
implements FindByIndexNameSessionRepository<RedisIndexedSessionRepository.RedisSession>, MessageListener {
|
||||
implements FindByIndexNameSessionRepository<RedisIndexedSessionRepository.RedisSession>, MessageListener,
|
||||
InitializingBean, DisposableBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RedisIndexedSessionRepository.class);
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
/**
|
||||
* The default cron expression used for expired session cleanup job.
|
||||
*/
|
||||
public static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
|
||||
|
||||
/**
|
||||
* The default Redis database used by Spring Session.
|
||||
*/
|
||||
@@ -288,18 +300,14 @@ public class RedisIndexedSessionRepository
|
||||
|
||||
private byte[] expiredKeyPrefixBytes;
|
||||
|
||||
private final RedisOperations<Object, Object> sessionRedisOperations;
|
||||
private final RedisOperations<String, Object> sessionRedisOperations;
|
||||
|
||||
private final RedisSessionExpirationPolicy expirationPolicy;
|
||||
|
||||
private ApplicationEventPublisher eventPublisher = (event) -> {
|
||||
};
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override the default value for
|
||||
* {@link RedisSession#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
private IndexResolver<Session> indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
|
||||
|
||||
@@ -309,12 +317,16 @@ public class RedisIndexedSessionRepository
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
private String cleanupCron = DEFAULT_CLEANUP_CRON;
|
||||
|
||||
private ThreadPoolTaskScheduler taskScheduler;
|
||||
|
||||
/**
|
||||
* Creates a new instance. For an example, refer to the class level javadoc.
|
||||
* @param sessionRedisOperations the {@link RedisOperations} to use for managing the
|
||||
* sessions. Cannot be null.
|
||||
*/
|
||||
public RedisIndexedSessionRepository(RedisOperations<Object, Object> sessionRedisOperations) {
|
||||
public RedisIndexedSessionRepository(RedisOperations<String, Object> sessionRedisOperations) {
|
||||
Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null");
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations, this::getExpirationsKey,
|
||||
@@ -322,6 +334,28 @@ public class RedisIndexedSessionRepository
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (!Scheduled.CRON_DISABLED.equals(this.cleanupCron)) {
|
||||
this.taskScheduler = createTaskScheduler();
|
||||
this.taskScheduler.initialize();
|
||||
this.taskScheduler.schedule(this::cleanUpExpiredSessions, new CronTrigger(this.cleanupCron));
|
||||
}
|
||||
}
|
||||
|
||||
private static ThreadPoolTaskScheduler createTaskScheduler() {
|
||||
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
|
||||
taskScheduler.setThreadNamePrefix("spring-session-");
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (this.taskScheduler != null) {
|
||||
this.taskScheduler.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ApplicationEventPublisher} that is used to publish
|
||||
* {@link SessionDestroyedEvent}. The default is to not publish a
|
||||
@@ -335,16 +369,29 @@ public class RedisIndexedSessionRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum inactive interval in seconds between requests before newly created
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* timeout. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
* time out. The default is 30 minutes.
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
public void setDefaultMaxInactiveInterval(Duration defaultMaxInactiveInterval) {
|
||||
Assert.notNull(defaultMaxInactiveInterval, "defaultMaxInactiveInterval must not be null");
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 1800 (30 minutes).
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval in seconds
|
||||
* @deprecated since 3.0.0, in favor of
|
||||
* {@link #setDefaultMaxInactiveInterval(Duration)}
|
||||
*/
|
||||
@Deprecated(since = "3.0.0")
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
setDefaultMaxInactiveInterval(Duration.ofSeconds(defaultMaxInactiveInterval));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link IndexResolver} to use.
|
||||
* @param indexResolver the index resolver
|
||||
@@ -382,6 +429,21 @@ public class RedisIndexedSessionRepository
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cleanup cron expression.
|
||||
* @param cleanupCron the cleanup cron expression
|
||||
* @since 3.0.0
|
||||
* @see CronExpression
|
||||
* @see Scheduled#CRON_DISABLED
|
||||
*/
|
||||
public void setCleanupCron(String cleanupCron) {
|
||||
Assert.notNull(cleanupCron, "cleanupCron must not be null");
|
||||
if (!Scheduled.CRON_DISABLED.equals(cleanupCron)) {
|
||||
Assert.isTrue(CronExpression.isValidExpression(cleanupCron), "cleanupCron must be valid");
|
||||
}
|
||||
this.cleanupCron = cleanupCron;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the database index to use. Defaults to {@link #DEFAULT_DATABASE}.
|
||||
* @param database the database index to use
|
||||
@@ -406,21 +468,16 @@ public class RedisIndexedSessionRepository
|
||||
* Returns the {@link RedisOperations} used for sessions.
|
||||
* @return the {@link RedisOperations} used for sessions
|
||||
*/
|
||||
public RedisOperations<Object, Object> getSessionRedisOperations() {
|
||||
public RedisOperations<String, Object> getSessionRedisOperations() {
|
||||
return this.sessionRedisOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(RedisSession session) {
|
||||
session.save();
|
||||
if (session.isNew) {
|
||||
String sessionCreatedKey = getSessionCreatedChannel(session.getId());
|
||||
this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
|
||||
session.isNew = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanupExpiredSessions() {
|
||||
public void cleanUpExpiredSessions() {
|
||||
this.expirationPolicy.cleanExpiredSessions();
|
||||
}
|
||||
|
||||
@@ -436,6 +493,9 @@ public class RedisIndexedSessionRepository
|
||||
}
|
||||
String principalKey = getPrincipalKey(indexValue);
|
||||
Set<Object> sessionIds = this.sessionRedisOperations.boundSetOps(principalKey).members();
|
||||
if (sessionIds == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, RedisSession> sessions = new HashMap<>(sessionIds.size());
|
||||
for (Object id : sessionIds) {
|
||||
RedisSession session = findById((String) id);
|
||||
@@ -454,11 +514,11 @@ public class RedisIndexedSessionRepository
|
||||
* @return the Redis session
|
||||
*/
|
||||
private RedisSession getSession(String id, boolean allowExpired) {
|
||||
Map<Object, Object> entries = getSessionBoundHashOperations(id).entries();
|
||||
if (entries.isEmpty()) {
|
||||
Map<String, Object> entries = getSessionBoundHashOperations(id).entries();
|
||||
if ((entries == null) || entries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
MapSession loaded = loadSession(id, entries);
|
||||
MapSession loaded = new RedisSessionMapper(id).apply(entries);
|
||||
if (!allowExpired && loaded.isExpired()) {
|
||||
return null;
|
||||
}
|
||||
@@ -467,26 +527,6 @@ public class RedisIndexedSessionRepository
|
||||
return result;
|
||||
}
|
||||
|
||||
private MapSession loadSession(String id, Map<Object, Object> entries) {
|
||||
MapSession loaded = new MapSession(id);
|
||||
for (Map.Entry<Object, Object> entry : entries.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
if (RedisSessionMapper.CREATION_TIME_KEY.equals(key)) {
|
||||
loaded.setCreationTime(Instant.ofEpochMilli((long) entry.getValue()));
|
||||
}
|
||||
else if (RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY.equals(key)) {
|
||||
loaded.setMaxInactiveInterval(Duration.ofSeconds((int) entry.getValue()));
|
||||
}
|
||||
else if (RedisSessionMapper.LAST_ACCESSED_TIME_KEY.equals(key)) {
|
||||
loaded.setLastAccessedTime(Instant.ofEpochMilli((long) entry.getValue()));
|
||||
}
|
||||
else if (key.startsWith(RedisSessionMapper.ATTRIBUTE_PREFIX)) {
|
||||
loaded.setAttribute(key.substring(RedisSessionMapper.ATTRIBUTE_PREFIX.length()), entry.getValue());
|
||||
}
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(String sessionId) {
|
||||
RedisSession session = getSession(sessionId, true);
|
||||
@@ -507,9 +547,7 @@ public class RedisIndexedSessionRepository
|
||||
@Override
|
||||
public RedisSession createSession() {
|
||||
MapSession cached = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
|
||||
RedisSession session = new RedisSession(cached, true);
|
||||
session.flushImmediateIfNecessary();
|
||||
return session;
|
||||
@@ -521,9 +559,13 @@ public class RedisIndexedSessionRepository
|
||||
|
||||
if (ByteUtils.startsWith(messageChannel, this.sessionCreatedChannelPrefixBytes)) {
|
||||
// TODO: is this thread safe?
|
||||
String channel = new String(messageChannel);
|
||||
String sessionId = channel.substring(channel.lastIndexOf(":") + 1);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> loaded = (Map<Object, Object>) this.defaultSerializer.deserialize(message.getBody());
|
||||
handleCreated(loaded, new String(messageChannel));
|
||||
Map<String, Object> entries = (Map<String, Object>) this.defaultSerializer.deserialize(message.getBody());
|
||||
MapSession loaded = new RedisSessionMapper(sessionId).apply(entries);
|
||||
RedisSession session = new RedisSession(loaded, false);
|
||||
handleCreated(session);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -571,9 +613,7 @@ public class RedisIndexedSessionRepository
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCreated(Map<Object, Object> loaded, String channel) {
|
||||
String id = channel.substring(channel.lastIndexOf(":") + 1);
|
||||
Session session = loadSession(id, loaded);
|
||||
private void handleCreated(RedisSession session) {
|
||||
publishEvent(new SessionCreatedEvent(this, session));
|
||||
}
|
||||
|
||||
@@ -661,7 +701,7 @@ public class RedisIndexedSessionRepository
|
||||
* @param sessionId the id of the {@link Session} to work with
|
||||
* @return the {@link BoundHashOperations} to operate on a {@link Session}
|
||||
*/
|
||||
private BoundHashOperations<Object, Object, Object> getSessionBoundHashOperations(String sessionId) {
|
||||
private BoundHashOperations<String, String, Object> getSessionBoundHashOperations(String sessionId) {
|
||||
String key = getSessionKey(sessionId);
|
||||
return this.sessionRedisOperations.boundHashOps(key);
|
||||
}
|
||||
@@ -827,9 +867,12 @@ public class RedisIndexedSessionRepository
|
||||
.add(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isNew) {
|
||||
String sessionCreatedKey = getSessionCreatedChannel(getId());
|
||||
RedisIndexedSessionRepository.this.sessionRedisOperations.convertAndSend(sessionCreatedKey, this.delta);
|
||||
this.isNew = false;
|
||||
}
|
||||
this.delta = new HashMap<>(this.delta.size());
|
||||
|
||||
Long originalExpiration = (this.originalLastAccessTime != null)
|
||||
? this.originalLastAccessTime.plus(getMaxInactiveInterval()).toEpochMilli() : null;
|
||||
RedisIndexedSessionRepository.this.expirationPolicy.onExpirationUpdated(originalExpiration, this);
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This {@link SessionRepository} implementation is kept in order to support migration to
|
||||
* {@link RedisIndexedSessionRepository} in a backwards compatible manner.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
* @deprecated since 2.2.0 in favor of {@link RedisIndexedSessionRepository}
|
||||
*/
|
||||
@Deprecated
|
||||
public class RedisOperationsSessionRepository extends RedisIndexedSessionRepository {
|
||||
|
||||
/**
|
||||
* Creates a new instance. For an example, refer to the class level javadoc.
|
||||
* @param sessionRedisOperations the {@link RedisOperations} to use for managing the
|
||||
* sessions. Cannot be null.
|
||||
* @see RedisIndexedSessionRepository#RedisIndexedSessionRepository(RedisOperations)
|
||||
*/
|
||||
public RedisOperationsSessionRepository(RedisOperations<Object, Object> sessionRedisOperations) {
|
||||
super(sessionRedisOperations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the redis flush mode. Default flush mode is {@link RedisFlushMode#ON_SAVE}.
|
||||
* @param redisFlushMode the new redis flush mode
|
||||
* @deprecated since 2.2.0 in favor of {@link #setFlushMode(FlushMode)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
setFlushMode(redisFlushMode.getFlushMode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,13 +52,13 @@ final class RedisSessionExpirationPolicy {
|
||||
|
||||
private static final String SESSION_EXPIRES_PREFIX = "expires:";
|
||||
|
||||
private final RedisOperations<Object, Object> redis;
|
||||
private final RedisOperations<String, Object> redis;
|
||||
|
||||
private final Function<Long, String> lookupExpirationKey;
|
||||
|
||||
private final Function<String, String> lookupSessionKey;
|
||||
|
||||
RedisSessionExpirationPolicy(RedisOperations<Object, Object> sessionRedisOperations,
|
||||
RedisSessionExpirationPolicy(RedisOperations<String, Object> sessionRedisOperations,
|
||||
Function<Long, String> lookupExpirationKey, Function<String, String> lookupSessionKey) {
|
||||
super();
|
||||
this.redis = sessionRedisOperations;
|
||||
@@ -96,7 +96,7 @@ final class RedisSessionExpirationPolicy {
|
||||
}
|
||||
|
||||
String expireKey = getExpirationKey(toExpire);
|
||||
BoundSetOperations<Object, Object> expireOperations = this.redis.boundSetOps(expireKey);
|
||||
BoundSetOperations<String, Object> expireOperations = this.redis.boundSetOps(expireKey);
|
||||
expireOperations.add(keyToExpire);
|
||||
|
||||
long fiveMinutesAfterExpires = sessionExpireInSeconds + TimeUnit.MINUTES.toSeconds(5);
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.session.data.redis;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -68,7 +67,9 @@ public class RedisSessionRepository implements SessionRepository<RedisSessionRep
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default maxInactiveInterval.
|
||||
* Set the maximum inactive interval in seconds between requests before newly created
|
||||
* sessions will be invalidated. A negative time indicates that the session will never
|
||||
* time out. The default is 30 minutes.
|
||||
* @param defaultMaxInactiveInterval the default maxInactiveInterval
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(Duration defaultMaxInactiveInterval) {
|
||||
@@ -76,17 +77,6 @@ public class RedisSessionRepository implements SessionRepository<RedisSessionRep
|
||||
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key namespace.
|
||||
* @param keyNamespace the key namespace
|
||||
* @deprecated since 2.4.0 in favor of {@link #setRedisKeyNamespace(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setKeyNamespace(String keyNamespace) {
|
||||
Assert.hasText(keyNamespace, "keyNamespace must not be empty");
|
||||
this.keyNamespace = keyNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Redis key namespace.
|
||||
* @param namespace the Redis key namespace
|
||||
@@ -307,8 +297,8 @@ public class RedisSessionRepository implements SessionRepository<RedisSessionRep
|
||||
String key = getSessionKey(getId());
|
||||
RedisSessionRepository.this.sessionRedisOperations.opsForHash().putAll(key, new HashMap<>(this.delta));
|
||||
RedisSessionRepository.this.sessionRedisOperations.expireAt(key,
|
||||
Date.from(Instant.ofEpochMilli(getLastAccessedTime().toEpochMilli())
|
||||
.plusSeconds(getMaxInactiveInterval().getSeconds())));
|
||||
Instant.ofEpochMilli(getLastAccessedTime().toEpochMilli())
|
||||
.plusSeconds(getMaxInactiveInterval().getSeconds()));
|
||||
this.delta.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -65,13 +65,13 @@ public class ConfigureNotifyKeyspaceEventsAction implements ConfigureRedisAction
|
||||
customizedNotifyOptions += "x";
|
||||
}
|
||||
if (!notifyOptions.equals(customizedNotifyOptions)) {
|
||||
connection.setConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS, customizedNotifyOptions);
|
||||
connection.serverCommands().setConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS, customizedNotifyOptions);
|
||||
}
|
||||
}
|
||||
|
||||
private String getNotifyOptions(RedisConnection connection) {
|
||||
try {
|
||||
Properties config = connection.getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS);
|
||||
Properties config = connection.serverCommands().getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS);
|
||||
if (config.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.redis.config.annotation.web.http;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.data.redis.RedisSessionRepository;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base configuration class for Redis based {@link SessionRepository} implementations.
|
||||
*
|
||||
* @param <T> the {@link SessionRepository} type
|
||||
* @author Vedran Pavic
|
||||
* @since 3.0.0
|
||||
* @see RedisHttpSessionConfiguration
|
||||
* @see RedisIndexedHttpSessionConfiguration
|
||||
* @see SpringSessionRedisConnectionFactory
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(SpringHttpSessionConfiguration.class)
|
||||
public abstract class AbstractRedisHttpSessionConfiguration<T extends SessionRepository<? extends Session>>
|
||||
implements BeanClassLoaderAware {
|
||||
|
||||
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
|
||||
|
||||
private String redisNamespace = RedisSessionRepository.DEFAULT_KEY_NAMESPACE;
|
||||
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
private RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
|
||||
private List<SessionRepositoryCustomizer<T>> sessionRepositoryCustomizers;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public abstract T sessionRepository();
|
||||
|
||||
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
|
||||
this.maxInactiveInterval = maxInactiveInterval;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
||||
setMaxInactiveInterval(Duration.ofSeconds(maxInactiveIntervalInSeconds));
|
||||
}
|
||||
|
||||
protected Duration getMaxInactiveInterval() {
|
||||
return this.maxInactiveInterval;
|
||||
}
|
||||
|
||||
public void setRedisNamespace(String namespace) {
|
||||
Assert.hasText(namespace, "namespace must not be empty");
|
||||
this.redisNamespace = namespace;
|
||||
}
|
||||
|
||||
protected String getRedisNamespace() {
|
||||
return this.redisNamespace;
|
||||
}
|
||||
|
||||
public void setFlushMode(FlushMode flushMode) {
|
||||
Assert.notNull(flushMode, "flushMode must not be null");
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
protected FlushMode getFlushMode() {
|
||||
return this.flushMode;
|
||||
}
|
||||
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
Assert.notNull(saveMode, "saveMode must not be null");
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
protected SaveMode getSaveMode() {
|
||||
return this.saveMode;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setRedisConnectionFactory(
|
||||
@SpringSessionRedisConnectionFactory ObjectProvider<RedisConnectionFactory> springSessionRedisConnectionFactory,
|
||||
ObjectProvider<RedisConnectionFactory> redisConnectionFactory) {
|
||||
this.redisConnectionFactory = springSessionRedisConnectionFactory
|
||||
.getIfAvailable(redisConnectionFactory::getObject);
|
||||
}
|
||||
|
||||
protected RedisConnectionFactory getRedisConnectionFactory() {
|
||||
return this.redisConnectionFactory;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionDefaultRedisSerializer")
|
||||
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
|
||||
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||
}
|
||||
|
||||
protected RedisSerializer<Object> getDefaultRedisSerializer() {
|
||||
return this.defaultRedisSerializer;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<SessionRepositoryCustomizer<T>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected List<SessionRepositoryCustomizer<T>> getSessionRepositoryCustomizers() {
|
||||
return this.sessionRepositoryCustomizers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
protected RedisTemplate<String, Object> createRedisTemplate() {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
if (getDefaultRedisSerializer() != null) {
|
||||
redisTemplate.setDefaultSerializer(getDefaultRedisSerializer());
|
||||
}
|
||||
redisTemplate.setConnectionFactory(getRedisConnectionFactory());
|
||||
redisTemplate.setBeanClassLoader(this.classLoader);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.FlushMode;
|
||||
@@ -31,19 +30,17 @@ import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisSessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
|
||||
/**
|
||||
* Add this annotation to an {@code @Configuration} class to expose the
|
||||
* {@link SessionRepositoryFilter} as a bean named {@code springSessionRepositoryFilter}
|
||||
* and backed by Redis. In order to leverage the annotation, a single
|
||||
* {@link RedisConnectionFactory} must be provided. For example:
|
||||
* and backed by {@link RedisSessionRepository}. In order to leverage the annotation, a
|
||||
* single {@link RedisConnectionFactory} must be provided. For example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @Configuration(proxyBeanMethods = false)
|
||||
* @EnableRedisHttpSession
|
||||
* public class RedisHttpSessionConfig {
|
||||
*
|
||||
@@ -55,8 +52,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* More advanced configurations can extend {@link RedisHttpSessionConfiguration} or
|
||||
* {@link RedisIndexedHttpSessionConfiguration} instead.
|
||||
* More advanced configurations can extend {@link RedisHttpSessionConfiguration} instead.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
@@ -66,8 +62,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(RedisHttpSessionConfigurationSelector.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(RedisHttpSessionConfiguration.class)
|
||||
public @interface EnableRedisHttpSession {
|
||||
|
||||
/**
|
||||
@@ -87,21 +82,7 @@ public @interface EnableRedisHttpSession {
|
||||
* the applications and they could function within the same Redis instance.
|
||||
* @return the unique namespace for keys
|
||||
*/
|
||||
String redisNamespace() default RedisIndexedSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
/**
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
* updates the backing Redis when {@link SessionRepository#save(Session)} is invoked.
|
||||
* In a web environment this happens just before the HTTP response is committed.
|
||||
* <p>
|
||||
* Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
|
||||
* Session are immediately written to the Redis instance.
|
||||
* @return the {@link RedisFlushMode} to use
|
||||
* @since 1.1
|
||||
* @deprecated since 2.2.0 in favor of {@link #flushMode()}
|
||||
*/
|
||||
@Deprecated
|
||||
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
|
||||
String redisNamespace() default RedisSessionRepository.DEFAULT_KEY_NAMESPACE;
|
||||
|
||||
/**
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
@@ -123,13 +104,4 @@ public @interface EnableRedisHttpSession {
|
||||
*/
|
||||
SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Indicate whether the {@link SessionRepository} should publish session events and
|
||||
* support fetching sessions by index. If true, a
|
||||
* {@link RedisIndexedSessionRepository} will be used in place of
|
||||
* {@link RedisSessionRepository}. This will result in slower performance.
|
||||
* @return true if indexing and events should be enabled, false otherwise
|
||||
*/
|
||||
boolean enableIndexingAndEvents() default false;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.redis.config.annotation.web.http;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
|
||||
/**
|
||||
* Add this annotation to an {@code @Configuration} class to expose the
|
||||
* {@link SessionRepositoryFilter} as a bean named {@code springSessionRepositoryFilter}
|
||||
* and backed by {@link RedisIndexedSessionRepository}. In order to leverage the
|
||||
* annotation, a single {@link RedisConnectionFactory} must be provided. For example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration(proxyBeanMethods = false)
|
||||
* @EnableRedisIndexedHttpSession
|
||||
* public class RedisHttpSessionConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public LettuceConnectionFactory redisConnectionFactory() {
|
||||
* return new LettuceConnectionFactory();
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* More advanced configurations can extend {@link RedisIndexedHttpSessionConfiguration}
|
||||
* instead.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 3.0.0
|
||||
* @see EnableSpringHttpSession
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(RedisIndexedHttpSessionConfiguration.class)
|
||||
public @interface EnableRedisIndexedHttpSession {
|
||||
|
||||
/**
|
||||
* The session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
|
||||
* This should be a non-negative integer.
|
||||
* @return the seconds a session can be inactive before expiring
|
||||
*/
|
||||
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
/**
|
||||
* Defines a unique namespace for keys. The value is used to isolate sessions by
|
||||
* changing the prefix from default {@code spring:session:} to
|
||||
* {@code <redisNamespace>:}.
|
||||
* <p>
|
||||
* For example, if you had an application named "Application A" that needed to keep
|
||||
* the sessions isolated from "Application B" you could set two different values for
|
||||
* the applications and they could function within the same Redis instance.
|
||||
* @return the unique namespace for keys
|
||||
*/
|
||||
String redisNamespace() default RedisIndexedSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
/**
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
* updates the backing Redis when {@link SessionRepository#save(Session)} is invoked.
|
||||
* In a web environment this happens just before the HTTP response is committed.
|
||||
* <p>
|
||||
* Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
|
||||
* Session are immediately written to the Redis instance.
|
||||
* @return the {@link FlushMode} to use
|
||||
*/
|
||||
FlushMode flushMode() default FlushMode.ON_SAVE;
|
||||
|
||||
/**
|
||||
* Save mode for the session. The default is {@link SaveMode#ON_SET_ATTRIBUTE}, which
|
||||
* only saves changes made to session.
|
||||
* @return the save mode
|
||||
*/
|
||||
SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* The cron expression for expired session cleanup job. By default runs every minute.
|
||||
* @return the session cleanup cron expression
|
||||
*/
|
||||
String cleanupCron() default RedisIndexedSessionRepository.DEFAULT_CLEANUP_CRON;
|
||||
|
||||
}
|
||||
@@ -17,14 +17,8 @@
|
||||
package org.springframework.session.data.redis.config.annotation.web.http;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -33,25 +27,15 @@ import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisSessionRepository;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* Exposes the {@link SessionRepositoryFilter} as a bean named
|
||||
* {@code springSessionRepositoryFilter}. In order to use this a single
|
||||
* {@link RedisConnectionFactory} must be exposed as a Bean.
|
||||
* {@code springSessionRepositoryFilter} backed by {@link RedisSessionRepository}. In
|
||||
* order to use this a single {@link RedisConnectionFactory} must be exposed as a Bean.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Eddú Meléndez
|
||||
@@ -60,129 +44,47 @@ import org.springframework.util.StringValueResolver;
|
||||
* @see EnableRedisHttpSession
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
private String redisNamespace = RedisSessionRepository.DEFAULT_KEY_NAMESPACE;
|
||||
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
private RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
|
||||
private List<SessionRepositoryCustomizer<RedisSessionRepository>> sessionRepositoryCustomizers;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
public class RedisHttpSessionConfiguration extends AbstractRedisHttpSessionConfiguration<RedisSessionRepository>
|
||||
implements EmbeddedValueResolverAware, ImportAware {
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public RedisSessionRepository sessionRepository() {
|
||||
RedisTemplate<String, Object> redisTemplate = createRedisTemplate();
|
||||
RedisSessionRepository sessionRepository = new RedisSessionRepository(redisTemplate);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(Duration.ofSeconds(this.maxInactiveIntervalInSeconds));
|
||||
if (StringUtils.hasText(this.redisNamespace)) {
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(getMaxInactiveInterval());
|
||||
if (StringUtils.hasText(getRedisNamespace())) {
|
||||
sessionRepository.setRedisKeyNamespace(getRedisNamespace());
|
||||
}
|
||||
sessionRepository.setFlushMode(this.flushMode);
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
this.sessionRepositoryCustomizers
|
||||
sessionRepository.setFlushMode(getFlushMode());
|
||||
sessionRepository.setSaveMode(getSaveMode());
|
||||
getSessionRepositoryCustomizers()
|
||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
}
|
||||
|
||||
public void setRedisNamespace(String namespace) {
|
||||
this.redisNamespace = namespace;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
setFlushMode(redisFlushMode.getFlushMode());
|
||||
}
|
||||
|
||||
public void setFlushMode(FlushMode flushMode) {
|
||||
Assert.notNull(flushMode, "flushMode cannot be null");
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setRedisConnectionFactory(
|
||||
@SpringSessionRedisConnectionFactory ObjectProvider<RedisConnectionFactory> springSessionRedisConnectionFactory,
|
||||
ObjectProvider<RedisConnectionFactory> redisConnectionFactory) {
|
||||
RedisConnectionFactory redisConnectionFactoryToUse = springSessionRedisConnectionFactory.getIfAvailable();
|
||||
if (redisConnectionFactoryToUse == null) {
|
||||
redisConnectionFactoryToUse = redisConnectionFactory.getObject();
|
||||
}
|
||||
this.redisConnectionFactory = redisConnectionFactoryToUse;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionDefaultRedisSerializer")
|
||||
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
|
||||
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<SessionRepositoryCustomizer<RedisSessionRepository>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||
this.embeddedValueResolver = resolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
Map<String, Object> attributeMap = importMetadata
|
||||
.getAnnotationAttributes(EnableRedisHttpSession.class.getName());
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
|
||||
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
setMaxInactiveInterval(Duration.ofSeconds(attributes.<Integer>getNumber("maxInactiveIntervalInSeconds")));
|
||||
String redisNamespaceValue = attributes.getString("redisNamespace");
|
||||
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||
this.redisNamespace = this.embeddedValueResolver.resolveStringValue(redisNamespaceValue);
|
||||
setRedisNamespace(this.embeddedValueResolver.resolveStringValue(redisNamespaceValue));
|
||||
}
|
||||
FlushMode flushMode = attributes.getEnum("flushMode");
|
||||
RedisFlushMode redisFlushMode = attributes.getEnum("redisFlushMode");
|
||||
if (flushMode == FlushMode.ON_SAVE && redisFlushMode != RedisFlushMode.ON_SAVE) {
|
||||
flushMode = redisFlushMode.getFlushMode();
|
||||
}
|
||||
this.flushMode = flushMode;
|
||||
this.saveMode = attributes.getEnum("saveMode");
|
||||
}
|
||||
|
||||
private RedisTemplate<String, Object> createRedisTemplate() {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
if (this.defaultRedisSerializer != null) {
|
||||
redisTemplate.setDefaultSerializer(this.defaultRedisSerializer);
|
||||
}
|
||||
redisTemplate.setConnectionFactory(this.redisConnectionFactory);
|
||||
redisTemplate.setBeanClassLoader(this.classLoader);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
setFlushMode(attributes.getEnum("flushMode"));
|
||||
setSaveMode(attributes.getEnum("saveMode"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.data.redis.config.annotation.web.http;
|
||||
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* Dynamically determines which session repository configuration to include using the
|
||||
* {@link EnableRedisHttpSession} annotation.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 3.0
|
||||
*/
|
||||
final class RedisHttpSessionConfigurationSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importMetadata) {
|
||||
if (!importMetadata.hasAnnotation(EnableRedisHttpSession.class.getName())) {
|
||||
return new String[0];
|
||||
}
|
||||
EnableRedisHttpSession annotation = importMetadata.getAnnotations().get(EnableRedisHttpSession.class)
|
||||
.synthesize();
|
||||
if (annotation.enableIndexingAndEvents()) {
|
||||
return new String[] { RedisIndexedHttpSessionConfiguration.class.getName() };
|
||||
}
|
||||
else {
|
||||
return new String[] { RedisHttpSessionConfiguration.class.getName() };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,18 +16,15 @@
|
||||
|
||||
package org.springframework.session.data.redis.config.annotation.web.http;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@@ -45,93 +42,66 @@ import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.PatternTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.IndexResolver;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.SessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
|
||||
import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* Exposes the {@link SessionRepositoryFilter} as a bean named
|
||||
* {@code springSessionRepositoryFilter}. In order to use this a single
|
||||
* {@link RedisConnectionFactory} must be exposed as a Bean.
|
||||
* {@code springSessionRepositoryFilter} backed by {@link RedisIndexedSessionRepository}.
|
||||
* In order to use this a single {@link RedisConnectionFactory} must be exposed as a Bean.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 3.0
|
||||
* @author Vedran Pavic
|
||||
* @since 3.0.0
|
||||
* @see EnableRedisIndexedHttpSession
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class RedisIndexedHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
public class RedisIndexedHttpSessionConfiguration
|
||||
extends AbstractRedisHttpSessionConfiguration<RedisIndexedSessionRepository>
|
||||
implements EmbeddedValueResolverAware, ImportAware {
|
||||
|
||||
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
private String redisNamespace = RedisIndexedSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
private FlushMode flushMode = FlushMode.ON_SAVE;
|
||||
|
||||
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
|
||||
|
||||
private String cleanupCron = DEFAULT_CLEANUP_CRON;
|
||||
private String cleanupCron = RedisIndexedSessionRepository.DEFAULT_CLEANUP_CRON;
|
||||
|
||||
private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();
|
||||
|
||||
private RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private IndexResolver<Session> indexResolver;
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private Executor redisTaskExecutor;
|
||||
|
||||
private Executor redisSubscriptionExecutor;
|
||||
|
||||
private List<SessionRepositoryCustomizer<RedisIndexedSessionRepository>> sessionRepositoryCustomizers;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public RedisIndexedSessionRepository sessionRepository() {
|
||||
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
|
||||
RedisTemplate<String, Object> redisTemplate = createRedisTemplate();
|
||||
RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
|
||||
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
|
||||
if (this.indexResolver != null) {
|
||||
sessionRepository.setIndexResolver(this.indexResolver);
|
||||
}
|
||||
if (this.defaultRedisSerializer != null) {
|
||||
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
|
||||
if (getDefaultRedisSerializer() != null) {
|
||||
sessionRepository.setDefaultSerializer(getDefaultRedisSerializer());
|
||||
}
|
||||
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
if (StringUtils.hasText(this.redisNamespace)) {
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(getMaxInactiveInterval());
|
||||
if (StringUtils.hasText(getRedisNamespace())) {
|
||||
sessionRepository.setRedisKeyNamespace(getRedisNamespace());
|
||||
}
|
||||
sessionRepository.setFlushMode(this.flushMode);
|
||||
sessionRepository.setSaveMode(this.saveMode);
|
||||
sessionRepository.setFlushMode(getFlushMode());
|
||||
sessionRepository.setSaveMode(getSaveMode());
|
||||
sessionRepository.setCleanupCron(this.cleanupCron);
|
||||
int database = resolveDatabase();
|
||||
sessionRepository.setDatabase(database);
|
||||
this.sessionRepositoryCustomizers
|
||||
getSessionRepositoryCustomizers()
|
||||
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
|
||||
return sessionRepository;
|
||||
}
|
||||
@@ -140,7 +110,7 @@ public class RedisIndexedHttpSessionConfiguration extends SpringHttpSessionConfi
|
||||
public RedisMessageListenerContainer springSessionRedisMessageListenerContainer(
|
||||
RedisIndexedSessionRepository sessionRepository) {
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(this.redisConnectionFactory);
|
||||
container.setConnectionFactory(getRedisConnectionFactory());
|
||||
if (this.redisTaskExecutor != null) {
|
||||
container.setTaskExecutor(this.redisTaskExecutor);
|
||||
}
|
||||
@@ -157,30 +127,7 @@ public class RedisIndexedHttpSessionConfiguration extends SpringHttpSessionConfi
|
||||
|
||||
@Bean
|
||||
public InitializingBean enableRedisKeyspaceNotificationsInitializer() {
|
||||
return new EnableRedisKeyspaceNotificationsInitializer(this.redisConnectionFactory, this.configureRedisAction);
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
}
|
||||
|
||||
public void setRedisNamespace(String namespace) {
|
||||
this.redisNamespace = namespace;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
setFlushMode(redisFlushMode.getFlushMode());
|
||||
}
|
||||
|
||||
public void setFlushMode(FlushMode flushMode) {
|
||||
Assert.notNull(flushMode, "flushMode cannot be null");
|
||||
this.flushMode = flushMode;
|
||||
}
|
||||
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
this.saveMode = saveMode;
|
||||
return new EnableRedisKeyspaceNotificationsInitializer(getRedisConnectionFactory(), this.configureRedisAction);
|
||||
}
|
||||
|
||||
public void setCleanupCron(String cleanupCron) {
|
||||
@@ -197,23 +144,6 @@ public class RedisIndexedHttpSessionConfiguration extends SpringHttpSessionConfi
|
||||
this.configureRedisAction = configureRedisAction;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setRedisConnectionFactory(
|
||||
@SpringSessionRedisConnectionFactory ObjectProvider<RedisConnectionFactory> springSessionRedisConnectionFactory,
|
||||
ObjectProvider<RedisConnectionFactory> redisConnectionFactory) {
|
||||
RedisConnectionFactory redisConnectionFactoryToUse = springSessionRedisConnectionFactory.getIfAvailable();
|
||||
if (redisConnectionFactoryToUse == null) {
|
||||
redisConnectionFactoryToUse = redisConnectionFactory.getObject();
|
||||
}
|
||||
this.redisConnectionFactory = redisConnectionFactoryToUse;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionDefaultRedisSerializer")
|
||||
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
|
||||
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
@@ -236,63 +166,40 @@ public class RedisIndexedHttpSessionConfiguration extends SpringHttpSessionConfi
|
||||
this.redisSubscriptionExecutor = redisSubscriptionExecutor;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSessionRepositoryCustomizer(
|
||||
ObjectProvider<SessionRepositoryCustomizer<RedisIndexedSessionRepository>> sessionRepositoryCustomizers) {
|
||||
this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||
this.embeddedValueResolver = resolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
Map<String, Object> attributeMap = importMetadata
|
||||
.getAnnotationAttributes(EnableRedisHttpSession.class.getName());
|
||||
.getAnnotationAttributes(EnableRedisIndexedHttpSession.class.getName());
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
|
||||
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
setMaxInactiveInterval(Duration.ofSeconds(attributes.<Integer>getNumber("maxInactiveIntervalInSeconds")));
|
||||
String redisNamespaceValue = attributes.getString("redisNamespace");
|
||||
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||
this.redisNamespace = this.embeddedValueResolver.resolveStringValue(redisNamespaceValue);
|
||||
setRedisNamespace(this.embeddedValueResolver.resolveStringValue(redisNamespaceValue));
|
||||
}
|
||||
FlushMode flushMode = attributes.getEnum("flushMode");
|
||||
RedisFlushMode redisFlushMode = attributes.getEnum("redisFlushMode");
|
||||
if (flushMode == FlushMode.ON_SAVE && redisFlushMode != RedisFlushMode.ON_SAVE) {
|
||||
flushMode = redisFlushMode.getFlushMode();
|
||||
setFlushMode(attributes.getEnum("flushMode"));
|
||||
setSaveMode(attributes.getEnum("saveMode"));
|
||||
String cleanupCron = attributes.getString("cleanupCron");
|
||||
if (StringUtils.hasText(cleanupCron)) {
|
||||
setCleanupCron(cleanupCron);
|
||||
}
|
||||
this.flushMode = flushMode;
|
||||
this.saveMode = attributes.getEnum("saveMode");
|
||||
}
|
||||
|
||||
private RedisTemplate<Object, Object> createRedisTemplate() {
|
||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
if (this.defaultRedisSerializer != null) {
|
||||
redisTemplate.setDefaultSerializer(this.defaultRedisSerializer);
|
||||
}
|
||||
redisTemplate.setConnectionFactory(this.redisConnectionFactory);
|
||||
redisTemplate.setBeanClassLoader(this.classLoader);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
private int resolveDatabase() {
|
||||
if (ClassUtils.isPresent("io.lettuce.core.RedisClient", null)
|
||||
&& this.redisConnectionFactory instanceof LettuceConnectionFactory) {
|
||||
return ((LettuceConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||
&& getRedisConnectionFactory() instanceof LettuceConnectionFactory) {
|
||||
return ((LettuceConnectionFactory) getRedisConnectionFactory()).getDatabase();
|
||||
}
|
||||
if (ClassUtils.isPresent("redis.clients.jedis.Jedis", null)
|
||||
&& this.redisConnectionFactory instanceof JedisConnectionFactory) {
|
||||
return ((JedisConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||
&& getRedisConnectionFactory() instanceof JedisConnectionFactory) {
|
||||
return ((JedisConnectionFactory) getRedisConnectionFactory()).getDatabase();
|
||||
}
|
||||
return RedisIndexedSessionRepository.DEFAULT_DATABASE;
|
||||
}
|
||||
@@ -308,7 +215,7 @@ public class RedisIndexedHttpSessionConfiguration extends SpringHttpSessionConfi
|
||||
|
||||
private final RedisConnectionFactory connectionFactory;
|
||||
|
||||
private ConfigureRedisAction configure;
|
||||
private final ConfigureRedisAction configure;
|
||||
|
||||
EnableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory,
|
||||
ConfigureRedisAction configure) {
|
||||
@@ -337,25 +244,4 @@ public class RedisIndexedHttpSessionConfiguration extends SpringHttpSessionConfi
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration of scheduled job for cleaning up expired sessions.
|
||||
*/
|
||||
@EnableScheduling
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class SessionCleanupConfiguration implements SchedulingConfigurer {
|
||||
|
||||
private final RedisIndexedSessionRepository sessionRepository;
|
||||
|
||||
SessionCleanupConfiguration(RedisIndexedSessionRepository sessionRepository) {
|
||||
this.sessionRepository = sessionRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
taskRegistrar.addCronTask(this.sessionRepository::cleanupExpiredSessions,
|
||||
RedisIndexedHttpSessionConfiguration.this.cleanupCron);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,16 +22,12 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
|
||||
import org.springframework.session.data.redis.ReactiveRedisSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
/**
|
||||
@@ -41,7 +37,7 @@ import org.springframework.web.server.session.WebSessionManager;
|
||||
* {@link ReactiveRedisConnectionFactory} must be provided. For example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @Configuration(proxyBeanMethods = false)
|
||||
* @EnableRedisWebSession
|
||||
* public class RedisWebSessionConfig {
|
||||
*
|
||||
@@ -64,7 +60,6 @@ import org.springframework.web.server.session.WebSessionManager;
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(RedisWebSessionConfiguration.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public @interface EnableRedisWebSession {
|
||||
|
||||
/**
|
||||
@@ -86,20 +81,6 @@ public @interface EnableRedisWebSession {
|
||||
*/
|
||||
String redisNamespace() default ReactiveRedisSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
/**
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
* updates the backing Redis when {@link ReactiveSessionRepository#save(Session)} is
|
||||
* invoked. In a web environment this happens just before the HTTP response is
|
||||
* committed.
|
||||
* <p>
|
||||
* Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
|
||||
* Session are immediately written to the Redis instance.
|
||||
* @return the {@link RedisFlushMode} to use
|
||||
* @deprecated since 2.2.0 as support {@code IMMEDIATE} is removed
|
||||
*/
|
||||
@Deprecated
|
||||
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
|
||||
|
||||
/**
|
||||
* Save mode for the session. The default is {@link SaveMode#ON_SET_ATTRIBUTE}, which
|
||||
* only saves changes made to session.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.data.redis.config.annotation.web.server;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
@@ -41,9 +43,7 @@ import org.springframework.session.SaveMode;
|
||||
import org.springframework.session.config.ReactiveSessionRepositoryCustomizer;
|
||||
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
|
||||
import org.springframework.session.data.redis.ReactiveRedisSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
@@ -58,10 +58,10 @@ import org.springframework.web.server.session.WebSessionManager;
|
||||
* @see EnableRedisWebSession
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
@Import(SpringWebSessionConfiguration.class)
|
||||
public class RedisWebSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
|
||||
|
||||
private String redisNamespace = ReactiveRedisSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
@@ -81,7 +81,7 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
public ReactiveRedisSessionRepository sessionRepository() {
|
||||
ReactiveRedisTemplate<String, Object> reactiveRedisTemplate = createReactiveRedisTemplate();
|
||||
ReactiveRedisSessionRepository sessionRepository = new ReactiveRedisSessionRepository(reactiveRedisTemplate);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveInterval);
|
||||
if (StringUtils.hasText(this.redisNamespace)) {
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
@@ -91,19 +91,19 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
|
||||
this.maxInactiveInterval = maxInactiveInterval;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
setMaxInactiveInterval(Duration.ofSeconds(maxInactiveIntervalInSeconds));
|
||||
}
|
||||
|
||||
public void setRedisNamespace(String namespace) {
|
||||
this.redisNamespace = namespace;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRedisFlushMode(RedisFlushMode redisFlushMode) {
|
||||
Assert.notNull(redisFlushMode, "redisFlushMode cannot be null");
|
||||
}
|
||||
|
||||
public void setSaveMode(SaveMode saveMode) {
|
||||
this.saveMode = saveMode;
|
||||
}
|
||||
@@ -147,7 +147,10 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
Map<String, Object> attributeMap = importMetadata
|
||||
.getAnnotationAttributes(EnableRedisWebSession.class.getName());
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
|
||||
this.maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
|
||||
if (attributes == null) {
|
||||
return;
|
||||
}
|
||||
this.maxInactiveInterval = Duration.ofSeconds(attributes.<Integer>getNumber("maxInactiveIntervalInSeconds"));
|
||||
String redisNamespaceValue = attributes.getString("redisNamespace");
|
||||
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||
this.redisNamespace = this.embeddedValueResolver.resolveStringValue(redisNamespaceValue);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -103,9 +103,10 @@ class ReactiveRedisSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void customMaxInactiveInterval() {
|
||||
this.repository.setDefaultMaxInactiveInterval(600);
|
||||
Duration interval = Duration.ofMinutes(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(interval);
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(this.repository, "defaultMaxInactiveInterval")).isEqualTo(600);
|
||||
assertThat(this.repository).extracting("defaultMaxInactiveInterval").isEqualTo(interval);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -118,11 +119,11 @@ class ReactiveRedisSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void createSessionCustomMaxInactiveInterval() {
|
||||
this.repository.setDefaultMaxInactiveInterval(600);
|
||||
Duration interval = Duration.ofMinutes(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(interval);
|
||||
|
||||
StepVerifier.create(this.repository.createSession())
|
||||
.consumeNextWith(
|
||||
(session) -> assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(600)))
|
||||
.consumeNextWith((session) -> assertThat(session.getMaxInactiveInterval()).isEqualTo(interval))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,10 +30,11 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.redis.connection.DefaultMessage;
|
||||
@@ -43,6 +44,7 @@ import org.springframework.data.redis.core.BoundValueOperations;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.FlushMode;
|
||||
import org.springframework.session.MapSession;
|
||||
@@ -65,19 +67,20 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Mock
|
||||
private RedisOperations<Object, Object> redisOperations;
|
||||
private RedisOperations<String, Object> redisOperations;
|
||||
|
||||
@Mock
|
||||
private BoundValueOperations<Object, Object> boundValueOperations;
|
||||
private BoundValueOperations<String, Object> boundValueOperations;
|
||||
|
||||
@Mock
|
||||
private BoundHashOperations<Object, Object, Object> boundHashOperations;
|
||||
private BoundHashOperations<String, String, Object> boundHashOperations;
|
||||
|
||||
@Mock
|
||||
private BoundSetOperations<Object, Object> boundSetOperations;
|
||||
private BoundSetOperations<String, Object> boundSetOperations;
|
||||
|
||||
@Mock
|
||||
private ApplicationEventPublisher publisher;
|
||||
@@ -97,7 +100,6 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.redisRepository = new RedisIndexedSessionRepository(this.redisOperations);
|
||||
this.redisRepository.setDefaultSerializer(this.defaultSerializer);
|
||||
|
||||
@@ -115,7 +117,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void changeSessionIdWhenNotSaved() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -132,7 +134,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void changeSessionIdWhenSaved() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -156,16 +158,16 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void createSessionCustomMaxInactiveInterval() {
|
||||
int interval = 1;
|
||||
Duration interval = Duration.ofSeconds(1);
|
||||
this.redisRepository.setDefaultMaxInactiveInterval(interval);
|
||||
Session session = this.redisRepository.createSession();
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(interval));
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(interval);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveNewSession() {
|
||||
RedisSession session = this.redisRepository.createSession();
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -200,7 +202,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
.roundUpToNextMinute(RedisSessionExpirationPolicy.expiresInMillis(session));
|
||||
String destroyedTriggerKey = "spring:session:sessions:expires:" + session.getId();
|
||||
|
||||
given(this.redisOperations.boundHashOps(sessionKey)).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(sessionKey)).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(backgroundExpireKey)).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(destroyedTriggerKey)).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -223,7 +225,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached, false);
|
||||
session.setLastAccessedTime(session.getLastAccessedTime());
|
||||
|
||||
given(this.redisOperations.boundHashOps("spring:session:sessions:session-id"))
|
||||
given(this.redisOperations.<String, Object>boundHashOps("spring:session:sessions:session-id"))
|
||||
.willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps("spring:session:expirations:1404361860000"))
|
||||
.willReturn(this.boundSetOperations);
|
||||
@@ -244,7 +246,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
void saveLastAccessChanged() {
|
||||
RedisSession session = this.redisRepository.new RedisSession(this.cached, false);
|
||||
session.setLastAccessedTime(Instant.ofEpochMilli(12345678L));
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -259,7 +261,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false);
|
||||
session.setAttribute(attrName, "attrValue");
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -274,7 +276,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
String attrName = "attrName";
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false);
|
||||
session.removeAttribute(attrName);
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -287,7 +289,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
void saveExpired() {
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false);
|
||||
session.setMaxInactiveInterval(Duration.ZERO);
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
|
||||
this.redisRepository.save(session);
|
||||
@@ -309,18 +311,18 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void delete() {
|
||||
String attrName = "attrName";
|
||||
MapSession expected = new MapSession();
|
||||
expected.setLastAccessedTime(Instant.now().minusSeconds(60));
|
||||
expected.setAttribute(attrName, "attrValue");
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
Map map = map(RedisIndexedSessionRepository.getSessionAttrNameKey(attrName), expected.getAttribute(attrName),
|
||||
RedisSessionMapper.CREATION_TIME_KEY, expected.getCreationTime().toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) expected.getMaxInactiveInterval().getSeconds(),
|
||||
RedisSessionMapper.LAST_ACCESSED_TIME_KEY, expected.getLastAccessedTime().toEpochMilli());
|
||||
Map<String, Object> map = map(RedisIndexedSessionRepository.getSessionAttrNameKey(attrName),
|
||||
expected.getAttribute(attrName), RedisSessionMapper.CREATION_TIME_KEY,
|
||||
expected.getCreationTime().toEpochMilli(), RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
(int) expected.getMaxInactiveInterval().getSeconds(), RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
expected.getLastAccessedTime().toEpochMilli());
|
||||
given(this.boundHashOperations.entries()).willReturn(map);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
|
||||
@@ -334,7 +336,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void deleteNullSession() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
|
||||
String id = "abc";
|
||||
this.redisRepository.deleteById(id);
|
||||
@@ -343,17 +345,15 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void getSessionNotFound() {
|
||||
String id = "abc";
|
||||
given(this.redisOperations.boundHashOps(getKey(id))).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(id))).willReturn(this.boundHashOperations);
|
||||
given(this.boundHashOperations.entries()).willReturn(map());
|
||||
|
||||
assertThat(this.redisRepository.findById(id)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void getSessionFound() {
|
||||
String attribute1 = "attribute1";
|
||||
String attribute2 = "attribute2";
|
||||
@@ -361,8 +361,9 @@ class RedisIndexedSessionRepositoryTests {
|
||||
expected.setLastAccessedTime(Instant.now().minusSeconds(60));
|
||||
expected.setAttribute(attribute1, "test");
|
||||
expected.setAttribute(attribute2, null);
|
||||
given(this.redisOperations.boundHashOps(getKey(expected.getId()))).willReturn(this.boundHashOperations);
|
||||
Map map = map(RedisIndexedSessionRepository.getSessionAttrNameKey(attribute1),
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(expected.getId())))
|
||||
.willReturn(this.boundHashOperations);
|
||||
Map<String, Object> map = map(RedisIndexedSessionRepository.getSessionAttrNameKey(attribute1),
|
||||
expected.getAttribute(attribute1), RedisIndexedSessionRepository.getSessionAttrNameKey(attribute2),
|
||||
expected.getAttribute(attribute2), RedisSessionMapper.CREATION_TIME_KEY,
|
||||
expected.getCreationTime().toEpochMilli(), RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY,
|
||||
@@ -383,11 +384,12 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void getSessionExpired() {
|
||||
String expiredId = "expired-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(expiredId))).willReturn(this.boundHashOperations);
|
||||
Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(expiredId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
Map<String, Object> map = map(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli());
|
||||
given(this.boundHashOperations.entries()).willReturn(map);
|
||||
|
||||
@@ -395,13 +397,14 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void findByPrincipalNameExpired() {
|
||||
String expiredId = "expired-id";
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.boundSetOperations.members()).willReturn(Collections.singleton(expiredId));
|
||||
given(this.redisOperations.boundHashOps(getKey(expiredId))).willReturn(this.boundHashOperations);
|
||||
Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(expiredId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
Map<String, Object> map = map(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli());
|
||||
given(this.boundHashOperations.entries()).willReturn(map);
|
||||
|
||||
@@ -411,7 +414,6 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void findByPrincipalName() {
|
||||
Instant lastAccessed = Instant.now().minusMillis(10);
|
||||
Instant createdTime = lastAccessed.minusMillis(10);
|
||||
@@ -419,8 +421,9 @@ class RedisIndexedSessionRepositoryTests {
|
||||
String sessionId = "some-id";
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.boundSetOperations.members()).willReturn(Collections.singleton(sessionId));
|
||||
given(this.redisOperations.boundHashOps(getKey(sessionId))).willReturn(this.boundHashOperations);
|
||||
Map map = map(RedisSessionMapper.CREATION_TIME_KEY, createdTime.toEpochMilli(),
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(sessionId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
Map<String, Object> map = map(RedisSessionMapper.CREATION_TIME_KEY, createdTime.toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, (int) maxInactive.getSeconds(),
|
||||
RedisSessionMapper.LAST_ACCESSED_TIME_KEY, lastAccessed.toEpochMilli());
|
||||
given(this.boundHashOperations.entries()).willReturn(map);
|
||||
@@ -446,7 +449,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
Set<Object> expiredIds = new HashSet<>(Arrays.asList("expired-key1", "expired-key2"));
|
||||
given(this.boundSetOperations.members()).willReturn(expiredIds);
|
||||
|
||||
this.redisRepository.cleanupExpiredSessions();
|
||||
this.redisRepository.cleanUpExpiredSessions();
|
||||
|
||||
for (Object id : expiredIds) {
|
||||
String expiredKey = "spring:session:sessions:" + id;
|
||||
@@ -462,7 +465,10 @@ class RedisIndexedSessionRepositoryTests {
|
||||
String channel = "spring:session:event:0:created:" + session.getId();
|
||||
JdkSerializationRedisSerializer defaultSerailizer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setDefaultSerializer(defaultSerailizer);
|
||||
byte[] body = defaultSerailizer.serialize(new HashMap());
|
||||
Map<String, Object> map = map(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 0, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
|
||||
byte[] body = defaultSerailizer.serialize(map);
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
@@ -479,7 +485,10 @@ class RedisIndexedSessionRepositoryTests {
|
||||
byte[] pattern = "".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] body = new byte[0];
|
||||
String channel = "spring:session:event:0:created:" + session.getId();
|
||||
given(this.defaultSerializer.deserialize(body)).willReturn(new HashMap<String, Object>());
|
||||
Map<String, Object> map = map(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 0, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
|
||||
given(this.defaultSerializer.deserialize(body)).willReturn(map);
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
|
||||
@@ -491,11 +500,12 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void onMessageDeletedSessionFound() {
|
||||
String deletedId = "deleted-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(deletedId))).willReturn(this.boundHashOperations);
|
||||
Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 0, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(deletedId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
Map<String, Object> map = map(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 0, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
|
||||
given(this.boundHashOperations.entries()).willReturn(map);
|
||||
|
||||
@@ -518,10 +528,10 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void onMessageDeletedSessionNotFound() {
|
||||
String deletedId = "deleted-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(deletedId))).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(deletedId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
given(this.boundHashOperations.entries()).willReturn(map());
|
||||
|
||||
String channel = "__keyevent@0__:del";
|
||||
@@ -541,11 +551,12 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void onMessageExpiredSessionFound() {
|
||||
String expiredId = "expired-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(expiredId))).willReturn(this.boundHashOperations);
|
||||
Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(expiredId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
Map<String, Object> map = map(RedisSessionMapper.CREATION_TIME_KEY, Instant.EPOCH.toEpochMilli(),
|
||||
RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
|
||||
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
|
||||
given(this.boundHashOperations.entries()).willReturn(map);
|
||||
|
||||
@@ -568,10 +579,10 @@ class RedisIndexedSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void onMessageExpiredSessionNotFound() {
|
||||
String expiredId = "expired-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(expiredId))).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(getKey(expiredId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
given(this.boundHashOperations.entries()).willReturn(map());
|
||||
|
||||
String channel = "__keyevent@0__:expired";
|
||||
@@ -631,7 +642,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void flushModeImmediateCreate() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -650,10 +661,10 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test // gh-1409
|
||||
void flushModeImmediateCreateWithCustomMaxInactiveInterval() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
this.redisRepository.setDefaultMaxInactiveInterval(60);
|
||||
this.redisRepository.setDefaultMaxInactiveInterval(Duration.ofSeconds(60));
|
||||
this.redisRepository.setFlushMode(FlushMode.IMMEDIATE);
|
||||
this.redisRepository.createSession();
|
||||
Map<String, Object> delta = getDelta();
|
||||
@@ -663,7 +674,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void flushModeImmediateSetAttribute() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -680,7 +691,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void flushModeImmediateRemoveAttribute() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -698,7 +709,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void flushModeSetMaxInactiveIntervalInSeconds() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -714,7 +725,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void flushModeSetLastAccessedTime() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
|
||||
@@ -735,13 +746,32 @@ class RedisIndexedSessionRepositoryTests {
|
||||
.withMessage("flushMode cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setCleanupCronNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.redisRepository.setCleanupCron(null))
|
||||
.withMessage("cleanupCron must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setCleanupCronInvalid() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.redisRepository.setCleanupCron("test"))
|
||||
.withMessage("cleanupCron must be valid");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setCleanupCronDisabled() {
|
||||
this.redisRepository.setCleanupCron(Scheduled.CRON_DISABLED);
|
||||
this.redisRepository.afterPropertiesSet();
|
||||
assertThat(this.redisRepository).extracting("taskScheduler").isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void changeRedisNamespace() {
|
||||
String namespace = "foo:bar";
|
||||
this.redisRepository.setRedisKeyNamespace(namespace);
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false);
|
||||
session.setMaxInactiveInterval(Duration.ZERO);
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
|
||||
this.redisRepository.save(session);
|
||||
@@ -784,7 +814,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
MapSession session = this.cached;
|
||||
String channel = "spring:session:event:created:1:" + session.getId();
|
||||
byte[] body = serializer.serialize(new HashMap());
|
||||
byte[] body = serializer.serialize(new HashMap<>());
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
@@ -831,7 +861,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnSetAttribute() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
this.redisRepository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE);
|
||||
@@ -848,7 +878,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeOnGetAttribute() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
this.redisRepository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE);
|
||||
@@ -865,7 +895,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
void saveWithSaveModeAlways() {
|
||||
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.<String, Object>boundHashOps(anyString())).willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
|
||||
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
|
||||
this.redisRepository.setSaveMode(SaveMode.ALWAYS);
|
||||
@@ -884,7 +914,7 @@ class RedisIndexedSessionRepositoryTests {
|
||||
return "spring:session:sessions:" + id;
|
||||
}
|
||||
|
||||
private Map map(Object... objects) {
|
||||
private Map<String, Object> map(Object... objects) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
if (objects == null) {
|
||||
return result;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,8 +22,9 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.data.redis.core.BoundHashOperations;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
@@ -39,6 +40,7 @@ import static org.mockito.Mockito.verify;
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RedisSessionExpirationPolicyTests {
|
||||
|
||||
// Wed Apr 15 10:28:32 CDT 2015
|
||||
@@ -47,17 +49,17 @@ class RedisSessionExpirationPolicyTests {
|
||||
// Wed Apr 15 10:27:32 CDT 2015
|
||||
private static final Long ONE_MINUTE_AGO = 1429111652346L;
|
||||
|
||||
@Mock
|
||||
RedisOperations<Object, Object> sessionRedisOperations;
|
||||
@Mock(lenient = true)
|
||||
RedisOperations<String, Object> sessionRedisOperations;
|
||||
|
||||
@Mock
|
||||
BoundSetOperations<Object, Object> setOperations;
|
||||
BoundSetOperations<String, Object> setOperations;
|
||||
|
||||
@Mock
|
||||
BoundHashOperations<Object, Object, Object> hashOperations;
|
||||
BoundHashOperations<String, Object, Object> hashOperations;
|
||||
|
||||
@Mock
|
||||
BoundValueOperations<Object, Object> valueOperations;
|
||||
BoundValueOperations<String, Object> valueOperations;
|
||||
|
||||
private RedisSessionExpirationPolicy policy;
|
||||
|
||||
@@ -65,7 +67,6 @@ class RedisSessionExpirationPolicyTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
RedisIndexedSessionRepository repository = new RedisIndexedSessionRepository(this.sessionRedisOperations);
|
||||
this.policy = new RedisSessionExpirationPolicy(this.sessionRedisOperations, repository::getExpirationsKey,
|
||||
repository::getSessionKey);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,16 +20,16 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
@@ -52,13 +52,14 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RedisSessionRepositoryTests {
|
||||
|
||||
private static final String TEST_SESSION_ID = "session-id";
|
||||
|
||||
private static final String TEST_SESSION_KEY = getSessionKey(TEST_SESSION_ID);
|
||||
|
||||
@Mock
|
||||
@Mock(lenient = true)
|
||||
private RedisOperations<String, Object> sessionRedisOperations;
|
||||
|
||||
@Mock
|
||||
@@ -71,7 +72,6 @@ class RedisSessionRepositoryTests {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
given(this.sessionRedisOperations.<String, Object>opsForHash()).willReturn(this.sessionHashOperations);
|
||||
this.sessionRepository = new RedisSessionRepository(this.sessionRedisOperations);
|
||||
}
|
||||
@@ -96,36 +96,18 @@ class RedisSessionRepositoryTests {
|
||||
.withMessage("defaultMaxInactiveInterval must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setKeyNamespace_ValidNamespace_ShouldSetNamespace() {
|
||||
this.sessionRepository.setKeyNamespace("test:");
|
||||
assertThat(ReflectionTestUtils.getField(this.sessionRepository, "keyNamespace")).isEqualTo("test:");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRedisKeyNamespace_ValidNamespace_ShouldSetNamespace() {
|
||||
this.sessionRepository.setRedisKeyNamespace("test");
|
||||
assertThat(ReflectionTestUtils.getField(this.sessionRepository, "keyNamespace")).isEqualTo("test:");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setKeyNamespace_NullNamespace_ShouldThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.sessionRepository.setKeyNamespace(null))
|
||||
.withMessage("keyNamespace must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRedisKeyNamespace_NullNamespace_ShouldThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.sessionRepository.setRedisKeyNamespace(null))
|
||||
.withMessage("namespace must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setKeyNamespace_EmptyNamespace_ShouldThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.sessionRepository.setKeyNamespace(" "))
|
||||
.withMessage("keyNamespace must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRedisKeyNamespace_EmptyNamespace_ShouldThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.sessionRepository.setRedisKeyNamespace(" "))
|
||||
@@ -395,9 +377,9 @@ class RedisSessionRepositoryTests {
|
||||
return "spring:session:sessions:" + sessionId;
|
||||
}
|
||||
|
||||
private static Date getExpiry(RedisSession session) {
|
||||
return Date.from(Instant.ofEpochMilli(session.getLastAccessedTime().toEpochMilli())
|
||||
.plusSeconds(session.getMaxInactiveInterval().getSeconds()));
|
||||
private static Instant getExpiry(RedisSession session) {
|
||||
return Instant.ofEpochMilli(session.getLastAccessedTime().toEpochMilli())
|
||||
.plusSeconds(session.getMaxInactiveInterval().getSeconds());
|
||||
}
|
||||
|
||||
private static Map mapOf(Object... objects) {
|
||||
|
||||
@@ -20,13 +20,15 @@ import java.util.Properties;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisServerCommands;
|
||||
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -36,6 +38,7 @@ import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class EnableRedisKeyspaceNotificationsInitializerTests {
|
||||
|
||||
private static final String CONFIG_NOTIFY_KEYSPACE_EVENTS = "notify-keyspace-events";
|
||||
@@ -46,6 +49,9 @@ class EnableRedisKeyspaceNotificationsInitializerTests {
|
||||
@Mock
|
||||
RedisConnection connection;
|
||||
|
||||
@Mock
|
||||
RedisServerCommands commands;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<String> options;
|
||||
|
||||
@@ -53,8 +59,8 @@ class EnableRedisKeyspaceNotificationsInitializerTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
given(this.connectionFactory.getConnection()).willReturn(this.connection);
|
||||
given(this.connection.serverCommands()).willReturn(this.commands);
|
||||
|
||||
this.initializer = new RedisIndexedHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer(
|
||||
this.connectionFactory, new ConfigureNotifyKeyspaceEventsAction());
|
||||
@@ -102,7 +108,7 @@ class EnableRedisKeyspaceNotificationsInitializerTests {
|
||||
|
||||
this.initializer.afterPropertiesSet();
|
||||
|
||||
verify(this.connection, never()).setConfig(anyString(), anyString());
|
||||
verify(this.commands, never()).setConfig(anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,11 +162,11 @@ class EnableRedisKeyspaceNotificationsInitializerTests {
|
||||
|
||||
this.initializer.afterPropertiesSet();
|
||||
|
||||
verify(this.connection, never()).setConfig(anyString(), anyString());
|
||||
verify(this.commands, never()).setConfig(anyString(), anyString());
|
||||
}
|
||||
|
||||
private void assertOptionsContains(String... expectedValues) {
|
||||
verify(this.connection).setConfig(eq(CONFIG_NOTIFY_KEYSPACE_EVENTS), this.options.capture());
|
||||
verify(this.commands).setConfig(eq(CONFIG_NOTIFY_KEYSPACE_EVENTS), this.options.capture());
|
||||
for (String expectedValue : expectedValues) {
|
||||
assertThat(this.options.getValue()).contains(expectedValue);
|
||||
}
|
||||
@@ -170,7 +176,7 @@ class EnableRedisKeyspaceNotificationsInitializerTests {
|
||||
private void setConfigNotification(String value) {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(CONFIG_NOTIFY_KEYSPACE_EVENTS, value);
|
||||
given(this.connection.getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS)).willReturn(properties);
|
||||
given(this.commands.getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS)).willReturn(properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user