Compare commits

..

57 Commits

Author SHA1 Message Date
Rob Winch
a0d03adbe1 Release 3.0.0-RC2 2022-11-09 12:44:49 -06:00
Rob Winch
b229103d8c Remove spring-session-sample-javaconfig-rest
Works around a compatability issue for now.

Issue gh-2201
2022-11-09 12:44:31 -06:00
Rob Winch
94b441c676 Define websocket depenendencies
Issue gh-2204
2022-11-09 12:43:22 -06:00
Rob Winch
b2f10c6752 Next Development Version 2022-11-09 10:24:54 -06:00
Rob Winch
b3d228eb2e Release 3.0.0-RC2 2022-11-09 10:24:22 -06:00
Rob Winch
19dd3d8be1 Revert "Update for docs-build branch"
This reverts commit cd628fe5af.
2022-11-09 10:22:52 -06:00
Rob Winch
1aaffb28fc Update to Spring Security 6.0.0-RC2
Closes gh-2206
2022-11-09 09:56:24 -06:00
Rob Winch
75af61ca6c Update to Spring Data 2022.0.0-RC2
Closes gh-2205
2022-11-09 09:55:30 -06:00
Rob Winch
2fff593423 Update to Spring Framework 6.0.0-RC4
Closes gh-2204
2022-11-09 09:54:10 -06:00
Rob Winch
6a381d3226 Update to reactor-bom:2022.0.0
Closes gh-2203
2022-11-09 09:53:09 -06:00
Rob Winch
cd628fe5af Update for docs-build branch 2022-11-08 12:59:06 -06:00
Andy Wilkinson
ee4df64bb1 Align wth Servlet 6.0 API 2022-11-07 10:27:13 -06:00
Andy Wilkinson
d850762bce Avoid changing line endings of png and mmdb files 2022-11-07 10:05:58 -06:00
Vedran Pavic
f71d1d6ca4 Add Spring Session BOM module
With Spring Session Data Geode module being removed from the BOM, all of Spring Session's modules are now managed by this repository.

This means that the BOM itself can be moved to this repository, in order to simplify the overall project maintenance.

See gh-2195
2022-10-25 14:33:58 -05:00
Vedran Pavic
e5eeacec5f Update security config in samples
This commit updates security configuration in samples to:

- use AuthorizationFilter instead of FilterSecurityInterceptor
- update session creation policy in REST sample
2022-10-25 14:27:39 -05:00
Rob Winch
62ec64310b Next Development Version 2022-10-18 20:46:04 -05:00
Rob Winch
25d810eaa3 Release 3.0.0-RC1 2022-10-18 20:46:04 -05:00
Vedran Pavic
82db55c3f8 Polish SessionRepositoryFilter
This commit polishes SessionRepositoryFilter by simplifying some code paths.
2022-10-18 16:47:30 -05:00
Yanming Zhou
1f38a937bd Eliminate unnecessary sessionRepository::findById 2022-10-18 16:47:30 -05:00
Rob Winch
e027d2091b Update to Spring Security 6.0.0-RC1
Closes gh-2193
2022-10-18 16:44:06 -05:00
Vedran Pavic
bcbe53c3dd Make MongoSession package private
See gh-2170
2022-10-18 14:57:23 -05:00
Vedran Pavic
72742b52e3 Make MongoSessionUtils package private
See gh-2170
2022-10-18 14:57:23 -05:00
Vedran Pavic
1889a4c64e Use standard Spring utils in MongoDB module
This commit replaces usages of custom assert utility class with standard one from Spring Framework, and removes the custom utility.

See gh-2170
2022-10-18 14:57:23 -05:00
Jerome Prinet
3000f3198f Update Gradle Enterprise plugin to 3.11.2 2022-10-18 14:32:18 -05:00
Jerome Prinet
82a12afe93 Update Gradle Enterprise plugin to 3.11.1 2022-10-18 14:32:18 -05:00
Vedran Pavic
d2da662c3f Upgrade Gradle to 7.5.1 2022-10-18 14:31:30 -05:00
Vedran Pavic
95e2c9e42a Add .gitattributes 2022-10-18 14:31:30 -05:00
Rob Winch
ff8672b1d5 Update to mongodb 4.8.0-beta0
Closes gh-2182
2022-10-17 22:48:31 -05:00
Rob Winch
31d4a766eb Update to derby 10.16.1.1
Closes gh-2183
2022-10-17 22:48:18 -05:00
Rob Winch
ce332022de Update to lettuce 6.2.1.RELEASE
Closes gh-2184
2022-10-17 22:48:05 -05:00
Rob Winch
5c2ca6d5ac Update to hazelcast 5.1.4
Closes gh-2185
2022-10-17 22:47:54 -05:00
Rob Winch
bc93d80a17 Update to Spring Data 2022.0.0-RC1
Closes gh-2186
2022-10-17 22:47:42 -05:00
Rob Winch
fd835fd316 Update to Spring Framework 6.0.0-RC2
Closes gh-2187
2022-10-17 22:47:25 -05:00
Rob Winch
4e3ea616ba Update to Mockito 4.8.1
Closes gh-2188
2022-10-17 22:47:12 -05:00
Rob Winch
08654749ae Update to JUnit 5.9.1
Closes gh-2189
2022-10-17 22:47:07 -05:00
Rob Winch
88306d037d Update to Jackson 2.13.4.20221013
Closes gh-2190
2022-10-17 22:46:52 -05:00
Rob Winch
de32e4c501 Update to Reactor 2022.0.0-RC1
Closes gh-2191
2022-10-17 22:46:42 -05:00
Vedran Pavic
668f85788a Avoid inheritance in configuration classes
This commit restructures configuration classes to avoid inheritance, where possible. This should provide more flexibility when composing custom configurations.

Closes gh-1415
2022-10-17 21:52:58 -05:00
Vedran Pavic
fc81049cbe Upgrade Logback to 1.4.4 2022-10-17 15:05:24 -05:00
Vedran Pavic
fbb94b40e8 Fix Spring Security XML config 2022-10-17 15:05:24 -05:00
Vedran Pavic
009cb5b592 Fix max inactive interval setters backwards compatibility
This commit restores integer based max inactive interval setters across all session repositories, that were migrated to java.time in 6d74cf5f.

This change caused problems building our Spring Boot based samples, as Spring Boot auto-configuration has move to session repository customizer based approach and is therefore using max inactive interval setters directly on session repository implementations.
2022-10-07 07:56:25 -05:00
Vedran Pavic
525b841ad6 Build against Project Reactor 2022.0.0-RC1 snapshots 2022-10-06 21:52:31 -05:00
Vedran Pavic
185ea87ff4 Build against Spring Security 6.0.0-RC1 snapshots 2022-10-06 21:52:31 -05:00
Vedran Pavic
aebe5ece6f Build against Spring Data 2022.0.0-RC1 snapshots 2022-10-06 21:52:31 -05:00
Vedran Pavic
30b8e68c67 Build against Spring Framework 6.0.0-RC1 snapshots 2022-10-06 21:52:31 -05:00
Vedran Pavic
c2f8428df9 Upgrade Logback to 1.4.1 2022-10-06 10:28:41 -05:00
Vedran Pavic
b5197b8665 Polish RedisIndexedSessionRepository
This commit addresses code warnings due to nullability of return values.
2022-10-06 10:28:11 -05:00
Vedran Pavic
2406ec8302 Fix SessionCreatedEvent handling in RedisIndexedSessionRepository
At present, RedisIndexedSessionRepository publishes a SessionCreatedEvent backed by an empty MapSession instance. This happens because session delta has been cleared before publishing a message to the session created channel.

Fixes gh-1338
2022-10-06 10:28:11 -05:00
Vedran Pavic
6d74cf5f35 Use java.time in all session repositories and configurations
This commit reworks all session repository implementations and their respective configurations to use java.time for managing maxInactiveInterval and other temporal values.
2022-10-06 10:26:50 -05:00
Vedran Pavic
da8e5fbbac Ensure configuration classes can be used with @Import
This commit adds tests that verify that all Spring Session configuration classes can be used with @Import, and fixes JDBC and Hazelcast HttpSession configurations and Redis WebSession configuration.
2022-09-26 09:01:09 -05:00
Vedran Pavic
4b34f35b82 Replace JSR-250 annotations with standard Spring lifecycle callbacks 2022-09-26 08:59:06 -05:00
Vedran Pavic
954a40f5d1 Simplify expired session cleanup jobs
At present, RedisIndexedHttpSessionConfiguration and JdbcHttpSessionConfiguration include [at]EnableScheduling annotated inner configuration classes that configure expired session cleanup jobs. This approach silently opts in users into general purpose task scheduling support provided by Spring Framework, which isn't something a library should do. Ideally, session cleanup jobs should only require a single thread dedicated to their execution and also one that doesn't compete for resources with general purpose task scheduling.

This commit updates RedisIndexedSessionRepository and JdbcIndexedSessionRepository to have them manage their own ThreadPoolTaskScheduler for purposes of running expired session cleanup jobs.

Closes gh-2136
2022-09-23 15:23:29 -05:00
Vedran Pavic
fb66cf3150 Remove @Configuration meta-annotation from @Enable*Session annotations
Closes gh-1361
2022-09-22 16:13:16 -05:00
Vedran Pavic
a23090e7e5 Fix "Find by Username" sample
Spring Boot's auto-configuration support for Spring Session now uses RedisSessionRepository as the default Redis session repository, so applications that rely on indexed session repository need to opt into the previous default. One such example is our "Find by Username" sample.

This commit fixes "Find by Username" sample by opting into the indexed repository type using newly introduced spring.session.redis.repository-type property.
2022-09-22 14:33:52 -05:00
Rob Winch
beaca53d44 Fix major/minor segments 2022-09-21 14:47:06 -05:00
Marcus Da Coregio
baed403a6a Polish SessionJdbcRuntimeHints
Issue gh-2103
2022-09-21 09:12:24 -03:00
Rob Winch
cd51c36dc0 Next Development Version 2022-09-20 14:59:00 -05:00
115 changed files with 1094 additions and 1591 deletions

9
.gitattributes vendored Normal file
View File

@@ -0,0 +1,9 @@
* text eol=lf
*.bat text eol=crlf
*.jar binary
*.png binary
*.mmdb binary

View File

@@ -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-M4
version=3.0.0-RC2

View File

@@ -1,19 +1,19 @@
dependencyManagement {
imports {
mavenBom 'io.projectreactor:reactor-bom:2022.0.0-M6'
mavenBom 'com.fasterxml.jackson:jackson-bom:2.13.4'
mavenBom 'org.junit:junit-bom:5.9.0'
mavenBom 'org.mockito:mockito-bom:4.8.0'
mavenBom 'org.springframework:spring-framework-bom:6.0.0-M6'
mavenBom 'org.springframework.data:spring-data-bom:2022.0.0-M6'
mavenBom 'org.springframework.security:spring-security-bom:6.0.0-M7'
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-RC4'
mavenBom 'org.springframework.data:spring-data-bom:2022.0.0-RC2'
mavenBom 'org.springframework.security:spring-security-bom:6.0.0-RC2'
mavenBom 'org.testcontainers:testcontainers-bom:1.17.3'
}
dependencies {
dependency 'com.hazelcast:hazelcast:5.1.3'
dependency 'com.hazelcast:hazelcast:5.1.4'
dependency 'org.aspectj:aspectjweaver:1.9.9.1'
dependency 'ch.qos.logback:logback-core:1.2.11'
dependency 'ch.qos.logback:logback-core:1.4.4'
dependency 'com.google.code.findbugs:jsr305:3.0.2'
dependency 'com.h2database:h2:2.1.214'
dependency 'com.ibm.db2:jcc:11.5.7.0'
@@ -21,16 +21,20 @@ dependencyManagement {
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.2.0.RELEASE'
dependency 'jakarta.annotation:jakarta.annotation-api:2.0.0'
dependency 'jakarta.servlet:jakarta.servlet-api:5.0.0'
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'
dependency 'org.apache.derby:derby:10.14.2.0'
dependencySet(group: 'org.apache.derby', version: '10.16.1.1') {
entry 'derby'
entry 'derbytools'
}
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.7.1') {
dependencySet(group: 'org.mongodb', version: '4.8.0-beta0') {
entry 'mongodb-driver-core'
entry 'mongodb-driver-sync'
entry 'mongodb-driver-reactivestreams'

Binary file not shown.

View File

@@ -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
View File

@@ -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
View File

@@ -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

View File

@@ -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'

View 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)
}
}
}
}

View File

@@ -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"

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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);
});
}

View File

@@ -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 {
}

View File

@@ -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);

View File

@@ -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 {
}

View File

@@ -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();
}

View File

@@ -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,7 +38,6 @@ 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);
@@ -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");
}
};
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -54,7 +54,7 @@ class WebSessionSecurityRuntimeHintsTests {
classUtilsMock.when(() -> ClassUtils.isPresent(eq("org.springframework.web.server.WebSession"), any()))
.thenReturn(false);
this.webSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
assertThat(this.hints.serialization().javaSerialization()).isEmpty();
assertThat(this.hints.serialization().javaSerializationHints()).isEmpty();
}
}
@@ -66,7 +66,7 @@ class WebSessionSecurityRuntimeHintsTests {
.isPresent(eq("org.springframework.security.web.server.csrf.DefaultCsrfToken"), any()))
.thenReturn(false);
this.webSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
assertThat(this.hints.serialization().javaSerialization()).isEmpty();
assertThat(this.hints.serialization().javaSerializationHints()).isEmpty();
}
}

View File

@@ -65,7 +65,7 @@ class HttpSessionSecurityRuntimeHintsTests {
classUtilsMock.when(() -> ClassUtils.isPresent(eq("jakarta.servlet.http.HttpSession"), any()))
.thenReturn(false);
this.httpSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
assertThat(this.hints.serialization().javaSerialization()).isEmpty();
assertThat(this.hints.serialization().javaSerializationHints()).isEmpty();
}
}
@@ -76,7 +76,7 @@ class HttpSessionSecurityRuntimeHintsTests {
() -> ClassUtils.isPresent(eq("org.springframework.security.web.csrf.DefaultCsrfToken"), any()))
.thenReturn(false);
this.httpSessionSecurityRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
assertThat(this.hints.serialization().javaSerialization()).isEmpty();
assertThat(this.hints.serialization().javaSerializationHints()).isEmpty();
}
}

View File

@@ -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 {

View File

@@ -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,7 +39,6 @@ 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;
@@ -316,52 +314,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() {
@@ -637,27 +589,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
@@ -741,23 +672,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() {
@@ -775,23 +689,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() {
@@ -809,23 +706,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() {
@@ -843,23 +723,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() {
@@ -921,20 +784,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() {
@@ -1327,8 +1176,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());

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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() {
}

View File

@@ -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() {

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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());

View File

@@ -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);
});

View File

@@ -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.
*/
@@ -295,11 +307,7 @@ public class RedisIndexedSessionRepository
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,6 +317,10 @@ 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
@@ -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
@@ -413,14 +475,9 @@ public class RedisIndexedSessionRepository
@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);
@@ -455,10 +515,10 @@ public class RedisIndexedSessionRepository
*/
private RedisSession getSession(String id, boolean allowExpired) {
Map<String, Object> entries = getSessionBoundHashOperations(id).entries();
if (entries.isEmpty()) {
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<String, Object> entries) {
MapSession loaded = new MapSession(id);
for (Map.Entry<String, Object> entry : entries.entrySet()) {
String key = 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<String, Object> loaded = (Map<String, 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<String, 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));
}
@@ -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);

View File

@@ -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) {
@@ -296,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();
}

View File

@@ -16,6 +16,7 @@
package org.springframework.session.data.redis.config.annotation.web.http;
import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;
@@ -24,6 +25,7 @@ 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;
@@ -50,10 +52,11 @@ import org.springframework.util.Assert;
* @see SpringSessionRedisConnectionFactory
*/
@Configuration(proxyBeanMethods = false)
@Import(SpringHttpSessionConfiguration.class)
public abstract class AbstractRedisHttpSessionConfiguration<T extends SessionRepository<? extends Session>>
extends SpringHttpSessionConfiguration implements BeanClassLoaderAware {
implements BeanClassLoaderAware {
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
private String redisNamespace = RedisSessionRepository.DEFAULT_KEY_NAMESPACE;
@@ -71,12 +74,17 @@ public abstract class AbstractRedisHttpSessionConfiguration<T extends SessionRep
public abstract T sessionRepository();
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
this.maxInactiveInterval = maxInactiveInterval;
}
protected Integer getMaxInactiveIntervalInSeconds() {
return this.maxInactiveIntervalInSeconds;
@Deprecated
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
setMaxInactiveInterval(Duration.ofSeconds(maxInactiveIntervalInSeconds));
}
protected Duration getMaxInactiveInterval() {
return this.maxInactiveInterval;
}
public void setRedisNamespace(String namespace) {

View File

@@ -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;
@@ -41,7 +40,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
* single {@link RedisConnectionFactory} must be provided. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;Configuration(proxyBeanMethods = false)
* &#064;EnableRedisHttpSession
* public class RedisHttpSessionConfig {
*
@@ -64,7 +63,6 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
@Target(ElementType.TYPE)
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration(proxyBeanMethods = false)
public @interface EnableRedisHttpSession {
/**

View File

@@ -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;
@@ -41,7 +40,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
* annotation, a single {@link RedisConnectionFactory} must be provided. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;Configuration(proxyBeanMethods = false)
* &#064;EnableRedisIndexedHttpSession
* public class RedisHttpSessionConfig {
*
@@ -64,7 +63,6 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
@Target(ElementType.TYPE)
@Documented
@Import(RedisIndexedHttpSessionConfiguration.class)
@Configuration(proxyBeanMethods = false)
public @interface EnableRedisIndexedHttpSession {
/**
@@ -108,6 +106,6 @@ public @interface EnableRedisIndexedHttpSession {
* The cron expression for expired session cleanup job. By default runs every minute.
* @return the session cleanup cron expression
*/
String cleanupCron() default RedisIndexedHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
String cleanupCron() default RedisIndexedSessionRepository.DEFAULT_CLEANUP_CRON;
}

View File

@@ -54,7 +54,7 @@ public class RedisHttpSessionConfiguration extends AbstractRedisHttpSessionConfi
public RedisSessionRepository sessionRepository() {
RedisTemplate<String, Object> redisTemplate = createRedisTemplate();
RedisSessionRepository sessionRepository = new RedisSessionRepository(redisTemplate);
sessionRepository.setDefaultMaxInactiveInterval(Duration.ofSeconds(getMaxInactiveIntervalInSeconds()));
sessionRepository.setDefaultMaxInactiveInterval(getMaxInactiveInterval());
if (StringUtils.hasText(getRedisNamespace())) {
sessionRepository.setRedisKeyNamespace(getRedisNamespace());
}
@@ -78,7 +78,7 @@ public class RedisHttpSessionConfiguration extends AbstractRedisHttpSessionConfi
if (attributes == null) {
return;
}
setMaxInactiveIntervalInSeconds(attributes.getNumber("maxInactiveIntervalInSeconds"));
setMaxInactiveInterval(Duration.ofSeconds(attributes.<Integer>getNumber("maxInactiveIntervalInSeconds")));
String redisNamespaceValue = attributes.getString("redisNamespace");
if (StringUtils.hasText(redisNamespaceValue)) {
setRedisNamespace(this.embeddedValueResolver.resolveStringValue(redisNamespaceValue));

View File

@@ -16,6 +16,7 @@
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.Map;
@@ -41,9 +42,6 @@ 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.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.session.IndexResolver;
import org.springframework.session.Session;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
@@ -68,9 +66,7 @@ public class RedisIndexedHttpSessionConfiguration
extends AbstractRedisHttpSessionConfiguration<RedisIndexedSessionRepository>
implements EmbeddedValueResolverAware, ImportAware {
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
private String cleanupCron = DEFAULT_CLEANUP_CRON;
private String cleanupCron = RedisIndexedSessionRepository.DEFAULT_CLEANUP_CRON;
private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();
@@ -96,12 +92,13 @@ public class RedisIndexedHttpSessionConfiguration
if (getDefaultRedisSerializer() != null) {
sessionRepository.setDefaultSerializer(getDefaultRedisSerializer());
}
sessionRepository.setDefaultMaxInactiveInterval(getMaxInactiveIntervalInSeconds());
sessionRepository.setDefaultMaxInactiveInterval(getMaxInactiveInterval());
if (StringUtils.hasText(getRedisNamespace())) {
sessionRepository.setRedisKeyNamespace(getRedisNamespace());
}
sessionRepository.setFlushMode(getFlushMode());
sessionRepository.setSaveMode(getSaveMode());
sessionRepository.setCleanupCron(this.cleanupCron);
int database = resolveDatabase();
sessionRepository.setDatabase(database);
getSessionRepositoryCustomizers()
@@ -182,7 +179,7 @@ public class RedisIndexedHttpSessionConfiguration
if (attributes == null) {
return;
}
setMaxInactiveIntervalInSeconds(attributes.getNumber("maxInactiveIntervalInSeconds"));
setMaxInactiveInterval(Duration.ofSeconds(attributes.<Integer>getNumber("maxInactiveIntervalInSeconds")));
String redisNamespaceValue = attributes.getString("redisNamespace");
if (StringUtils.hasText(redisNamespaceValue)) {
setRedisNamespace(this.embeddedValueResolver.resolveStringValue(redisNamespaceValue));
@@ -247,25 +244,4 @@ public class RedisIndexedHttpSessionConfiguration
}
/**
* 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);
}
}
}

View File

@@ -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.ReactiveRedisConnectionFactory;
import org.springframework.session.MapSession;
@@ -38,7 +37,7 @@ import org.springframework.web.server.session.WebSessionManager;
* {@link ReactiveRedisConnectionFactory} must be provided. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;Configuration(proxyBeanMethods = false)
* &#064;EnableRedisWebSession
* public class RedisWebSessionConfig {
*
@@ -61,7 +60,6 @@ import org.springframework.web.server.session.WebSessionManager;
@Target(ElementType.TYPE)
@Documented
@Import(RedisWebSessionConfiguration.class)
@Configuration(proxyBeanMethods = false)
public @interface EnableRedisWebSession {
/**

View File

@@ -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;
@@ -56,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;
@@ -79,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);
}
@@ -89,8 +91,13 @@ 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) {
@@ -140,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);

View File

@@ -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();
}

View File

@@ -44,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;
@@ -157,10 +158,10 @@ 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
@@ -310,7 +311,6 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void delete() {
String attrName = "attrName";
MapSession expected = new MapSession();
@@ -318,10 +318,11 @@ class RedisIndexedSessionRepositoryTests {
expected.setAttribute(attrName, "attrValue");
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);
@@ -344,7 +345,6 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void getSessionNotFound() {
String id = "abc";
given(this.redisOperations.<String, Object>boundHashOps(getKey(id))).willReturn(this.boundHashOperations);
@@ -354,7 +354,6 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void getSessionFound() {
String attribute1 = "attribute1";
String attribute2 = "attribute2";
@@ -364,7 +363,7 @@ class RedisIndexedSessionRepositoryTests {
expected.setAttribute(attribute2, null);
given(this.redisOperations.<String, Object>boundHashOps(getKey(expected.getId())))
.willReturn(this.boundHashOperations);
Map map = map(RedisIndexedSessionRepository.getSessionAttrNameKey(attribute1),
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,
@@ -385,12 +384,12 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void getSessionExpired() {
String expiredId = "expired-id";
given(this.redisOperations.<String, Object>boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
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);
@@ -398,14 +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.<String, Object>boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
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);
@@ -415,7 +414,6 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void findByPrincipalName() {
Instant lastAccessed = Instant.now().minusMillis(10);
Instant createdTime = lastAccessed.minusMillis(10);
@@ -425,7 +423,7 @@ class RedisIndexedSessionRepositoryTests {
given(this.boundSetOperations.members()).willReturn(Collections.singleton(sessionId));
given(this.redisOperations.<String, Object>boundHashOps(getKey(sessionId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisSessionMapper.CREATION_TIME_KEY, createdTime.toEpochMilli(),
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);
@@ -451,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;
@@ -467,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);
@@ -484,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);
@@ -496,12 +500,12 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void onMessageDeletedSessionFound() {
String deletedId = "deleted-id";
given(this.redisOperations.<String, Object>boundHashOps(getKey(deletedId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 0, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
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);
@@ -524,7 +528,6 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void onMessageDeletedSessionNotFound() {
String deletedId = "deleted-id";
given(this.redisOperations.<String, Object>boundHashOps(getKey(deletedId)))
@@ -548,12 +551,12 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void onMessageExpiredSessionFound() {
String expiredId = "expired-id";
given(this.redisOperations.<String, Object>boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisSessionMapper.MAX_INACTIVE_INTERVAL_KEY, 1, RedisSessionMapper.LAST_ACCESSED_TIME_KEY,
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);
@@ -576,7 +579,6 @@ class RedisIndexedSessionRepositoryTests {
}
@Test
@SuppressWarnings("unchecked")
void onMessageExpiredSessionNotFound() {
String expiredId = "expired-id";
given(this.redisOperations.<String, Object>boundHashOps(getKey(expiredId)))
@@ -662,7 +664,7 @@ class RedisIndexedSessionRepositoryTests {
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();
@@ -744,6 +746,25 @@ 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";
@@ -793,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));
@@ -893,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;

View File

@@ -20,7 +20,6 @@ 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;
@@ -378,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) {

View File

@@ -27,6 +27,7 @@ import org.springframework.beans.factory.BeanCreationException;
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.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.annotation.Order;
@@ -198,6 +199,13 @@ class RedisHttpsSessionConfigurationTests {
Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
void importConfigAndCustomize() {
registerAndRefresh(RedisConfig.class, ImportConfigAndCustomizeConfiguration.class);
RedisSessionRepository sessionRepository = this.context.getBean(RedisSessionRepository.class);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ZERO);
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
this.context.register(annotatedClasses);
this.context.refresh();
@@ -261,6 +269,7 @@ class RedisHttpsSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisHttpSession(saveMode = SaveMode.ALWAYS)
static class CustomSaveModeExpressionAnnotationConfiguration {
@@ -342,6 +351,7 @@ class RedisHttpsSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisHttpSession
static class SessionRepositoryCustomizerConfiguration {
@@ -360,4 +370,15 @@ class RedisHttpsSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@Import(RedisHttpSessionConfiguration.class)
static class ImportConfigAndCustomizeConfiguration {
@Bean
SessionRepositoryCustomizer<RedisSessionRepository> sessionRepositoryCustomizer() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.session.data.redis.config.annotation.web.http;
import java.time.Duration;
import java.util.Map;
import java.util.Properties;
@@ -27,6 +28,7 @@ import org.springframework.beans.factory.BeanCreationException;
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.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.annotation.Order;
@@ -117,10 +119,8 @@ class RedisIndexedHttpSessionConfigurationTests {
void customCleanupCronAnnotation() {
registerAndRefresh(RedisConfig.class, CustomCleanupCronExpressionAnnotationConfiguration.class);
RedisIndexedHttpSessionConfiguration configuration = this.context
.getBean(RedisIndexedHttpSessionConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(ReflectionTestUtils.getField(configuration, "cleanupCron")).isEqualTo(CLEANUP_CRON_EXPRESSION);
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
assertThat(sessionRepository).extracting("cleanupCron").isEqualTo(CLEANUP_CRON_EXPRESSION);
}
@Test
@@ -229,8 +229,15 @@ class RedisIndexedHttpSessionConfigurationTests {
void sessionRepositoryCustomizer() {
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
void importConfigAndCustomize() {
registerAndRefresh(RedisConfig.class, ImportConfigAndCustomizeConfiguration.class);
RedisIndexedSessionRepository sessionRepository = this.context.getBean(RedisIndexedSessionRepository.class);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ZERO);
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
@@ -296,11 +303,13 @@ class RedisIndexedHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisIndexedHttpSession(cleanupCron = CLEANUP_CRON_EXPRESSION)
static class CustomCleanupCronExpressionAnnotationConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisIndexedHttpSession(saveMode = SaveMode.ALWAYS)
static class CustomSaveModeExpressionAnnotationConfiguration {
@@ -405,20 +414,32 @@ class RedisIndexedHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisIndexedHttpSession
static class SessionRepositoryCustomizerConfiguration {
@Bean
@Order(0)
SessionRepositoryCustomizer<RedisIndexedSessionRepository> sessionRepositoryCustomizerOne() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
@Bean
@Order(1)
SessionRepositoryCustomizer<RedisIndexedSessionRepository> sessionRepositoryCustomizerTwo() {
return (sessionRepository) -> sessionRepository
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
.setDefaultMaxInactiveInterval(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
}
@Configuration(proxyBeanMethods = false)
@Import(RedisIndexedHttpSessionConfiguration.class)
static class ImportConfigAndCustomizeConfiguration {
@Bean
SessionRepositoryCustomizer<RedisIndexedSessionRepository> sessionRepositoryCustomizer() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.session.data.redis.config.annotation.web.http.gh109;
import java.time.Duration;
import java.util.Properties;
import org.junit.jupiter.api.Test;
@@ -60,7 +61,7 @@ class Gh109Tests {
@Configuration
static class Config extends RedisHttpSessionConfiguration {
int sessionTimeout = 100;
Duration sessionTimeout = Duration.ofSeconds(100);
/**
* override sessionRepository construction to set the custom session-timeout

View File

@@ -16,6 +16,8 @@
package org.springframework.session.data.redis.config.annotation.web.server;
import java.time.Duration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -25,6 +27,7 @@ import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
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.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
@@ -102,9 +105,8 @@ class RedisWebSessionConfigurationTests {
registerAndRefresh(RedisConfig.class, CustomMaxInactiveIntervalConfig.class);
ReactiveRedisSessionRepository repository = this.context.getBean(ReactiveRedisSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval"))
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(repository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
@@ -189,7 +191,7 @@ class RedisWebSessionConfigurationTests {
void multipleConnectionFactoryRedisConfig() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> registerAndRefresh(RedisConfig.class, MultipleConnectionFactoryRedisConfig.class))
.withCauseInstanceOf(NoUniqueBeanDefinitionException.class).havingCause()
.havingRootCause().isInstanceOf(NoUniqueBeanDefinitionException.class)
.withMessageContaining("expected single matching bean but found 2");
}
@@ -221,8 +223,15 @@ class RedisWebSessionConfigurationTests {
void sessionRepositoryCustomizer() {
registerAndRefresh(RedisConfig.class, SessionRepositoryCustomizerConfiguration.class);
ReactiveRedisSessionRepository sessionRepository = this.context.getBean(ReactiveRedisSessionRepository.class);
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
void importConfigAndCustomize() {
registerAndRefresh(RedisConfig.class, ImportConfigAndCustomizeConfiguration.class);
ReactiveRedisSessionRepository sessionRepository = this.context.getBean(ReactiveRedisSessionRepository.class);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ZERO);
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
@@ -240,11 +249,13 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class DefaultConfig {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class SpringSessionRedisOperationsResolvingConfig {
@@ -257,16 +268,19 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession(redisNamespace = REDIS_NAMESPACE)
static class CustomNamespaceConfig {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class CustomMaxInactiveIntervalConfig {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession(saveMode = SaveMode.ALWAYS)
static class CustomSaveModeExpressionAnnotationConfiguration {
@@ -281,6 +295,7 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class QualifiedConnectionFactoryRedisConfig {
@@ -292,6 +307,7 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class PrimaryConnectionFactoryRedisConfig {
@@ -303,6 +319,7 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class QualifiedAndPrimaryConnectionFactoryRedisConfig {
@@ -320,6 +337,7 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class NamedConnectionFactoryRedisConfig {
@@ -330,6 +348,7 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class MultipleConnectionFactoryRedisConfig {
@@ -340,6 +359,7 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class CustomRedisSerializerConfig {
@@ -351,20 +371,32 @@ class RedisWebSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableRedisWebSession
static class SessionRepositoryCustomizerConfiguration {
@Bean
@Order(0)
ReactiveSessionRepositoryCustomizer<ReactiveRedisSessionRepository> sessionRepositoryCustomizerOne() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
@Bean
@Order(1)
ReactiveSessionRepositoryCustomizer<ReactiveRedisSessionRepository> sessionRepositoryCustomizerTwo() {
return (sessionRepository) -> sessionRepository
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
.setDefaultMaxInactiveInterval(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
}
@Configuration(proxyBeanMethods = false)
@Import(RedisWebSessionConfiguration.class)
static class ImportConfigAndCustomizeConfiguration {
@Bean
ReactiveSessionRepositoryCustomizer<ReactiveRedisSessionRepository> sessionRepositoryCustomizer() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
}

View File

@@ -1,4 +1,4 @@
name: ROOT
version: '3.0.0-M4'
version: '3.0.0-RC2'
prerelease: 'true'
display_version: '3.0.0-M4'
display_version: '3.0.0-RC2'

View File

@@ -91,6 +91,7 @@ function createSymbolicVersionAlias (component, version, symbolicVersionSegment,
function computeOut (src, family, version, htmlUrlExtensionStyle) {
let { component, module: module_, basename, extname, relative, stem } = src
if (component === 'ROOT') component = ''
if (module_ === 'ROOT') module_ = ''
let indexifyPathSegment = ''
let familyPathSegment = ''
@@ -120,8 +121,11 @@ function computePub (src, out, family, version, htmlUrlExtensionStyle) {
const pub = {}
let url
if (family === 'nav') {
const urlSegments = version ? [src.component, version] : [src.component]
if (src.module && src.module !== 'ROOT') urlSegments.push(src.module)
const component = src.component || 'ROOT'
const urlSegments = component === 'ROOT' ? [] : [component]
if (version) urlSegments.push(version)
const module_ = src.module || 'ROOT'
if (module_ !== 'ROOT') urlSegments.push(module_)
// an artificial URL used for resolving page references in navigation model
url = '/' + urlSegments.join('/') + '/'
pub.moduleRootPath = '.'

View File

@@ -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,11 +19,13 @@ package docs;
import java.util.concurrent.ConcurrentHashMap;
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.session.config.annotation.web.server.EnableSpringWebSession;
// tag::class[]
@Configuration(proxyBeanMethods = false)
@EnableSpringWebSession
public class SpringWebSessionConfig {

View File

@@ -51,7 +51,7 @@ public class RememberMeSecurityConfiguration {
return http
.formLogin(Customizer.withDefaults())
.authorizeRequests((authorize) -> authorize
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
).build();
}

View File

@@ -11,6 +11,7 @@
<!-- ... -->
<security:form-login />
<security:remember-me services-ref="rememberMeServices"/>
<security:intercept-url pattern="/**" access="permitAll()"/>
</security:http>
<bean id="rememberMeServices"

View File

@@ -15,7 +15,6 @@
*** xref:guides/boot-webflux-custom-cookie.adoc[Custom Cookie]
** Java Configuration
** XML Configuration
* xref:modules.adoc[Modules]
* xref:http-session.adoc[HttpSession Integration]
* xref:web-socket.adoc[WebSocket Integration]
* xref:web-session.adoc[WebSession Integration]

View File

@@ -1,22 +0,0 @@
[[modules]]
= Spring Session Modules
In Spring Session 1.x, all of the Spring Session's `SessionRepository` implementations were available within the `spring-session` artifact.
While convenient, this approach was not sustainable long-term as more features and `SessionRepository` implementations were added to the project.
With Spring Session 2.0, several modules were split off to be separate modules as well as managed repositories.
Spring Session for MongoDB was retired, but was later reactivated as a separate module.
As of Spring Session 2.6, Spring Session for MongoDB was merged back into Spring Session.
Now the situation with the various repositories and modules is as follows:
* https://github.com/spring-projects/spring-session[`spring-session` repository]
** Hosts the Spring Session Core, Spring Session for MongoDB, Spring Session for Redis, Spring Session JDBC, and Spring Session Hazelcast modules.
* https://github.com/spring-projects/spring-session-data-geode[`spring-session-data-geode` repository]
** Hosts the Spring Session Data Geode modules. Spring Session Data Geode has its own user guide, which you can find at the [https://spring.io/projects/spring-session-data-geode#learn site].
Finally, Spring Session also provides a Maven BOM ("`bill of materials`") module in order to help users with version management concerns:
* https://github.com/spring-projects/spring-session-bom[`spring-session-bom` repository]
** Hosts the Spring Session BOM module

View File

@@ -1,4 +1 @@
= What's New
Check also the Spring Session BOM https://github.com/spring-projects/spring-session-bom/wiki#release-notes[release notes]
for a list of new and noteworthy features, as well as upgrade instructions for each release.

View File

@@ -23,7 +23,6 @@ dependencies {
testImplementation 'org.assertj:assertj-core'
testImplementation 'com.hazelcast:hazelcast'
testImplementation 'io.lettuce:lettuce-core'
testImplementation 'jakarta.annotation:jakarta.annotation-api'
testImplementation 'jakarta.servlet:jakarta.servlet-api'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'

View File

@@ -3,7 +3,6 @@ apply plugin: 'io.spring.convention.spring-module'
dependencies {
api project(':spring-session-core')
api "com.hazelcast:hazelcast"
api "jakarta.annotation:jakarta.annotation-api"
api "org.springframework:spring-context"
testImplementation "jakarta.servlet:jakarta.servlet-api"

View File

@@ -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.
@@ -26,9 +26,6 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
@@ -40,6 +37,8 @@ import com.hazelcast.query.Predicates;
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.ApplicationEventPublisher;
import org.springframework.session.DelegatingIndexResolver;
import org.springframework.session.FindByIndexNameSessionRepository;
@@ -116,7 +115,8 @@ import org.springframework.util.Assert;
public class HazelcastIndexedSessionRepository
implements FindByIndexNameSessionRepository<HazelcastIndexedSessionRepository.HazelcastSession>,
EntryAddedListener<String, MapSession>, EntryEvictedListener<String, MapSession>,
EntryRemovedListener<String, MapSession>, EntryExpiredListener<String, MapSession> {
EntryRemovedListener<String, MapSession>, EntryExpiredListener<String, MapSession>, InitializingBean,
DisposableBean {
/**
* The default name of map used by Spring Session to store sessions.
@@ -137,11 +137,7 @@ public class HazelcastIndexedSessionRepository
private ApplicationEventPublisher eventPublisher = (event) -> {
};
/**
* If non-null, this value is used to override
* {@link MapSession#setMaxInactiveInterval(Duration)}.
*/
private Integer defaultMaxInactiveInterval;
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
private IndexResolver<Session> indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
@@ -164,14 +160,14 @@ public class HazelcastIndexedSessionRepository
this.hazelcastInstance = hazelcastInstance;
}
@PostConstruct
public void init() {
@Override
public void afterPropertiesSet() {
this.sessions = this.hazelcastInstance.getMap(this.sessionMapName);
this.sessionListenerId = this.sessions.addEntryListener(this, true);
}
@PreDestroy
public void close() {
@Override
public void destroy() {
this.sessions.removeEntryListener(this.sessionListenerId);
}
@@ -190,13 +186,27 @@ public class HazelcastIndexedSessionRepository
/**
* 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 maximum inactive interval in seconds
* time out. The default is 30 minutes.
* @param defaultMaxInactiveInterval the default maxInactiveInterval
*/
public void setDefaultMaxInactiveInterval(Integer 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(Integer defaultMaxInactiveInterval) {
setDefaultMaxInactiveInterval(Duration.ofSeconds(defaultMaxInactiveInterval));
}
/**
* Set the {@link IndexResolver} to use.
* @param indexResolver the index resolver
@@ -236,9 +246,7 @@ public class HazelcastIndexedSessionRepository
@Override
public HazelcastSession createSession() {
MapSession cached = new MapSession();
if (this.defaultMaxInactiveInterval != null) {
cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
}
cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
HazelcastSession session = new HazelcastSession(cached, true);
session.flushImmediateIfNecessary();
return session;

View File

@@ -24,7 +24,6 @@ import java.lang.annotation.Target;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.session.FlushMode;
import org.springframework.session.MapSession;
@@ -41,7 +40,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
* {@link HazelcastInstance} must be provided. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;Configuration(proxyBeanMethods = false)
* &#064;EnableHazelcastHttpSession
* public class HazelcastHttpSessionConfig {
*
@@ -67,7 +66,6 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
@Target(ElementType.TYPE)
@Documented
@Import(HazelcastHttpSessionConfiguration.class)
@Configuration(proxyBeanMethods = false)
public @interface EnableHazelcastHttpSession {
/**

View File

@@ -16,6 +16,7 @@
package org.springframework.session.hazelcast.config.annotation.web.http;
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.Autowired;
import org.springframework.context.ApplicationEventPublisher;
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;
@@ -54,9 +56,10 @@ import org.springframework.util.StringUtils;
* @see EnableHazelcastHttpSession
*/
@Configuration(proxyBeanMethods = false)
public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
@Import(SpringHttpSessionConfiguration.class)
public class HazelcastHttpSessionConfiguration implements ImportAware {
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
private String sessionMapName = HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME;
@@ -77,8 +80,13 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
return createHazelcastIndexedSessionRepository();
}
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
this.maxInactiveInterval = maxInactiveInterval;
}
@Deprecated
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
setMaxInactiveInterval(Duration.ofSeconds(maxInactiveIntervalInSeconds));
}
public void setSessionMapName(String sessionMapName) {
@@ -125,7 +133,10 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
Map<String, Object> attributeMap = importMetadata
.getAnnotationAttributes(EnableHazelcastHttpSession.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 sessionMapNameValue = attributes.getString("sessionMapName");
if (StringUtils.hasText(sessionMapNameValue)) {
this.sessionMapName = sessionMapNameValue;
@@ -144,7 +155,7 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
if (StringUtils.hasText(this.sessionMapName)) {
sessionRepository.setSessionMapName(this.sessionMapName);
}
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveInterval);
sessionRepository.setFlushMode(this.flushMode);
sessionRepository.setSaveMode(this.saveMode);
this.sessionRepositoryCustomizers

View File

@@ -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.
@@ -76,7 +76,7 @@ class HazelcastIndexedSessionRepositoryTests {
void setUp() {
given(this.hazelcastInstance.<String, MapSession>getMap(anyString())).willReturn(this.sessions);
this.repository = new HazelcastIndexedSessionRepository(this.hazelcastInstance);
this.repository.init();
this.repository.afterPropertiesSet();
}
@Test
@@ -105,12 +105,12 @@ class HazelcastIndexedSessionRepositoryTests {
void createSessionCustomMaxInactiveInterval() {
verify(this.sessions, times(1)).addEntryListener(any(MapListener.class), anyBoolean());
int interval = 1;
Duration interval = Duration.ofSeconds(1);
this.repository.setDefaultMaxInactiveInterval(interval);
HazelcastSession session = this.repository.createSession();
assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(interval));
assertThat(session.getMaxInactiveInterval()).isEqualTo(interval);
verifyNoMoreInteractions(this.sessions);
}

View File

@@ -16,6 +16,8 @@
package org.springframework.session.hazelcast.config.annotation.web.http;
import java.time.Duration;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import org.junit.jupiter.api.AfterEach;
@@ -27,6 +29,7 @@ import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
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.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.session.FlushMode;
@@ -68,9 +71,8 @@ class HazelcastHttpSessionConfigurationTests {
@Test
void noHazelcastInstanceConfiguration() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> registerAndRefresh(NoHazelcastInstanceConfiguration.class))
.withCauseInstanceOf(NoSuchBeanDefinitionException.class).havingCause()
.withMessageContaining("HazelcastInstance");
.isThrownBy(() -> registerAndRefresh(NoHazelcastInstanceConfiguration.class)).havingRootCause()
.isInstanceOf(NoSuchBeanDefinitionException.class).withMessageContaining("HazelcastInstance");
}
@Test
@@ -105,9 +107,8 @@ class HazelcastHttpSessionConfigurationTests {
registerAndRefresh(BaseConfiguration.class, CustomMaxInactiveIntervalInSecondsSetConfiguration.class);
HazelcastIndexedSessionRepository repository = this.context.getBean(HazelcastIndexedSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval"))
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(repository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
@@ -115,9 +116,8 @@ class HazelcastHttpSessionConfigurationTests {
registerAndRefresh(CustomMaxInactiveIntervalInSecondsConfiguration.class);
HazelcastIndexedSessionRepository repository = this.context.getBean(HazelcastIndexedSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval"))
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(repository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
@@ -205,8 +205,8 @@ class HazelcastHttpSessionConfigurationTests {
@Test
void multipleHazelcastInstanceConfiguration() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> registerAndRefresh(MultipleHazelcastInstanceConfiguration.class))
.withCauseInstanceOf(NoUniqueBeanDefinitionException.class).havingCause()
.isThrownBy(() -> registerAndRefresh(MultipleHazelcastInstanceConfiguration.class)).havingRootCause()
.isInstanceOf(NoUniqueBeanDefinitionException.class)
.withMessageContaining("expected single matching bean but found 2");
}
@@ -226,8 +226,16 @@ class HazelcastHttpSessionConfigurationTests {
registerAndRefresh(SessionRepositoryCustomizerConfiguration.class);
HazelcastIndexedSessionRepository sessionRepository = this.context
.getBean(HazelcastIndexedSessionRepository.class);
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
void importConfigAndCustomize() {
registerAndRefresh(ImportConfigAndCustomizeConfiguration.class);
HazelcastIndexedSessionRepository sessionRepository = this.context
.getBean(HazelcastIndexedSessionRepository.class);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ZERO);
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
@@ -280,7 +288,7 @@ class HazelcastHttpSessionConfigurationTests {
static class CustomMaxInactiveIntervalInSecondsSetConfiguration extends HazelcastHttpSessionConfiguration {
CustomMaxInactiveIntervalInSecondsSetConfiguration() {
setMaxInactiveIntervalInSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS);
setMaxInactiveInterval(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
}
@@ -300,6 +308,7 @@ class HazelcastHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableHazelcastHttpSession(saveMode = SaveMode.ALWAYS)
static class CustomSaveModeExpressionAnnotationConfiguration {
@@ -414,6 +423,7 @@ class HazelcastHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableHazelcastHttpSession
static class CustomIndexResolverConfiguration extends BaseConfiguration {
@@ -425,20 +435,32 @@ class HazelcastHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableHazelcastHttpSession
static class SessionRepositoryCustomizerConfiguration extends BaseConfiguration {
@Bean
@Order(0)
SessionRepositoryCustomizer<HazelcastIndexedSessionRepository> sessionRepositoryCustomizerOne() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
@Bean
@Order(1)
SessionRepositoryCustomizer<HazelcastIndexedSessionRepository> sessionRepositoryCustomizerTwo() {
return (sessionRepository) -> sessionRepository
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
.setDefaultMaxInactiveInterval(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
}
@Configuration(proxyBeanMethods = false)
@Import(HazelcastHttpSessionConfiguration.class)
static class ImportConfigAndCustomizeConfiguration extends BaseConfiguration {
@Bean
SessionRepositoryCustomizer<HazelcastIndexedSessionRepository> sessionRepositoryCustomizer() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
}

View File

@@ -15,6 +15,7 @@ dependencies {
testImplementation "org.springframework:spring-web"
testImplementation "org.springframework.security:spring-security-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "org.junit.jupiter:junit-jupiter-params"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine"
integrationTestCompile "com.h2database:h2"
@@ -24,6 +25,7 @@ dependencies {
integrationTestCompile "com.zaxxer:HikariCP"
integrationTestCompile "mysql:mysql-connector-java"
integrationTestCompile "org.apache.derby:derby"
integrationTestCompile "org.apache.derby:derbytools"
integrationTestCompile "org.hsqldb:hsqldb"
integrationTestCompile "org.mariadb.jdbc:mariadb-java-client"
integrationTestCompile "org.postgresql:postgresql"

View File

@@ -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.
@@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -827,6 +828,7 @@ abstract class AbstractJdbcIndexedSessionRepositoryITests {
return this.changedContext.getAuthentication().getName();
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class BaseConfig {

View File

@@ -34,6 +34,8 @@ import java.util.stream.Collectors;
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.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.GenericConversionService;
@@ -48,6 +50,10 @@ import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
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;
@@ -130,14 +136,19 @@ import org.springframework.util.StringUtils;
* @author Craig Andrews
* @since 2.2.0
*/
public class JdbcIndexedSessionRepository
implements FindByIndexNameSessionRepository<JdbcIndexedSessionRepository.JdbcSession> {
public class JdbcIndexedSessionRepository implements
FindByIndexNameSessionRepository<JdbcIndexedSessionRepository.JdbcSession>, InitializingBean, DisposableBean {
/**
* The default name of database table used by Spring Session to store sessions.
*/
public static final String DEFAULT_TABLE_NAME = "SPRING_SESSION";
/**
* The default cron expression used for expired session cleanup job.
*/
public static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private static final String CREATE_SESSION_QUERY = """
@@ -225,11 +236,7 @@ public class JdbcIndexedSessionRepository
private String deleteSessionsByExpiryTimeQuery;
/**
* If non-null, this value is used to override the default value for
* {@link JdbcSession#setMaxInactiveInterval(Duration)}.
*/
private Integer defaultMaxInactiveInterval;
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
private IndexResolver<Session> indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>());
@@ -241,6 +248,10 @@ public class JdbcIndexedSessionRepository
private SaveMode saveMode = SaveMode.ON_SET_ATTRIBUTE;
private String cleanupCron = DEFAULT_CLEANUP_CRON;
private ThreadPoolTaskScheduler taskScheduler;
/**
* Create a new {@link JdbcIndexedSessionRepository} instance which uses the provided
* {@link JdbcOperations} and {@link TransactionOperations} to manage sessions.
@@ -255,6 +266,28 @@ public class JdbcIndexedSessionRepository
prepareQueries();
}
@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();
}
}
/**
* Set the name of database table used to store sessions.
* @param tableName the database table name
@@ -349,13 +382,27 @@ public class JdbcIndexedSessionRepository
/**
* 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 maximum inactive interval in seconds
* time out. The default is 30 minutes.
* @param defaultMaxInactiveInterval the default maxInactiveInterval
*/
public void setDefaultMaxInactiveInterval(Integer 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(Integer defaultMaxInactiveInterval) {
setDefaultMaxInactiveInterval(Duration.ofSeconds(defaultMaxInactiveInterval));
}
/**
* Set the {@link IndexResolver} to use.
* @param indexResolver the index resolver
@@ -397,12 +444,25 @@ public class JdbcIndexedSessionRepository
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;
}
@Override
public JdbcSession createSession() {
MapSession delegate = new MapSession();
if (this.defaultMaxInactiveInterval != null) {
delegate.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval));
}
delegate.setMaxInactiveInterval(this.defaultMaxInactiveInterval);
JdbcSession session = new JdbcSession(delegate, UUID.randomUUID().toString(), true);
session.flushIfRequired();
return session;

View File

@@ -32,7 +32,26 @@ class SessionJdbcRuntimeHints implements RuntimeHintsRegistrar {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerType(TypeReference.of("javax.sql.DataSource"),
(hint) -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS));
hints.resources().registerPattern("org/springframework/session/jdbc/*.sql");
hints.resources().registerPattern("org/springframework/session/jdbc/schema-db2.sql")
.registerPattern("org/springframework/session/jdbc/schema-derby.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-db2.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-derby.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-h2.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-hsqldb.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-mysql.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-oracle.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-postgresql.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-sqlite.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-sqlserver.sql")
.registerPattern("org/springframework/session/jdbc/schema-drop-sybase.sql")
.registerPattern("org/springframework/session/jdbc/schema-h2.sql")
.registerPattern("org/springframework/session/jdbc/schema-hsqldb.sql")
.registerPattern("org/springframework/session/jdbc/schema-mysql.sql")
.registerPattern("org/springframework/session/jdbc/schema-oracle.sql")
.registerPattern("org/springframework/session/jdbc/schema-postgresql.sql")
.registerPattern("org/springframework/session/jdbc/schema-sqlite.sql")
.registerPattern("org/springframework/session/jdbc/schema-sqlserver.sql")
.registerPattern("org/springframework/session/jdbc/schema-sybase.sql");
}
}

View File

@@ -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.lang.annotation.Target;
import javax.sql.DataSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.session.FlushMode;
import org.springframework.session.MapSession;
@@ -42,7 +41,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
* {@link DataSource} must be provided. For example:
*
* <pre class="code">
* &#064;Configuration
* &#064;Configuration(proxyBeanMethods = false)
* &#064;EnableJdbcHttpSession
* public class JdbcHttpSessionConfig {
*
@@ -77,7 +76,6 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
@Target(ElementType.TYPE)
@Documented
@Import(JdbcHttpSessionConfiguration.class)
@Configuration(proxyBeanMethods = false)
public @interface EnableJdbcHttpSession {
/**
@@ -98,7 +96,7 @@ public @interface EnableJdbcHttpSession {
* @return the session cleanup cron expression
* @since 2.0.0
*/
String cleanupCron() default JdbcHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
String cleanupCron() default JdbcIndexedSessionRepository.DEFAULT_CLEANUP_CRON;
/**
* Flush mode for the sessions. The default is {@code ON_SAVE} which only updates the

View File

@@ -17,6 +17,7 @@
package org.springframework.session.jdbc.config.annotation.web.http;
import java.sql.DatabaseMetaData;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -30,6 +31,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.convert.ConversionService;
@@ -43,9 +45,6 @@ import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
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;
@@ -77,16 +76,14 @@ import org.springframework.util.StringValueResolver;
* @see EnableJdbcHttpSession
*/
@Configuration(proxyBeanMethods = false)
public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
@Import(SpringHttpSessionConfiguration.class)
public class JdbcHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
private Duration maxInactiveInterval = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL;
private String tableName = JdbcIndexedSessionRepository.DEFAULT_TABLE_NAME;
private String cleanupCron = DEFAULT_CLEANUP_CRON;
private String cleanupCron = JdbcIndexedSessionRepository.DEFAULT_CLEANUP_CRON;
private FlushMode flushMode = FlushMode.ON_SAVE;
@@ -123,9 +120,10 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
if (StringUtils.hasText(this.tableName)) {
sessionRepository.setTableName(this.tableName);
}
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveInterval);
sessionRepository.setFlushMode(this.flushMode);
sessionRepository.setSaveMode(this.saveMode);
sessionRepository.setCleanupCron(this.cleanupCron);
if (this.indexResolver != null) {
sessionRepository.setIndexResolver(this.indexResolver);
}
@@ -162,8 +160,13 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
}
}
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
this.maxInactiveInterval = maxInactiveInterval;
}
@Deprecated
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
setMaxInactiveInterval(Duration.ofSeconds(maxInactiveIntervalInSeconds));
}
public void setTableName(String tableName) {
@@ -247,7 +250,10 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
Map<String, Object> attributeMap = importMetadata
.getAnnotationAttributes(EnableJdbcHttpSession.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 tableNameValue = attributes.getString("tableName");
if (StringUtils.hasText(tableNameValue)) {
this.tableName = this.embeddedValueResolver.resolveStringValue(tableNameValue);
@@ -281,25 +287,4 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
return conversionService;
}
/**
* Configuration of scheduled job for cleaning up expired sessions.
*/
@EnableScheduling
@Configuration(proxyBeanMethods = false)
class SessionCleanupConfiguration implements SchedulingConfigurer {
private final JdbcIndexedSessionRepository sessionRepository;
SessionCleanupConfiguration(JdbcIndexedSessionRepository sessionRepository) {
this.sessionRepository = sessionRepository;
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addCronTask(this.sessionRepository::cleanUpExpiredSessions,
JdbcHttpSessionConfiguration.this.cleanupCron);
}
}
}

View File

@@ -38,6 +38,7 @@ import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.TemporaryLobCreator;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
@@ -243,6 +244,25 @@ class JdbcIndexedSessionRepositoryTests {
.withMessage("saveMode must not be null");
}
@Test
void setCleanupCronNull() {
assertThatIllegalArgumentException().isThrownBy(() -> this.repository.setCleanupCron(null))
.withMessage("cleanupCron must not be null");
}
@Test
void setCleanupCronInvalid() {
assertThatIllegalArgumentException().isThrownBy(() -> this.repository.setCleanupCron("test"))
.withMessage("cleanupCron must be valid");
}
@Test
void setCleanupCronDisabled() {
this.repository.setCleanupCron(Scheduled.CRON_DISABLED);
this.repository.afterPropertiesSet();
assertThat(this.repository).extracting("taskScheduler").isNull();
}
@Test
void createSessionDefaultMaxInactiveInterval() {
JdbcSession session = this.repository.createSession();
@@ -254,13 +274,13 @@ class JdbcIndexedSessionRepositoryTests {
@Test
void createSessionCustomMaxInactiveInterval() {
int interval = 1;
Duration interval = Duration.ofSeconds(1);
this.repository.setDefaultMaxInactiveInterval(interval);
JdbcSession session = this.repository.createSession();
assertThat(session.isNew()).isTrue();
assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(interval));
assertThat(session.getMaxInactiveInterval()).isEqualTo(interval);
verifyNoMoreInteractions(this.jdbcOperations);
}

View File

@@ -16,12 +16,20 @@
package org.springframework.session.jdbc.aot.hint;
import java.io.IOException;
import java.util.Arrays;
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.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.SpringFactoriesLoader;
import static org.assertj.core.api.Assertions.assertThat;
@@ -45,13 +53,21 @@ class SessionJdbcRuntimeHintsTests {
assertThat(match).isTrue();
}
@Test
void jdbcSchemasHasHints() {
@ParameterizedTest
@MethodSource("getSchemaFileNames")
void jdbcSchemasHasHints(String schemaFileName) {
this.sessionJdbcRuntimeHints.registerHints(this.hints, getClass().getClassLoader());
assertThat(RuntimeHintsPredicates.resource().forResource("org/springframework/session/jdbc/schema.sql"))
assertThat(RuntimeHintsPredicates.resource().forResource("org/springframework/session/jdbc/" + schemaFileName))
.accepts(this.hints);
}
private static Stream<String> getSchemaFileNames() throws IOException {
return Arrays
.stream(new PathMatchingResourcePatternResolver()
.getResources("classpath*:org/springframework/session/jdbc/schema-*.sql"))
.map(Resource::getFilename);
}
@Test
void dataSourceHasHints() {
this.sessionJdbcRuntimeHints.registerHints(this.hints, getClass().getClassLoader());

View File

@@ -16,6 +16,8 @@
package org.springframework.session.jdbc.config.annotation.web.http;
import java.time.Duration;
import javax.sql.DataSource;
import org.junit.jupiter.api.AfterEach;
@@ -27,6 +29,7 @@ import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
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.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.annotation.Order;
@@ -117,9 +120,8 @@ class JdbcHttpSessionConfigurationTests {
CustomMaxInactiveIntervalInSecondsAnnotationConfiguration.class);
JdbcIndexedSessionRepository repository = this.context.getBean(JdbcIndexedSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval"))
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(repository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
@@ -127,27 +129,24 @@ class JdbcHttpSessionConfigurationTests {
registerAndRefresh(DataSourceConfiguration.class, CustomMaxInactiveIntervalInSecondsSetterConfiguration.class);
JdbcIndexedSessionRepository repository = this.context.getBean(JdbcIndexedSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval"))
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(repository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
void customCleanupCronAnnotation() {
registerAndRefresh(DataSourceConfiguration.class, CustomCleanupCronExpressionAnnotationConfiguration.class);
JdbcHttpSessionConfiguration configuration = this.context.getBean(JdbcHttpSessionConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(ReflectionTestUtils.getField(configuration, "cleanupCron")).isEqualTo(CLEANUP_CRON_EXPRESSION);
JdbcIndexedSessionRepository repository = this.context.getBean(JdbcIndexedSessionRepository.class);
assertThat(repository).extracting("cleanupCron").isEqualTo(CLEANUP_CRON_EXPRESSION);
}
@Test
void customCleanupCronSetter() {
registerAndRefresh(DataSourceConfiguration.class, CustomCleanupCronExpressionSetterConfiguration.class);
JdbcHttpSessionConfiguration configuration = this.context.getBean(JdbcHttpSessionConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(ReflectionTestUtils.getField(configuration, "cleanupCron")).isEqualTo(CLEANUP_CRON_EXPRESSION);
JdbcIndexedSessionRepository repository = this.context.getBean(JdbcIndexedSessionRepository.class);
assertThat(repository).extracting("cleanupCron").isEqualTo(CLEANUP_CRON_EXPRESSION);
}
@Test
@@ -298,8 +297,8 @@ class JdbcHttpSessionConfigurationTests {
void sessionRepositoryCustomizer() {
registerAndRefresh(DataSourceConfiguration.class, SessionRepositoryCustomizerConfiguration.class);
JdbcIndexedSessionRepository sessionRepository = this.context.getBean(JdbcIndexedSessionRepository.class);
assertThat(sessionRepository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval",
MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval")
.isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
@Test
@@ -312,11 +311,19 @@ class JdbcHttpSessionConfigurationTests {
assertThat(jdbcTemplate.getExceptionTranslator()).isInstanceOf(SQLErrorCodeSQLExceptionTranslator.class);
}
@Test
void importConfigAndCustomize() {
registerAndRefresh(DataSourceConfiguration.class, ImportConfigAndCustomizeConfiguration.class);
JdbcIndexedSessionRepository sessionRepository = this.context.getBean(JdbcIndexedSessionRepository.class);
assertThat(sessionRepository).extracting("defaultMaxInactiveInterval").isEqualTo(Duration.ZERO);
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
this.context.register(annotatedClasses);
this.context.refresh();
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class NoDataSourceConfiguration {
@@ -337,11 +344,13 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class DefaultConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession(tableName = TABLE_NAME)
static class CustomTableNameAnnotationConfiguration {
@@ -356,6 +365,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class CustomMaxInactiveIntervalInSecondsAnnotationConfiguration {
@@ -370,6 +380,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession(cleanupCron = CLEANUP_CRON_EXPRESSION)
static class CustomCleanupCronExpressionAnnotationConfiguration {
@@ -384,6 +395,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession(flushMode = FlushMode.IMMEDIATE)
static class CustomFlushModeExpressionAnnotationConfiguration {
@@ -398,6 +410,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession(saveMode = SaveMode.ALWAYS)
static class CustomSaveModeExpressionAnnotationConfiguration {
@@ -412,6 +425,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class QualifiedDataSourceConfiguration {
@@ -423,6 +437,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class PrimaryDataSourceConfiguration {
@@ -434,6 +449,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class QualifiedAndPrimaryDataSourceConfiguration {
@@ -451,6 +467,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class NamedDataSourceConfiguration {
@@ -461,6 +478,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class MultipleDataSourceConfiguration {
@@ -471,6 +489,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class CustomTransactionOperationsConfiguration {
@@ -481,6 +500,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class CustomIndexResolverConfiguration {
@@ -492,6 +512,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class CustomLobHandlerConfiguration {
@@ -502,6 +523,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class CustomConversionServiceConfiguration {
@@ -512,6 +534,7 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession(tableName = "${session.jdbc.tableName}")
static class CustomJdbcHttpSessionConfiguration {
@@ -522,20 +545,32 @@ class JdbcHttpSessionConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession
static class SessionRepositoryCustomizerConfiguration {
@Bean
@Order(0)
SessionRepositoryCustomizer<JdbcIndexedSessionRepository> sessionRepositoryCustomizerOne() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(0);
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
@Bean
@Order(1)
SessionRepositoryCustomizer<JdbcIndexedSessionRepository> sessionRepositoryCustomizerTwo() {
return (sessionRepository) -> sessionRepository
.setDefaultMaxInactiveInterval(MAX_INACTIVE_INTERVAL_IN_SECONDS);
.setDefaultMaxInactiveInterval(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS));
}
}
@Configuration(proxyBeanMethods = false)
@Import(JdbcHttpSessionConfiguration.class)
static class ImportConfigAndCustomizeConfiguration {
@Bean
SessionRepositoryCustomizer<JdbcIndexedSessionRepository> sessionRepositoryCustomizer() {
return (sessionRepository) -> sessionRepository.setDefaultMaxInactiveInterval(Duration.ZERO);
}
}

View File

@@ -36,7 +36,7 @@ public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests((authorize) -> authorize
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)

View File

@@ -1 +1,2 @@
spring.security.user.password=password
spring.session.redis.repository-type=indexed

View File

@@ -35,7 +35,7 @@ public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests((authorize) -> authorize
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)

View File

@@ -45,7 +45,7 @@ public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests((authorize) -> authorize
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)

View File

@@ -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.
@@ -19,10 +19,12 @@ package org.springframework.session.mongodb.examples.config;
import java.time.Duration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.mongo.JdkMongoSessionConverter;
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;
// tag::class[]
@Configuration(proxyBeanMethods = false)
@EnableMongoHttpSession // <1>
public class HttpSessionConfig {

View File

@@ -35,7 +35,7 @@ public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests((authorize) -> authorize
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)

View File

@@ -36,7 +36,7 @@ public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests((authorize) -> authorize
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)

View File

@@ -54,7 +54,7 @@ public class WebSecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests((authorize) -> authorize
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated()
)

View File

@@ -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.
@@ -17,12 +17,14 @@
package sample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
@Configuration(proxyBeanMethods = false)
@Import(EmbeddedRedisConfig.class)
@EnableRedisHttpSession
public class Config {

View File

@@ -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,6 +19,7 @@ package sample;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
@@ -27,6 +28,7 @@ import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHtt
import org.springframework.transaction.PlatformTransactionManager;
// tag::class[]
@Configuration(proxyBeanMethods = false)
@EnableJdbcHttpSession // <1>
public class Config {

View File

@@ -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.
@@ -17,12 +17,14 @@
package sample;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@Import(EmbeddedRedisConfig.class)
// tag::class[]
@Configuration(proxyBeanMethods = false)
@EnableRedisHttpSession // <1>
public class Config {

Some files were not shown because too many files have changed in this diff Show More