Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56033a9b68 | ||
|
|
99a2b079ac | ||
|
|
9120151692 | ||
|
|
5abbe66b1d | ||
|
|
f00c196430 | ||
|
|
be2604ca69 | ||
|
|
2aa71ffb6d | ||
|
|
8bdcba6e50 | ||
|
|
8dd1a10f1b | ||
|
|
1d247aa96f | ||
|
|
c00d6a7bf2 | ||
|
|
c0df3bf28b | ||
|
|
1b8c9838a4 | ||
|
|
8a1b454121 | ||
|
|
ef69c8169a | ||
|
|
40b3d07224 | ||
|
|
8c726f2215 | ||
|
|
c2a86a27ce | ||
|
|
6a08ef6f97 | ||
|
|
9c4e20f074 | ||
|
|
5845a9c46a | ||
|
|
7c6693a268 | ||
|
|
05a3f59813 | ||
|
|
47a7a35aa4 | ||
|
|
04b4fe3e3b | ||
|
|
36bb65e4b5 | ||
|
|
8ef36e4f3e | ||
|
|
ab3e280993 | ||
|
|
30562b5749 | ||
|
|
d42a7b65ea | ||
|
|
db9807d12b | ||
|
|
db09fa8168 | ||
|
|
031541bc05 | ||
|
|
084e3428fb | ||
|
|
b321ff02f0 | ||
|
|
c6c6beb40c | ||
|
|
0127ef9f9b | ||
|
|
cd8686ae9c | ||
|
|
233d179bfa | ||
|
|
4e8ae8d9d4 | ||
|
|
8c6810c6dd | ||
|
|
fca411996a | ||
|
|
79b8296e1c | ||
|
|
043cb42149 | ||
|
|
c28f047eb5 | ||
|
|
972cf66d7e | ||
|
|
f1319483ee | ||
|
|
6ad5006280 | ||
|
|
f7e07b7f6b | ||
|
|
4cf26d9c36 | ||
|
|
a848df1235 | ||
|
|
f8292ba512 | ||
|
|
21bcc6e8d7 |
@@ -16,4 +16,5 @@ cache:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
script: ./gradlew build
|
||||
install: true
|
||||
script: ./gradlew clean build --refresh-dependencies --no-daemon
|
||||
|
||||
17
Jenkinsfile
vendored
17
Jenkinsfile
vendored
@@ -24,21 +24,6 @@ try {
|
||||
}
|
||||
}
|
||||
},
|
||||
sonar: {
|
||||
stage('Sonar') {
|
||||
node {
|
||||
checkout scm
|
||||
withCredentials([string(credentialsId: 'spring-sonar.login', variable: 'SONAR_LOGIN')]) {
|
||||
try {
|
||||
sh "./gradlew clean sonarqube -PexcludeProjects='**/samples/**' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon"
|
||||
} catch(Exception e) {
|
||||
currentBuild.result = 'FAILED: sonar'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
springio: {
|
||||
stage('Spring IO') {
|
||||
node {
|
||||
@@ -95,7 +80,7 @@ try {
|
||||
subject: subject,
|
||||
body: details,
|
||||
recipientProviders: RECIPIENTS,
|
||||
to: "$SPRING_SECURITY_TEAM_EMAILS"
|
||||
to: "$SPRING_SESSION_TEAM_EMAILS"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.2.RELEASE'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.3.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
}
|
||||
repositories {
|
||||
maven { url 'https://repo.spring.io/libs-snapshot' }
|
||||
maven { url 'https://repo.spring.io/plugins-snapshot' }
|
||||
maven { url 'https://repo.spring.io/plugins-release' }
|
||||
}
|
||||
}
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
|
||||
@@ -2,8 +2,10 @@ apply plugin: 'io.spring.convention.docs'
|
||||
apply plugin: 'io.spring.convention.spring-test'
|
||||
|
||||
dependencies {
|
||||
testCompile project(':spring-session')
|
||||
testCompile project(':spring-session-core')
|
||||
testCompile project(':spring-session-data-redis')
|
||||
testCompile project(':spring-session-hazelcast')
|
||||
testCompile project(':spring-session-jdbc')
|
||||
testCompile "org.springframework:spring-jdbc"
|
||||
testCompile "org.springframework:spring-messaging"
|
||||
testCompile "org.springframework:spring-webmvc"
|
||||
@@ -39,5 +41,5 @@ asciidoctor {
|
||||
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',
|
||||
'docs-test-resources-dir' : rootProject.projectDir.path + '/docs/src/test/resources/',
|
||||
'samples-dir' : rootProject.projectDir.path + '/samples/',
|
||||
'session-main-resources-dir' : rootProject.projectDir.path + '/spring-session/src/main/resources/'
|
||||
'session-jdbc-main-resources-dir' : project(':spring-session-jdbc').projectDir.path + '/src/main/resources/'
|
||||
}
|
||||
|
||||
@@ -416,7 +416,7 @@ include::{docs-test-dir}docs/security/SecurityConfiguration.java[tags=class]
|
||||
----
|
||||
|
||||
This assumes that you've also configured Spring Session to provide a `FindByIndexNameSessionRepository` that
|
||||
returns `ExpiringSession` instances.
|
||||
returns `Session` instances.
|
||||
|
||||
When using XML configuration, it would look something like this:
|
||||
[source,xml,indent=0]
|
||||
@@ -458,11 +458,7 @@ include::{indexdoc-tests}[tags=repository-demo]
|
||||
<5> We retrieve the `Session` from the `SessionRepository`.
|
||||
<6> We obtain the persisted `User` from our `Session` without the need for explicitly casting our attribute.
|
||||
|
||||
[[api-expiringsession]]
|
||||
=== ExpiringSession
|
||||
|
||||
An `ExpiringSession` extends a `Session` by providing attributes related to the `Session` instance's expiration.
|
||||
If there is no need to interact with the expiration information, prefer using the more simple `Session` API.
|
||||
`Session` API also provides attributes related to the `Session` instance's expiration.
|
||||
|
||||
Typical usage might look like the following:
|
||||
|
||||
@@ -471,17 +467,17 @@ Typical usage might look like the following:
|
||||
include::{indexdoc-tests}[tags=expire-repository-demo]
|
||||
----
|
||||
|
||||
<1> We create a `SessionRepository` instance with a generic type, `S`, that extends `ExpiringSession`. The generic type is defined in our class.
|
||||
<2> We create a new `ExpiringSession` using our `SessionRepository` and assign it to a variable of type `S`.
|
||||
<3> We interact with the `ExpiringSession`.
|
||||
In our example, we demonstrate updating the amount of time the `ExpiringSession` can be inactive before it expires.
|
||||
<4> We now save the `ExpiringSession`.
|
||||
<1> We create a `SessionRepository` instance with a generic type, `S`, that extends `Session`. The generic type is defined in our class.
|
||||
<2> We create a new `Session` using our `SessionRepository` and assign it to a variable of type `S`.
|
||||
<3> We interact with the `Session`.
|
||||
In our example, we demonstrate updating the amount of time the `Session` can be inactive before it expires.
|
||||
<4> We now save the `Session`.
|
||||
This is why we needed the generic type `S`.
|
||||
The `SessionRepository` only allows saving `ExpiringSession` instances that were created or retrieved using the same `SessionRepository`.
|
||||
The `SessionRepository` only allows saving `Session` instances that were created or retrieved using the same `SessionRepository`.
|
||||
This allows for the `SessionRepository` to make implementation specific optimizations (i.e. only writing attributes that have changed).
|
||||
The last accessed time is automatically updated when the `ExpiringSession` is saved.
|
||||
<5> We retrieve the `ExpiringSession` from the `SessionRepository`.
|
||||
If the `ExpiringSession` were expired, the result would be null.
|
||||
The last accessed time is automatically updated when the `Session` is saved.
|
||||
<5> We retrieve the `Session` from the `SessionRepository`.
|
||||
If the `Session` were expired, the result would be null.
|
||||
|
||||
[[api-sessionrepository]]
|
||||
=== SessionRepository
|
||||
@@ -640,7 +636,7 @@ HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:a
|
||||
[[api-redisoperationssessionrepository-expiration]]
|
||||
===== Session Expiration
|
||||
|
||||
An expiration is associated to each session using the EXPIRE command based upon the `ExpiringSession.getMaxInactiveInterval()`.
|
||||
An expiration is associated to each session using the EXPIRE command based upon the `Session.getMaxInactiveInterval()`.
|
||||
For example:
|
||||
|
||||
----
|
||||
@@ -653,7 +649,7 @@ An expiration is set on the session itself five minutes after it actually expire
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The `SessionRepository.getSession(String)` method ensures that no expired sessions will be returned.
|
||||
The `SessionRepository.findById(String)` method ensures that no expired sessions will be returned.
|
||||
This means there is no need to check the expiration before using a session.
|
||||
====
|
||||
|
||||
@@ -776,7 +772,7 @@ redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed
|
||||
[[api-mapsessionrepository]]
|
||||
=== MapSessionRepository
|
||||
|
||||
The `MapSessionRepository` allows for persisting `ExpiringSession` in a `Map` with the key being the `ExpiringSession` id and the value being the `ExpiringSession`.
|
||||
The `MapSessionRepository` allows for persisting `Session` in a `Map` with the key being the `Session` id and the value being the `Session`.
|
||||
The implementation can be used with a `ConcurrentHashMap` as a testing or convenience mechanism.
|
||||
Alternatively, it can be used with distributed `Map` implementations. For example, it can be used with Hazelcast.
|
||||
|
||||
@@ -860,14 +856,14 @@ For example, with PostgreSQL database you would use the following schema script:
|
||||
|
||||
[source,sql,indent=0]
|
||||
----
|
||||
include::{session-main-resources-dir}org/springframework/session/jdbc/schema-postgresql.sql[]
|
||||
include::{session-jdbc-main-resources-dir}org/springframework/session/jdbc/schema-postgresql.sql[]
|
||||
----
|
||||
|
||||
And with MySQL database:
|
||||
|
||||
[source,sql,indent=0]
|
||||
----
|
||||
include::{session-main-resources-dir}org/springframework/session/jdbc/schema-mysql.sql[]
|
||||
include::{session-jdbc-main-resources-dir}org/springframework/session/jdbc/schema-mysql.sql[]
|
||||
----
|
||||
|
||||
==== Transaction management
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -38,7 +38,7 @@ import static org.mockito.Mockito.mock;
|
||||
@WebAppConfiguration
|
||||
public class HttpSessionConfigurationNoOpConfigureRedisActionXmlTests {
|
||||
@Autowired
|
||||
SessionRepositoryFilter<? extends ExpiringSession> filter;
|
||||
SessionRepositoryFilter<? extends Session> filter;
|
||||
|
||||
@Test
|
||||
public void redisConnectionFactoryNotUsedSinceNoValidation() {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package docs;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
@@ -26,7 +28,6 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
@@ -49,7 +50,7 @@ public class IndexDocTests {
|
||||
|
||||
@Test
|
||||
public void repositoryDemo() {
|
||||
RepositoryDemo<ExpiringSession> demo = new RepositoryDemo<>();
|
||||
RepositoryDemo<MapSession> demo = new RepositoryDemo<>();
|
||||
demo.repository = new MapSessionRepository();
|
||||
|
||||
demo.demo();
|
||||
@@ -68,7 +69,7 @@ public class IndexDocTests {
|
||||
|
||||
this.repository.save(toSave); // <4>
|
||||
|
||||
S session = this.repository.getSession(toSave.getId()); // <5>
|
||||
S session = this.repository.findById(toSave.getId()); // <5>
|
||||
|
||||
// <6>
|
||||
User user = session.getAttribute(ATTR_USER);
|
||||
@@ -81,24 +82,24 @@ public class IndexDocTests {
|
||||
|
||||
@Test
|
||||
public void expireRepositoryDemo() {
|
||||
ExpiringRepositoryDemo<ExpiringSession> demo = new ExpiringRepositoryDemo<>();
|
||||
ExpiringRepositoryDemo<MapSession> demo = new ExpiringRepositoryDemo<>();
|
||||
demo.repository = new MapSessionRepository();
|
||||
|
||||
demo.demo();
|
||||
}
|
||||
|
||||
// tag::expire-repository-demo[]
|
||||
public class ExpiringRepositoryDemo<S extends ExpiringSession> {
|
||||
public class ExpiringRepositoryDemo<S extends Session> {
|
||||
private SessionRepository<S> repository; // <1>
|
||||
|
||||
public void demo() {
|
||||
S toSave = this.repository.createSession(); // <2>
|
||||
// ...
|
||||
toSave.setMaxInactiveIntervalInSeconds(30); // <3>
|
||||
toSave.setMaxInactiveInterval(Duration.ofSeconds(30)); // <3>
|
||||
|
||||
this.repository.save(toSave); // <4>
|
||||
|
||||
S session = this.repository.getSession(toSave.getId()); // <5>
|
||||
S session = this.repository.findById(toSave.getId()); // <5>
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ public class IndexDocTests {
|
||||
public void newRedisOperationsSessionRepository() {
|
||||
// tag::new-redisoperationssessionrepository[]
|
||||
LettuceConnectionFactory factory = new LettuceConnectionFactory();
|
||||
SessionRepository<? extends ExpiringSession> repository = new RedisOperationsSessionRepository(
|
||||
SessionRepository<? extends Session> repository = new RedisOperationsSessionRepository(
|
||||
factory);
|
||||
// end::new-redisoperationssessionrepository[]
|
||||
}
|
||||
@@ -120,7 +121,7 @@ public class IndexDocTests {
|
||||
@SuppressWarnings("unused")
|
||||
public void mapRepository() {
|
||||
// tag::new-mapsessionrepository[]
|
||||
SessionRepository<? extends ExpiringSession> repository = new MapSessionRepository();
|
||||
SessionRepository<? extends Session> repository = new MapSessionRepository();
|
||||
// end::new-mapsessionrepository[]
|
||||
}
|
||||
|
||||
@@ -136,7 +137,7 @@ public class IndexDocTests {
|
||||
|
||||
// ... configure transactionManager ...
|
||||
|
||||
SessionRepository<? extends ExpiringSession> repository =
|
||||
SessionRepository<? extends Session> repository =
|
||||
new JdbcOperationsSessionRepository(jdbcTemplate, transactionManager);
|
||||
// end::new-jdbcoperationssessionrepository[]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package docs.http;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -31,11 +33,13 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Mark Paluch
|
||||
* @since 1.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@@ -63,6 +67,7 @@ public abstract class AbstractHttpSessionListenerTests {
|
||||
RedisConnection connection = mock(RedisConnection.class);
|
||||
|
||||
given(factory.getConnection()).willReturn(connection);
|
||||
given(connection.getConfig(anyString())).willReturn(new Properties());
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package docs.security;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -48,7 +48,7 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv
|
||||
@ContextConfiguration(classes = RememberMeSecurityConfiguration.class)
|
||||
@WebAppConfiguration
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class RememberMeSecurityConfigurationTests<T extends ExpiringSession> {
|
||||
public class RememberMeSecurityConfigurationTests<T extends Session> {
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
@Autowired
|
||||
@@ -81,9 +81,9 @@ public class RememberMeSecurityConfigurationTests<T extends ExpiringSession> {
|
||||
Cookie cookie = result.getResponse().getCookie("SESSION");
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(Integer.MAX_VALUE);
|
||||
T session = this.sessions
|
||||
.getSession(new String(Base64.getDecoder().decode(cookie.getValue())));
|
||||
assertThat(session.getMaxInactiveIntervalInSeconds())
|
||||
.isEqualTo((int) TimeUnit.DAYS.toSeconds(30));
|
||||
.findById(new String(Base64.getDecoder().decode(cookie.getValue())));
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(Duration.ofDays(30));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package docs.security;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -48,7 +48,7 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class RememberMeSecurityConfigurationXmlTests<T extends ExpiringSession> {
|
||||
public class RememberMeSecurityConfigurationXmlTests<T extends Session> {
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
@Autowired
|
||||
@@ -81,9 +81,9 @@ public class RememberMeSecurityConfigurationXmlTests<T extends ExpiringSession>
|
||||
Cookie cookie = result.getResponse().getCookie("SESSION");
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(Integer.MAX_VALUE);
|
||||
T session = this.sessions
|
||||
.getSession(new String(Base64.getDecoder().decode(cookie.getValue())));
|
||||
assertThat(session.getMaxInactiveIntervalInSeconds())
|
||||
.isEqualTo((int) TimeUnit.DAYS.toSeconds(30));
|
||||
.findById(new String(Base64.getDecoder().decode(cookie.getValue())));
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(Duration.ofDays(30));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
|
||||
|
||||
/**
|
||||
@@ -33,7 +33,7 @@ import org.springframework.session.security.SpringSessionBackedSessionRegistry;
|
||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private FindByIndexNameSessionRepository<ExpiringSession> sessionRepository;
|
||||
private FindByIndexNameSessionRepository<Session> sessionRepository;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
@@ -48,8 +48,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Bean
|
||||
SpringSessionBackedSessionRegistry sessionRegistry() {
|
||||
return new SpringSessionBackedSessionRegistry<ExpiringSession>(
|
||||
this.sessionRepository);
|
||||
return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
|
||||
}
|
||||
}
|
||||
// end::class[]
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
springBootVersion=2.0.0.M1
|
||||
version=2.0.0.M2
|
||||
springBootVersion=2.0.0.M2
|
||||
version=2.0.0.M3
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.0.0.RC2'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-M4'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.0.0.M2'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Bismuth-M3'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.0.0.RC3'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-RC1'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.0.0.M3'
|
||||
}
|
||||
dependencies {
|
||||
dependency 'com.fasterxml.jackson.core:jackson-databind:2.9.0.pr3'
|
||||
@@ -12,7 +13,7 @@ dependencyManagement {
|
||||
dependency 'com.maxmind.geoip2:geoip2:2.3.1'
|
||||
dependency 'commons-codec:commons-codec:1.10'
|
||||
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
|
||||
dependency 'io.lettuce:lettuce-core:5.0.0.M2'
|
||||
dependency 'io.lettuce:lettuce-core:5.0.0.RC1'
|
||||
dependency 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1'
|
||||
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
|
||||
dependency 'javax.servlet:javax.servlet-api:3.1.0'
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Wed Jan 11 10:54:44 CST 2017
|
||||
#Fri Jul 07 11:11:13 CDT 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-bin.zip
|
||||
|
||||
19
gradlew
vendored
19
gradlew
vendored
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -154,16 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save ( ) {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-boot'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework.boot:spring-boot-starter-data-redis"
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
|
||||
compile "org.springframework.boot:spring-boot-starter-security"
|
||||
compile "org.springframework.boot:spring-boot-devtools"
|
||||
compile "org.springframework.session:spring-session"
|
||||
compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
|
||||
compile "org.webjars:bootstrap"
|
||||
compile "org.webjars:html5shiv"
|
||||
|
||||
6
samples/boot/gradle/dependency-management.gradle
Normal file
6
samples/boot/gradle/dependency-management.gradle
Normal file
@@ -0,0 +1,6 @@
|
||||
dependencyManagement {
|
||||
dependencies {
|
||||
dependency 'io.lettuce:lettuce-core:5.0.0.M2'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-boot'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-jdbc')
|
||||
compile "org.springframework.boot:spring-boot-starter-jdbc"
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
|
||||
compile "org.springframework.boot:spring-boot-starter-security"
|
||||
compile "org.springframework.boot:spring-boot-devtools"
|
||||
compile "org.springframework.session:spring-session"
|
||||
compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
|
||||
compile "org.webjars:bootstrap"
|
||||
compile "org.webjars:html5shiv"
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-boot'
|
||||
|
||||
dependencies {
|
||||
compile(project(':spring-session-data-redis')) {
|
||||
exclude module: 'jedis'
|
||||
}
|
||||
compile "org.springframework.boot:spring-boot-starter-data-redis"
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
|
||||
compile "org.springframework.boot:spring-boot-starter-security"
|
||||
compile "org.springframework.boot:spring-boot-devtools"
|
||||
compile "org.springframework.session:spring-session"
|
||||
compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
|
||||
compile "org.webjars:bootstrap"
|
||||
compile "org.webjars:html5shiv"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-boot'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework.boot:spring-boot-starter-data-redis"
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
|
||||
compile "org.springframework.boot:spring-boot-starter-security"
|
||||
compile "org.springframework.boot:spring-boot-devtools"
|
||||
compile "org.springframework.session:spring-session"
|
||||
compile "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
|
||||
compile "org.webjars:bootstrap"
|
||||
compile "org.webjars:html5shiv"
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-boot'
|
||||
|
||||
dependencies {
|
||||
compile(project(':spring-session-data-redis')) {
|
||||
exclude module: 'jedis'
|
||||
}
|
||||
compile "org.springframework.boot:spring-boot-starter-data-redis"
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
|
||||
compile "org.springframework.boot:spring-boot-starter-security"
|
||||
compile "org.springframework.boot:spring-boot-starter-data-jpa"
|
||||
compile "org.springframework.boot:spring-boot-starter-websocket"
|
||||
compile "org.springframework.boot:spring-boot-devtools"
|
||||
compile "org.springframework.session:spring-session"
|
||||
compile "org.springframework:spring-websocket"
|
||||
compile "org.springframework.security:spring-security-messaging"
|
||||
compile "org.springframework.security:spring-security-data"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile(project(':spring-session-data-redis')) {
|
||||
exclude module: 'jedis'
|
||||
}
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework:spring-web"
|
||||
compile "io.lettuce:lettuce-core"
|
||||
compile "org.webjars:bootstrap"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session')
|
||||
compile project(':spring-session-hazelcast')
|
||||
compile "org.springframework:spring-web"
|
||||
compile "org.springframework.security:spring-security-config"
|
||||
compile "org.springframework.security:spring-security-web"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile(project(':spring-session-data-redis')) {
|
||||
exclude module: 'jedis'
|
||||
}
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework:spring-web"
|
||||
compile "io.lettuce:lettuce-core"
|
||||
compile "org.webjars:bootstrap"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile(project(':spring-session-data-redis')) {
|
||||
exclude module: 'jedis'
|
||||
}
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "io.lettuce:lettuce-core"
|
||||
compile "org.springframework:spring-webmvc"
|
||||
compile "org.springframework.security:spring-security-config"
|
||||
@@ -19,4 +17,4 @@ dependencies {
|
||||
testCompile "org.assertj:assertj-core"
|
||||
testCompile "org.springframework:spring-test"
|
||||
testCompile "commons-codec:commons-codec"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import sample.mvc.MvcConfig;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -50,7 +50,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
public class RestMockMvcTests {
|
||||
|
||||
@Autowired
|
||||
SessionRepositoryFilter<? extends ExpiringSession> sessionRepositoryFilter;
|
||||
SessionRepositoryFilter<? extends Session> sessionRepositoryFilter;
|
||||
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile(project(':spring-session-data-redis')) {
|
||||
exclude module: 'jedis'
|
||||
}
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework:spring-web"
|
||||
compile "org.springframework.security:spring-security-config"
|
||||
compile "org.springframework.security:spring-security-web"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile(project(':spring-session-data-redis')) {
|
||||
exclude module: 'jedis'
|
||||
}
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework:spring-web"
|
||||
compile "io.lettuce:lettuce-core"
|
||||
compile "org.webjars:bootstrap"
|
||||
|
||||
@@ -61,7 +61,7 @@ public class UserAccountsFilter implements Filter {
|
||||
String alias = entry.getKey();
|
||||
String sessionId = entry.getValue();
|
||||
|
||||
Session session = repo.getSession(sessionId);
|
||||
Session session = repo.findById(sessionId);
|
||||
if (session == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session')
|
||||
compile project(':spring-session-core')
|
||||
compile "org.webjars:bootstrap"
|
||||
compile "org.webjars:webjars-taglib"
|
||||
compile "com.hazelcast:hazelcast-client"
|
||||
|
||||
@@ -23,52 +23,35 @@ import java.util.Map;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.FilterRegistration.Dynamic;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.annotation.WebListener;
|
||||
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.config.MapConfig;
|
||||
import com.hazelcast.config.NetworkConfig;
|
||||
import com.hazelcast.config.SerializerConfig;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
|
||||
@WebListener
|
||||
public class Initializer implements ServletContextListener {
|
||||
|
||||
private static final String SESSION_MAP_NAME = "spring:session:sessions";
|
||||
|
||||
private HazelcastInstance instance;
|
||||
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
String sessionMapName = "spring:session:sessions";
|
||||
ServletContext sc = sce.getServletContext();
|
||||
this.instance = createHazelcastInstance();
|
||||
Map<String, Session> sessions = this.instance.getMap(SESSION_MAP_NAME);
|
||||
|
||||
Config cfg = new Config();
|
||||
NetworkConfig netConfig = new NetworkConfig();
|
||||
netConfig.setPort(getAvailablePort());
|
||||
cfg.setNetworkConfig(netConfig);
|
||||
SerializerConfig serializer = new SerializerConfig().setTypeClass(Object.class)
|
||||
.setImplementation(new ObjectStreamSerializer());
|
||||
cfg.getSerializationConfig().addSerializerConfig(serializer);
|
||||
MapConfig mc = new MapConfig();
|
||||
mc.setName(sessionMapName);
|
||||
mc.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
cfg.addMapConfig(mc);
|
||||
|
||||
this.instance = Hazelcast.newHazelcastInstance(cfg);
|
||||
Map<String, ExpiringSession> sessions = this.instance.getMap(sessionMapName);
|
||||
|
||||
SessionRepository<ExpiringSession> sessionRepository = new MapSessionRepository(
|
||||
sessions);
|
||||
SessionRepositoryFilter<ExpiringSession> filter = new SessionRepositoryFilter<>(
|
||||
MapSessionRepository sessionRepository = new MapSessionRepository(sessions);
|
||||
SessionRepositoryFilter<? extends Session> filter = new SessionRepositoryFilter<>(
|
||||
sessionRepository);
|
||||
Dynamic fr = sc.addFilter("springSessionFilter", filter);
|
||||
|
||||
Dynamic fr = sce.getServletContext().addFilter("springSessionFilter", filter);
|
||||
fr.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
|
||||
}
|
||||
|
||||
@@ -76,6 +59,17 @@ public class Initializer implements ServletContextListener {
|
||||
this.instance.shutdown();
|
||||
}
|
||||
|
||||
private HazelcastInstance createHazelcastInstance() {
|
||||
Config config = new Config();
|
||||
|
||||
config.getNetworkConfig().setPort(getAvailablePort());
|
||||
|
||||
config.getMapConfig(SESSION_MAP_NAME)
|
||||
.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
return Hazelcast.newHazelcastInstance(config);
|
||||
}
|
||||
|
||||
private static int getAvailablePort() {
|
||||
ServerSocket socket = null;
|
||||
try {
|
||||
@@ -93,4 +87,5 @@ public class Initializer implements ServletContextListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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
|
||||
*
|
||||
* http://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 sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.hazelcast.nio.ObjectDataInput;
|
||||
import com.hazelcast.nio.ObjectDataOutput;
|
||||
import com.hazelcast.nio.serialization.StreamSerializer;
|
||||
|
||||
/**
|
||||
* A {@link StreamSerializer} that uses Java serialization to persist the session. This is
|
||||
* certainly not the most efficient way to persist sessions, but the example is intended
|
||||
* to demonstrate using minimal dependencies. For better serialization methods try using
|
||||
* <a href="https://github.com/EsotericSoftware/kryo">Kryo</a>.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class ObjectStreamSerializer implements StreamSerializer<Object> {
|
||||
public int getTypeId() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public void write(ObjectDataOutput objectDataOutput, Object object)
|
||||
throws IOException {
|
||||
ObjectOutputStream out = new ObjectOutputStream((OutputStream) objectDataOutput);
|
||||
out.writeObject(object);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public Object read(ObjectDataInput objectDataInput) throws IOException {
|
||||
ObjectInputStream in = new ObjectInputStream((InputStream) objectDataInput);
|
||||
try {
|
||||
return in.readObject();
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile(project(':spring-session-data-redis')) {
|
||||
exclude module: 'jedis'
|
||||
}
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework:spring-web"
|
||||
compile "io.lettuce:lettuce-core"
|
||||
compile "org.webjars:bootstrap"
|
||||
|
||||
@@ -5,25 +5,18 @@ description = "Spring Session"
|
||||
dependencies {
|
||||
compile "org.springframework:spring-jcl"
|
||||
|
||||
optional "io.projectreactor:reactor-core"
|
||||
optional "javax.servlet:javax.servlet-api"
|
||||
optional "org.springframework:spring-context"
|
||||
optional "org.springframework:spring-jdbc"
|
||||
optional "org.springframework:spring-messaging"
|
||||
optional "org.springframework:spring-web"
|
||||
optional "org.springframework:spring-webflux"
|
||||
optional "org.springframework:spring-websocket"
|
||||
optional "org.springframework.data:spring-data-redis"
|
||||
optional "org.springframework.security:spring-security-core"
|
||||
optional "org.springframework.security:spring-security-web"
|
||||
optional "com.hazelcast:hazelcast"
|
||||
|
||||
provided "javax.servlet:javax.servlet-api"
|
||||
|
||||
integrationTestCompile "redis.clients:jedis"
|
||||
integrationTestCompile "org.apache.commons:commons-pool2"
|
||||
integrationTestCompile "org.apache.derby:derby"
|
||||
integrationTestCompile "com.h2database:h2"
|
||||
integrationTestCompile "com.hazelcast:hazelcast-client"
|
||||
integrationTestCompile "org.hsqldb:hsqldb"
|
||||
|
||||
testCompile "io.projectreactor:reactor-test"
|
||||
testCompile "junit:junit"
|
||||
testCompile "org.mockito:mockito-core"
|
||||
testCompile "edu.umd.cs.mtc:multithreadedtc"
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
|
||||
/**
|
||||
* A {@link SessionRepository} backed by a {@link Map} and that uses a
|
||||
* {@link MapSession}. By default a {@link ConcurrentHashMap} is
|
||||
* used, but a custom {@link Map} can be injected to use distributed maps
|
||||
* provided by NoSQL stores like Redis and Hazelcast.
|
||||
*
|
||||
* <p>
|
||||
* The implementation does NOT support firing {@link SessionDeletedEvent} or
|
||||
* {@link SessionExpiredEvent}.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapReactorSessionRepository implements ReactorSessionRepository<MapSession> {
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private final Map<String, Session> sessions;
|
||||
|
||||
/**
|
||||
* Creates an instance backed by a {@link ConcurrentHashMap}.
|
||||
*/
|
||||
public MapReactorSessionRepository() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance backed by the provided {@link Map}. This allows
|
||||
* injecting a distributed {@link Map}.
|
||||
*
|
||||
* @param sessions the {@link Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapReactorSessionRepository(Map<String, Session> sessions) {
|
||||
if (sessions == null) {
|
||||
throw new IllegalArgumentException("sessions cannot be null");
|
||||
}
|
||||
this.sessions = sessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance backed by the provided {@link Map}. This allows
|
||||
* injecting a distributed {@link Map}.
|
||||
*
|
||||
* @param sessions the {@link Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapReactorSessionRepository(Session... sessions) {
|
||||
if (sessions == null) {
|
||||
throw new IllegalArgumentException("sessions cannot be null");
|
||||
}
|
||||
this.sessions = new ConcurrentHashMap<>();
|
||||
for (Session session : sessions) {
|
||||
this.performSave(new MapSession(session));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance backed by the provided {@link Map}. This allows
|
||||
* injecting a distributed {@link Map}.
|
||||
*
|
||||
* @param sessions the {@link Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapReactorSessionRepository(Iterable<Session> sessions) {
|
||||
if (sessions == null) {
|
||||
throw new IllegalArgumentException("sessions cannot be null");
|
||||
}
|
||||
this.sessions = new ConcurrentHashMap<>();
|
||||
for (Session session : sessions) {
|
||||
this.performSave(new MapSession(session));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
|
||||
this.defaultMaxInactiveInterval = Integer.valueOf(defaultMaxInactiveInterval);
|
||||
}
|
||||
|
||||
public Mono<Void> save(MapSession session) {
|
||||
return Mono.fromRunnable(() -> performSave(session));
|
||||
}
|
||||
|
||||
private void performSave(MapSession session) {
|
||||
if (!session.getId().equals(session.getOriginalId())) {
|
||||
this.sessions.remove(session.getOriginalId());
|
||||
session.setOriginalId(session.getId());
|
||||
}
|
||||
this.sessions.put(session.getId(), new MapSession(session));
|
||||
}
|
||||
|
||||
public Mono<MapSession> findById(String id) {
|
||||
return Mono.defer(() -> {
|
||||
Session saved = this.sessions.get(id);
|
||||
if (saved == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
if (saved.isExpired()) {
|
||||
delete(saved.getId());
|
||||
return Mono.empty();
|
||||
}
|
||||
return Mono.just(new MapSession(saved));
|
||||
});
|
||||
}
|
||||
|
||||
public Mono<Void> delete(String id) {
|
||||
return Mono.fromRunnable(() -> this.sessions.remove(id));
|
||||
}
|
||||
|
||||
public Mono<MapSession> createSession() {
|
||||
return Mono.defer(() -> {
|
||||
MapSession result = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
result.setMaxInactiveInterval(
|
||||
Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
return Mono.just(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,12 @@
|
||||
package org.springframework.session;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -41,31 +42,34 @@ import java.util.concurrent.TimeUnit;
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class MapSession implements ExpiringSession, Serializable {
|
||||
public final class MapSession implements Session, Serializable {
|
||||
/**
|
||||
* Default {@link #setMaxInactiveIntervalInSeconds(int)} (30 minutes).
|
||||
* Default {@link #setMaxInactiveInterval(Duration)} (30 minutes).
|
||||
*/
|
||||
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS = 1800;
|
||||
|
||||
private String id;
|
||||
private String originalId;
|
||||
private Map<String, Object> sessionAttrs = new HashMap<>();
|
||||
private long creationTime = System.currentTimeMillis();
|
||||
private long lastAccessedTime = this.creationTime;
|
||||
private Instant creationTime = Instant.now();
|
||||
private Instant lastAccessedTime = this.creationTime;
|
||||
|
||||
/**
|
||||
* Defaults to 30 minutes.
|
||||
*/
|
||||
private int maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
private Duration maxInactiveInterval = Duration.ofSeconds(DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
/**
|
||||
* Creates a new instance with a secure randomly generated identifier.
|
||||
*/
|
||||
public MapSession() {
|
||||
this(UUID.randomUUID().toString());
|
||||
this(generateId());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified id. This is preferred to the default
|
||||
* constructor when the id is known to prevent unnecessary consumption on entropy
|
||||
@@ -75,6 +79,7 @@ public final class MapSession implements ExpiringSession, Serializable {
|
||||
*/
|
||||
public MapSession(String id) {
|
||||
this.id = id;
|
||||
this.originalId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,27 +88,30 @@ public final class MapSession implements ExpiringSession, Serializable {
|
||||
* @param session the {@link Session} to initialize this {@link Session} with. Cannot
|
||||
* be null.
|
||||
*/
|
||||
public MapSession(ExpiringSession session) {
|
||||
public MapSession(Session session) {
|
||||
if (session == null) {
|
||||
throw new IllegalArgumentException("session cannot be null");
|
||||
}
|
||||
this.id = session.getId();
|
||||
this.originalId = this.id;
|
||||
this.sessionAttrs = new HashMap<>(
|
||||
session.getAttributeNames().size());
|
||||
for (String attrName : session.getAttributeNames()) {
|
||||
Object attrValue = session.getAttribute(attrName);
|
||||
this.sessionAttrs.put(attrName, attrValue);
|
||||
if (attrValue != null) {
|
||||
this.sessionAttrs.put(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
this.lastAccessedTime = session.getLastAccessedTime();
|
||||
this.creationTime = session.getCreationTime();
|
||||
this.maxInactiveInterval = session.getMaxInactiveIntervalInSeconds();
|
||||
this.maxInactiveInterval = session.getMaxInactiveInterval();
|
||||
}
|
||||
|
||||
public void setLastAccessedTime(long lastAccessedTime) {
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.lastAccessedTime = lastAccessedTime;
|
||||
}
|
||||
|
||||
public long getCreationTime() {
|
||||
public Instant getCreationTime() {
|
||||
return this.creationTime;
|
||||
}
|
||||
|
||||
@@ -111,28 +119,41 @@ public final class MapSession implements ExpiringSession, Serializable {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public long getLastAccessedTime() {
|
||||
String getOriginalId() {
|
||||
return this.originalId;
|
||||
}
|
||||
|
||||
void setOriginalId(String originalId) {
|
||||
this.originalId = originalId;
|
||||
}
|
||||
|
||||
public String changeSessionId() {
|
||||
String changedId = generateId();
|
||||
setId(changedId);
|
||||
return changedId;
|
||||
}
|
||||
|
||||
public Instant getLastAccessedTime() {
|
||||
return this.lastAccessedTime;
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(int interval) {
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
this.maxInactiveInterval = interval;
|
||||
}
|
||||
|
||||
public int getMaxInactiveIntervalInSeconds() {
|
||||
public Duration getMaxInactiveInterval() {
|
||||
return this.maxInactiveInterval;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return isExpired(System.currentTimeMillis());
|
||||
return isExpired(Instant.now());
|
||||
}
|
||||
|
||||
boolean isExpired(long now) {
|
||||
if (this.maxInactiveInterval < 0) {
|
||||
boolean isExpired(Instant now) {
|
||||
if (this.maxInactiveInterval.isNegative()) {
|
||||
return false;
|
||||
}
|
||||
return now - TimeUnit.SECONDS
|
||||
.toMillis(this.maxInactiveInterval) >= this.lastAccessedTime;
|
||||
return now.minus(this.maxInactiveInterval).compareTo(this.lastAccessedTime) >= 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -158,12 +179,11 @@ public final class MapSession implements ExpiringSession, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time that this {@link Session} was created in milliseconds since midnight
|
||||
* of 1/1/1970 GMT. The default is when the {@link Session} was instantiated.
|
||||
* @param creationTime the time that this {@link Session} was created in milliseconds
|
||||
* since midnight of 1/1/1970 GMT.
|
||||
* Sets the time that this {@link Session} was created. The default is when the
|
||||
* {@link Session} was instantiated.
|
||||
* @param creationTime the time that this {@link Session} was created.
|
||||
*/
|
||||
public void setCreationTime(long creationTime) {
|
||||
public void setCreationTime(Instant creationTime) {
|
||||
this.creationTime = creationTime;
|
||||
}
|
||||
|
||||
@@ -186,5 +206,9 @@ public final class MapSession implements ExpiringSession, Serializable {
|
||||
return this.id.hashCode();
|
||||
}
|
||||
|
||||
private static String generateId() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 7160779239673823561L;
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -36,14 +37,14 @@ import org.springframework.session.events.SessionExpiredEvent;
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
public class MapSessionRepository implements SessionRepository<ExpiringSession> {
|
||||
public class MapSessionRepository implements SessionRepository<MapSession> {
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link ExpiringSession#setMaxInactiveIntervalInSeconds(int)}.
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
private final Map<String, ExpiringSession> sessions;
|
||||
private final Map<String, Session> sessions;
|
||||
|
||||
/**
|
||||
* Creates an instance backed by a {@link java.util.concurrent.ConcurrentHashMap}.
|
||||
@@ -58,7 +59,7 @@ public class MapSessionRepository implements SessionRepository<ExpiringSession>
|
||||
*
|
||||
* @param sessions the {@link java.util.Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapSessionRepository(Map<String, ExpiringSession> sessions) {
|
||||
public MapSessionRepository(Map<String, Session> sessions) {
|
||||
if (sessions == null) {
|
||||
throw new IllegalArgumentException("sessions cannot be null");
|
||||
}
|
||||
@@ -67,7 +68,7 @@ public class MapSessionRepository implements SessionRepository<ExpiringSession>
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
* {@link ExpiringSession#setMaxInactiveIntervalInSeconds(int)}.
|
||||
* {@link Session#setMaxInactiveInterval(Duration)}.
|
||||
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
|
||||
* should be kept alive between client requests.
|
||||
*/
|
||||
@@ -75,30 +76,35 @@ public class MapSessionRepository implements SessionRepository<ExpiringSession>
|
||||
this.defaultMaxInactiveInterval = Integer.valueOf(defaultMaxInactiveInterval);
|
||||
}
|
||||
|
||||
public void save(ExpiringSession session) {
|
||||
public void save(MapSession session) {
|
||||
if (!session.getId().equals(session.getOriginalId())) {
|
||||
this.sessions.remove(session.getOriginalId());
|
||||
session.setOriginalId(session.getId());
|
||||
}
|
||||
this.sessions.put(session.getId(), new MapSession(session));
|
||||
}
|
||||
|
||||
public ExpiringSession getSession(String id) {
|
||||
ExpiringSession saved = this.sessions.get(id);
|
||||
public MapSession findById(String id) {
|
||||
Session saved = this.sessions.get(id);
|
||||
if (saved == null) {
|
||||
return null;
|
||||
}
|
||||
if (saved.isExpired()) {
|
||||
delete(saved.getId());
|
||||
deleteById(saved.getId());
|
||||
return null;
|
||||
}
|
||||
return new MapSession(saved);
|
||||
}
|
||||
|
||||
public void delete(String id) {
|
||||
public void deleteById(String id) {
|
||||
this.sessions.remove(id);
|
||||
}
|
||||
|
||||
public ExpiringSession createSession() {
|
||||
ExpiringSession result = new MapSession();
|
||||
public MapSession createSession() {
|
||||
MapSession result = new MapSession();
|
||||
if (this.defaultMaxInactiveInterval != null) {
|
||||
result.setMaxInactiveIntervalInSeconds(this.defaultMaxInactiveInterval);
|
||||
result.setMaxInactiveInterval(
|
||||
Duration.ofSeconds(this.defaultMaxInactiveInterval));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* A repository interface for managing {@link Session} instances.
|
||||
*
|
||||
* @param <S> the {@link Session} type
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactorSessionRepository<S extends Session> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Session} that is capable of being persisted by this
|
||||
* {@link ReactorSessionRepository}.
|
||||
*
|
||||
* <p>
|
||||
* This allows optimizations and customizations in how the {@link Session} is
|
||||
* persisted. For example, the implementation returned might keep track of the changes
|
||||
* ensuring that only the delta needs to be persisted on a save.
|
||||
* </p>
|
||||
*
|
||||
* @return a new {@link Session} that is capable of being persisted by this
|
||||
* {@link ReactorSessionRepository}
|
||||
*/
|
||||
Mono<S> createSession();
|
||||
|
||||
/**
|
||||
* Ensures the {@link Session} created by
|
||||
* {@link ReactorSessionRepository#createSession()} is saved.
|
||||
*
|
||||
* <p>
|
||||
* Some implementations may choose to save as the {@link Session} is updated by
|
||||
* returning a {@link Session} that immediately persists any changes. In this case,
|
||||
* this method may not actually do anything.
|
||||
* </p>
|
||||
*
|
||||
* @param session the {@link Session} to save
|
||||
* @return indicator of operation completion
|
||||
*/
|
||||
Mono<Void> save(S session);
|
||||
|
||||
/**
|
||||
* Gets the {@link Session} by the {@link Session#getId()} or null if no
|
||||
* {@link Session} is found.
|
||||
*
|
||||
* @param id the {@link Session#getId()} to lookup
|
||||
* @return the {@link Session} by the {@link Session#getId()} or null if no
|
||||
* {@link Session} is found.
|
||||
*/
|
||||
Mono<S> findById(String id);
|
||||
|
||||
/**
|
||||
* Deletes the {@link Session} with the given {@link Session#getId()} or does nothing
|
||||
* if the {@link Session} is not found.
|
||||
* @param id the {@link Session#getId()} to delete
|
||||
* @return indicator of operation completion
|
||||
*/
|
||||
Mono<Void> delete(String id);
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides a way to identify a user in an agnostic way. This allows the session to be
|
||||
* used by an HttpSession, WebSocket Session, or even non web related sessions.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface Session {
|
||||
|
||||
/**
|
||||
* Gets a unique string that identifies the {@link Session}.
|
||||
*
|
||||
* @return a unique string that identifies the {@link Session}
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Changes the session id. After invoking the {@link #getId()} will return a new identifier.
|
||||
* @return the new session id which {@link #getId()} will now return
|
||||
*/
|
||||
String changeSessionId();
|
||||
|
||||
/**
|
||||
* Gets the Object associated with the specified name or null if no Object is
|
||||
* associated to that name.
|
||||
*
|
||||
* @param <T> The return type of the attribute
|
||||
* @param attributeName the name of the attribute to get
|
||||
* @return the Object associated with the specified name or null if no Object is
|
||||
* associated to that name
|
||||
*/
|
||||
<T> T getAttribute(String attributeName);
|
||||
|
||||
/**
|
||||
* Return the session attribute value or if not present raise an
|
||||
* {@link IllegalArgumentException}.
|
||||
* @param name the attribute name
|
||||
* @param <T> the attribute type
|
||||
* @return the attribute value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> T getRequiredAttribute(String name) {
|
||||
T result = getAttribute(name);
|
||||
if (result == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Required attribute '" + name + "' is missing.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the session attribute value, or a default, fallback value.
|
||||
* @param name the attribute name
|
||||
* @param defaultValue a default value to return instead
|
||||
* @param <T> the attribute type
|
||||
* @return the attribute value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> T getAttributeOrDefault(String name, T defaultValue) {
|
||||
T result = getAttribute(name);
|
||||
return result == null ? defaultValue : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attribute names that have a value associated with it. Each value can be
|
||||
* passed into {@link org.springframework.session.Session#getAttribute(String)} to
|
||||
* obtain the attribute value.
|
||||
*
|
||||
* @return the attribute names that have a value associated with it.
|
||||
* @see #getAttribute(String)
|
||||
*/
|
||||
Set<String> getAttributeNames();
|
||||
|
||||
/**
|
||||
* Sets the attribute value for the provided attribute name. If the attributeValue is
|
||||
* null, it has the same result as removing the attribute with
|
||||
* {@link org.springframework.session.Session#removeAttribute(String)} .
|
||||
*
|
||||
* @param attributeName the attribute name to set
|
||||
* @param attributeValue the value of the attribute to set. If null, the attribute
|
||||
* will be removed.
|
||||
*/
|
||||
void setAttribute(String attributeName, Object attributeValue);
|
||||
|
||||
/**
|
||||
* Removes the attribute with the provided attribute name.
|
||||
* @param attributeName the name of the attribute to remove
|
||||
*/
|
||||
void removeAttribute(String attributeName);
|
||||
|
||||
/**
|
||||
* Gets the time when this session was created.
|
||||
*
|
||||
* @return the time when this session was created.
|
||||
*/
|
||||
Instant getCreationTime();
|
||||
|
||||
/**
|
||||
* Sets the last accessed time.
|
||||
*
|
||||
* @param lastAccessedTime the last accessed time
|
||||
*/
|
||||
void setLastAccessedTime(Instant lastAccessedTime);
|
||||
|
||||
/**
|
||||
* Gets the last time this {@link Session} was accessed.
|
||||
*
|
||||
* @return the last time the client sent a request associated with the session
|
||||
*/
|
||||
Instant getLastAccessedTime();
|
||||
|
||||
/**
|
||||
* Sets the maximum inactive interval between requests before this session will be
|
||||
* invalidated. A negative time indicates that the session will never timeout.
|
||||
*
|
||||
* @param interval the amount of time that the {@link Session} should be kept alive
|
||||
* between client requests.
|
||||
*/
|
||||
void setMaxInactiveInterval(Duration interval);
|
||||
|
||||
/**
|
||||
* Gets the maximum inactive interval between requests before this session will be
|
||||
* invalidated. A negative time indicates that the session will never timeout.
|
||||
*
|
||||
* @return the maximum inactive interval between requests before this session will be
|
||||
* invalidated. A negative time indicates that the session will never timeout.
|
||||
*/
|
||||
Duration getMaxInactiveInterval();
|
||||
|
||||
/**
|
||||
* Returns true if the session is expired.
|
||||
*
|
||||
* @return true if the session is expired, else false.
|
||||
*/
|
||||
boolean isExpired();
|
||||
|
||||
}
|
||||
@@ -62,12 +62,12 @@ public interface SessionRepository<S extends Session> {
|
||||
* @return the {@link Session} by the {@link Session#getId()} or null if no
|
||||
* {@link Session} is found.
|
||||
*/
|
||||
S getSession(String id);
|
||||
S findById(String id);
|
||||
|
||||
/**
|
||||
* Deletes the {@link Session} with the given {@link Session#getId()} or does nothing
|
||||
* if the {@link Session} is not found.
|
||||
* @param id the {@link org.springframework.session.Session#getId()} to delete
|
||||
*/
|
||||
void delete(String id);
|
||||
void deleteById(String id);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDestroyedEvent;
|
||||
@@ -122,7 +122,7 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
|
||||
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
|
||||
SessionRepository<S> sessionRepository) {
|
||||
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(
|
||||
sessionRepository);
|
||||
@@ -23,7 +23,6 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
@@ -32,12 +31,12 @@ import org.springframework.session.SessionRepository;
|
||||
* Ensures that calling {@link #expireNow()} propagates to Spring Session, since this
|
||||
* session information contains only derived data and is not the authoritative source.
|
||||
*
|
||||
* @param <S> the {@link ExpiringSession} type.
|
||||
* @param <S> the {@link Session} type.
|
||||
* @author Joris Kuipers
|
||||
* @author Vedran Pavic
|
||||
* @since 1.3
|
||||
*/
|
||||
class SpringSessionBackedSessionInformation<S extends ExpiringSession>
|
||||
class SpringSessionBackedSessionInformation<S extends Session>
|
||||
extends SessionInformation {
|
||||
|
||||
static final String EXPIRED_ATTR = SpringSessionBackedSessionInformation.class
|
||||
@@ -53,9 +52,10 @@ class SpringSessionBackedSessionInformation<S extends ExpiringSession>
|
||||
SpringSessionBackedSessionInformation(S session,
|
||||
SessionRepository<S> sessionRepository) {
|
||||
super(resolvePrincipal(session), session.getId(),
|
||||
new Date(session.getLastAccessedTime()));
|
||||
Date.from(session.getLastAccessedTime()));
|
||||
this.sessionRepository = sessionRepository;
|
||||
if (Boolean.TRUE.equals(session.getAttribute(EXPIRED_ATTR))) {
|
||||
Boolean expired = session.getAttribute(EXPIRED_ATTR);
|
||||
if (Boolean.TRUE.equals(expired)) {
|
||||
super.expireNow();
|
||||
}
|
||||
}
|
||||
@@ -72,8 +72,10 @@ class SpringSessionBackedSessionInformation<S extends ExpiringSession>
|
||||
if (principalName != null) {
|
||||
return principalName;
|
||||
}
|
||||
SecurityContext securityContext = session.getAttribute(SPRING_SECURITY_CONTEXT);
|
||||
if (securityContext != null && securityContext.getAuthentication() != null) {
|
||||
SecurityContext securityContext = session
|
||||
.getAttribute(SPRING_SECURITY_CONTEXT);
|
||||
if (securityContext != null
|
||||
&& securityContext.getAuthentication() != null) {
|
||||
return securityContext.getAuthentication().getName();
|
||||
}
|
||||
return "";
|
||||
@@ -87,7 +89,7 @@ class SpringSessionBackedSessionInformation<S extends ExpiringSession>
|
||||
+ "sessions was exceeded");
|
||||
}
|
||||
super.expireNow();
|
||||
S session = this.sessionRepository.getSession(getSessionId());
|
||||
S session = this.sessionRepository.findById(getSessionId());
|
||||
if (session != null) {
|
||||
session.setAttribute(EXPIRED_ATTR, Boolean.TRUE);
|
||||
this.sessionRepository.save(session);
|
||||
@@ -24,8 +24,8 @@ import java.util.List;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -39,12 +39,12 @@ import org.springframework.util.Assert;
|
||||
* <p>
|
||||
* Does not support {@link #getAllPrincipals()}, since that information is not available.
|
||||
*
|
||||
* @param <S> the {@link ExpiringSession} type.
|
||||
* @param <S> the {@link Session} type.
|
||||
* @author Joris Kuipers
|
||||
* @author Vedran Pavic
|
||||
* @since 1.3
|
||||
*/
|
||||
public class SpringSessionBackedSessionRegistry<S extends ExpiringSession>
|
||||
public class SpringSessionBackedSessionRegistry<S extends Session>
|
||||
implements SessionRegistry {
|
||||
|
||||
private final FindByIndexNameSessionRepository<S> sessionRepository;
|
||||
@@ -70,7 +70,7 @@ public class SpringSessionBackedSessionRegistry<S extends ExpiringSession>
|
||||
for (S session : sessions) {
|
||||
if (includeExpiredSessions || !Boolean.TRUE.equals(session
|
||||
.getAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR))) {
|
||||
infos.add(new SpringSessionBackedSessionInformation<S>(session,
|
||||
infos.add(new SpringSessionBackedSessionInformation<>(session,
|
||||
this.sessionRepository));
|
||||
}
|
||||
}
|
||||
@@ -78,9 +78,9 @@ public class SpringSessionBackedSessionRegistry<S extends ExpiringSession>
|
||||
}
|
||||
|
||||
public SessionInformation getSessionInformation(String sessionId) {
|
||||
S session = this.sessionRepository.getSession(sessionId);
|
||||
S session = this.sessionRepository.findById(sessionId);
|
||||
if (session != null) {
|
||||
return new SpringSessionBackedSessionInformation<S>(session,
|
||||
return new SpringSessionBackedSessionInformation<>(session,
|
||||
this.sessionRepository);
|
||||
}
|
||||
return null;
|
||||
@@ -33,7 +33,6 @@ import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link HttpSessionStrategy} that uses a cookie to obtain the session from.
|
||||
@@ -121,7 +120,7 @@ import org.springframework.util.Assert;
|
||||
* entry.getValue();
|
||||
* </code>
|
||||
*
|
||||
* Session session = repo.getSession(sessionId); if(session == null) { continue; }
|
||||
* Session session = repo.findById(sessionId); if(session == null) { continue; }
|
||||
*
|
||||
* String username = session.getAttribute("username"); if(username == null) {
|
||||
* newSessionAlias = alias; continue; }
|
||||
@@ -293,7 +292,9 @@ public final class CookieHttpSessionStrategy
|
||||
* @param cookieSerializer the cookieSerializer to set. Cannot be null.
|
||||
*/
|
||||
public void setCookieSerializer(CookieSerializer cookieSerializer) {
|
||||
Assert.notNull(cookieSerializer, "cookieSerializer cannot be null");
|
||||
if (cookieSerializer == null) {
|
||||
throw new IllegalArgumentException("cookieSerializer cannot be null");
|
||||
}
|
||||
this.cookieSerializer = cookieSerializer;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link HttpSessionStrategy} that uses a header to obtain the session from.
|
||||
@@ -79,7 +78,9 @@ public class HeaderHttpSessionStrategy implements HttpSessionStrategy {
|
||||
* @param headerName the name of the header to obtain the session id from.
|
||||
*/
|
||||
public void setHeaderName(String headerName) {
|
||||
Assert.notNull(headerName, "headerName cannot be null");
|
||||
if (headerName == null) {
|
||||
throw new IllegalArgumentException("headerName cannot be null");
|
||||
}
|
||||
this.headerName = headerName;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.NoSuchElementException;
|
||||
@@ -25,23 +26,23 @@ import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpSessionContext;
|
||||
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
|
||||
/**
|
||||
* Adapts Spring Session's {@link ExpiringSession} to an {@link HttpSession}.
|
||||
* Adapts Spring Session's {@link Session} to an {@link HttpSession}.
|
||||
*
|
||||
* @param <S> the {@link ExpiringSession} type
|
||||
* @param <S> the {@link Session} type
|
||||
* @author Rob Winch
|
||||
* @since 1.1
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class ExpiringSessionHttpSession<S extends ExpiringSession> implements HttpSession {
|
||||
class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
private S session;
|
||||
private final ServletContext servletContext;
|
||||
private boolean invalidated;
|
||||
private boolean old;
|
||||
|
||||
ExpiringSessionHttpSession(S session, ServletContext servletContext) {
|
||||
HttpSessionAdapter(S session, ServletContext servletContext) {
|
||||
this.session = session;
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
@@ -56,7 +57,7 @@ class ExpiringSessionHttpSession<S extends ExpiringSession> implements HttpSessi
|
||||
|
||||
public long getCreationTime() {
|
||||
checkState();
|
||||
return this.session.getCreationTime();
|
||||
return this.session.getCreationTime().toEpochMilli();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@@ -65,7 +66,7 @@ class ExpiringSessionHttpSession<S extends ExpiringSession> implements HttpSessi
|
||||
|
||||
public long getLastAccessedTime() {
|
||||
checkState();
|
||||
return this.session.getLastAccessedTime();
|
||||
return this.session.getLastAccessedTime().toEpochMilli();
|
||||
}
|
||||
|
||||
public ServletContext getServletContext() {
|
||||
@@ -73,11 +74,11 @@ class ExpiringSessionHttpSession<S extends ExpiringSession> implements HttpSessi
|
||||
}
|
||||
|
||||
public void setMaxInactiveInterval(int interval) {
|
||||
this.session.setMaxInactiveIntervalInSeconds(interval);
|
||||
this.session.setMaxInactiveInterval(Duration.ofSeconds(interval));
|
||||
}
|
||||
|
||||
public int getMaxInactiveInterval() {
|
||||
return this.session.getMaxInactiveIntervalInSeconds();
|
||||
return (int) this.session.getMaxInactiveInterval().getSeconds();
|
||||
}
|
||||
|
||||
public HttpSessionContext getSessionContext() {
|
||||
@@ -24,7 +24,7 @@ import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDestroyedEvent;
|
||||
@@ -73,8 +73,8 @@ public class SessionEventHttpSessionListenerAdapter
|
||||
}
|
||||
|
||||
private HttpSessionEvent createHttpSessionEvent(AbstractSessionEvent event) {
|
||||
ExpiringSession session = event.getSession();
|
||||
HttpSession httpSession = new ExpiringSessionHttpSession<>(session,
|
||||
Session session = event.getSession();
|
||||
HttpSession httpSession = new HttpSessionAdapter<>(session,
|
||||
this.context);
|
||||
HttpSessionEvent httpSessionEvent = new HttpSessionEvent(httpSession);
|
||||
return httpSessionEvent;
|
||||
@@ -17,9 +17,7 @@
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.time.Instant;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletContext;
|
||||
@@ -33,7 +31,6 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
@@ -56,7 +53,7 @@ import org.springframework.session.SessionRepository;
|
||||
* <li>The session id is looked up using
|
||||
* {@link HttpSessionStrategy#getRequestedSessionId(javax.servlet.http.HttpServletRequest)}
|
||||
* . The default is to look in a cookie named SESSION.</li>
|
||||
* <li>The session id of newly created {@link org.springframework.session.ExpiringSession}
|
||||
* <li>The session id of newly created {@link org.springframework.session.Session}
|
||||
* is sent to the client using
|
||||
* <li>The client is notified that the session id is no longer valid with
|
||||
* {@link HttpSessionStrategy#onInvalidateSession(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
|
||||
@@ -69,12 +66,12 @@ import org.springframework.session.SessionRepository;
|
||||
* persisted properly.
|
||||
* </p>
|
||||
*
|
||||
* @param <S> the {@link ExpiringSession} type.
|
||||
* @param <S> the {@link Session} type.
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
|
||||
public class SessionRepositoryFilter<S extends ExpiringSession>
|
||||
public class SessionRepositoryFilter<S extends Session>
|
||||
extends OncePerRequestFilter {
|
||||
private static final String SESSION_LOGGER_NAME = SessionRepositoryFilter.class
|
||||
.getName().concat(".SESSION_LOGGER");
|
||||
@@ -274,30 +271,7 @@ public class SessionRepositoryFilter<S extends ExpiringSession>
|
||||
"Cannot change session ID. There is no session associated with this request.");
|
||||
}
|
||||
|
||||
// eagerly get session attributes in case implementation lazily loads them
|
||||
Map<String, Object> attrs = new HashMap<>();
|
||||
Enumeration<String> iAttrNames = session.getAttributeNames();
|
||||
while (iAttrNames.hasMoreElements()) {
|
||||
String attrName = iAttrNames.nextElement();
|
||||
Object value = session.getAttribute(attrName);
|
||||
|
||||
attrs.put(attrName, value);
|
||||
}
|
||||
|
||||
SessionRepositoryFilter.this.sessionRepository.delete(session.getId());
|
||||
HttpSessionWrapper original = getCurrentSession();
|
||||
setCurrentSession(null);
|
||||
|
||||
HttpSessionWrapper newSession = getSession();
|
||||
original.setSession(newSession.getSession());
|
||||
|
||||
newSession.setMaxInactiveInterval(session.getMaxInactiveInterval());
|
||||
for (Map.Entry<String, Object> attr : attrs.entrySet()) {
|
||||
String attrName = attr.getKey();
|
||||
Object attrValue = attr.getValue();
|
||||
newSession.setAttribute(attrName, attrValue);
|
||||
}
|
||||
return newSession.getId();
|
||||
return getCurrentSession().getSession().changeSessionId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -324,11 +298,11 @@ public class SessionRepositoryFilter<S extends ExpiringSession>
|
||||
|
||||
private S getSession(String sessionId) {
|
||||
S session = SessionRepositoryFilter.this.sessionRepository
|
||||
.getSession(sessionId);
|
||||
.findById(sessionId);
|
||||
if (session == null) {
|
||||
return null;
|
||||
}
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -370,7 +344,7 @@ public class SessionRepositoryFilter<S extends ExpiringSession>
|
||||
"For debugging purposes only (not an error)"));
|
||||
}
|
||||
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
currentSession = new HttpSessionWrapper(session, getServletContext());
|
||||
setCurrentSession(currentSession);
|
||||
return currentSession;
|
||||
@@ -402,7 +376,7 @@ public class SessionRepositoryFilter<S extends ExpiringSession>
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
private final class HttpSessionWrapper extends ExpiringSessionHttpSession<S> {
|
||||
private final class HttpSessionWrapper extends HttpSessionAdapter<S> {
|
||||
|
||||
HttpSessionWrapper(S session, ServletContext servletContext) {
|
||||
super(session, servletContext);
|
||||
@@ -413,7 +387,7 @@ public class SessionRepositoryFilter<S extends ExpiringSession>
|
||||
super.invalidate();
|
||||
SessionRepositoryRequestWrapper.this.requestedSessionInvalidated = true;
|
||||
setCurrentSession(null);
|
||||
SessionRepositoryFilter.this.sessionRepository.delete(getId());
|
||||
SessionRepositoryFilter.this.sessionRepository.deleteById(getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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.web.server.session;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.session.ReactorSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebSession;
|
||||
import org.springframework.web.server.session.CookieWebSessionIdResolver;
|
||||
import org.springframework.web.server.session.WebSessionIdResolver;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
import org.springframework.web.server.session.WebSessionStore;
|
||||
|
||||
/**
|
||||
* The {@link WebSessionManager} implementation backed by
|
||||
* {@link ReactorSessionRepository}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SpringSessionWebSessionManager implements WebSessionManager {
|
||||
|
||||
private final SpringSessionWebSessionStore<? extends Session> sessionStore;
|
||||
|
||||
private WebSessionIdResolver sessionIdResolver = new CookieWebSessionIdResolver();
|
||||
|
||||
private Clock clock = Clock.system(ZoneOffset.UTC);
|
||||
|
||||
public SpringSessionWebSessionManager(
|
||||
ReactorSessionRepository<? extends Session> sessionRepository) {
|
||||
this.sessionStore = new SpringSessionWebSessionStore<>(sessionRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured {@link WebSessionIdResolver}.
|
||||
* @return the configured {@link WebSessionIdResolver}
|
||||
*/
|
||||
private WebSessionIdResolver getSessionIdResolver() {
|
||||
return this.sessionIdResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the id resolution strategy.
|
||||
* <p>
|
||||
* By default an instance of {@link CookieWebSessionIdResolver}.
|
||||
* @param sessionIdResolver the resolver to use
|
||||
*/
|
||||
public void setSessionIdResolver(WebSessionIdResolver sessionIdResolver) {
|
||||
Assert.notNull(sessionIdResolver, "WebSessionIdResolver is required.");
|
||||
this.sessionIdResolver = sessionIdResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured {@link WebSessionStore}.
|
||||
* @return the configured {@link WebSessionStore}
|
||||
*/
|
||||
private WebSessionStore getSessionStore() {
|
||||
return this.sessionStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured clock for session {@code lastAccessTime} calculations.
|
||||
* @return the configured clock for session {@code lastAccessTime} calculations
|
||||
*/
|
||||
private Clock getClock() {
|
||||
return this.clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the {@link Clock} to use to set lastAccessTime on every created session
|
||||
* and to calculate if it is expired.
|
||||
* <p>
|
||||
* This may be useful to align to different timezone or to set the clock back in a
|
||||
* test, e.g. {@code Clock.offset(clock, Duration.ofMinutes(-31))} in order to
|
||||
* simulate session expiration.
|
||||
* <p>
|
||||
* By default this is {@code Clock.system(ZoneOffset.UTC)}.
|
||||
* @param clock the clock to use
|
||||
*/
|
||||
public void setClock(Clock clock) {
|
||||
Assert.notNull(clock, "'clock' is required.");
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<WebSession> getSession(ServerWebExchange exchange) {
|
||||
// @formatter:off
|
||||
return Mono.defer(() ->
|
||||
retrieveSession(exchange)
|
||||
.flatMap(session -> removeSessionIfExpired(exchange, session))
|
||||
.flatMap(session -> {
|
||||
Instant lastAccessTime = Instant.now(getClock());
|
||||
return this.sessionStore.setLastAccessedTime(session, lastAccessTime);
|
||||
})
|
||||
.switchIfEmpty(createSession(exchange))
|
||||
.doOnNext(session -> exchange.getResponse().beforeCommit(session::save)));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private Mono<WebSession> retrieveSession(ServerWebExchange exchange) {
|
||||
// @formatter:off
|
||||
return Flux.fromIterable(getSessionIdResolver().resolveSessionIds(exchange))
|
||||
.concatMap(this.sessionStore::retrieveSession)
|
||||
.cast(WebSession.class)
|
||||
.next();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private Mono<WebSession> removeSessionIfExpired(ServerWebExchange exchange,
|
||||
WebSession session) {
|
||||
if (session.isExpired()) {
|
||||
this.sessionIdResolver.expireSession(exchange);
|
||||
return this.sessionStore.removeSession(session.getId()).then(Mono.empty());
|
||||
}
|
||||
return Mono.just(session);
|
||||
}
|
||||
|
||||
private Mono<Void> saveSession(ServerWebExchange exchange, WebSession session) {
|
||||
if (session.isExpired()) {
|
||||
return Mono.error(new IllegalStateException(
|
||||
"Sessions are checked for expiration and have their "
|
||||
+ "lastAccessTime updated when first accessed during request processing. "
|
||||
+ "However this session is expired meaning that maxIdleTime elapsed "
|
||||
+ "before the call to session.save()."));
|
||||
}
|
||||
|
||||
if (!session.isStarted()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
// Force explicit start
|
||||
session.start();
|
||||
|
||||
if (hasNewSessionId(exchange, session)) {
|
||||
this.sessionIdResolver.setSessionId(exchange, session.getId());
|
||||
}
|
||||
|
||||
return this.sessionStore.storeSession(session);
|
||||
}
|
||||
|
||||
private boolean hasNewSessionId(ServerWebExchange exchange, WebSession session) {
|
||||
List<String> ids = getSessionIdResolver().resolveSessionIds(exchange);
|
||||
return ids.isEmpty() || !session.getId().equals(ids.get(0));
|
||||
}
|
||||
|
||||
private Mono<WebSession> createSession(ServerWebExchange exchange) {
|
||||
return this.sessionStore.createSession();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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.web.server.session;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.session.ReactorSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.WebSession;
|
||||
import org.springframework.web.server.session.WebSessionStore;
|
||||
|
||||
/**
|
||||
* The {@link WebSessionStore} implementation that provides the {@link WebSession}
|
||||
* implementation backed by a {@link Session} returned by the
|
||||
* {@link ReactorSessionRepository}.
|
||||
*
|
||||
* @param <S> the {@link Session} type
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
class SpringSessionWebSessionStore<S extends Session> implements WebSessionStore {
|
||||
|
||||
private final ReactorSessionRepository<S> sessions;
|
||||
|
||||
SpringSessionWebSessionStore(ReactorSessionRepository<S> sessions) {
|
||||
Assert.notNull(sessions, "sessions cannot be null");
|
||||
this.sessions = sessions;
|
||||
}
|
||||
|
||||
public Mono<WebSession> createSession() {
|
||||
return this.sessions.createSession().map(this::createSession);
|
||||
}
|
||||
|
||||
public Mono<WebSession> setLastAccessedTime(WebSession session,
|
||||
Instant lastAccessedTime) {
|
||||
@SuppressWarnings("unchecked")
|
||||
SpringSessionWebSession springSessionWebSession = (SpringSessionWebSession) session;
|
||||
springSessionWebSession.session.setLastAccessedTime(lastAccessedTime);
|
||||
return Mono.just(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> storeSession(WebSession session) {
|
||||
@SuppressWarnings("unchecked")
|
||||
SpringSessionWebSession springWebSession = (SpringSessionWebSession) session;
|
||||
return this.sessions.save(springWebSession.session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<WebSession> retrieveSession(String sessionId) {
|
||||
return this.sessions.findById(sessionId).map(this::existingSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> changeSessionId(String s, WebSession webSession) {
|
||||
return storeSession(webSession);
|
||||
}
|
||||
|
||||
private SpringSessionWebSession createSession(S session) {
|
||||
return new SpringSessionWebSession(session, State.NEW);
|
||||
}
|
||||
|
||||
private SpringSessionWebSession existingSession(S session) {
|
||||
return new SpringSessionWebSession(session, State.STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> removeSession(String sessionId) {
|
||||
return this.sessions.delete(sessionId);
|
||||
}
|
||||
|
||||
private enum State {
|
||||
NEW, STARTED
|
||||
}
|
||||
|
||||
private static class SpringSessionMap implements Map<String, Object> {
|
||||
|
||||
private final Session session;
|
||||
|
||||
private final Collection<Object> values = new SessionValues();
|
||||
|
||||
SpringSessionMap(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.session.getAttributeNames().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.session.getAttributeNames().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return key instanceof String
|
||||
&& this.session.getAttributeNames().contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return this.session.getAttributeNames().stream()
|
||||
.anyMatch(attrName -> this.session.getAttribute(attrName) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object get(Object key) {
|
||||
if (key instanceof String) {
|
||||
return this.session.getAttribute((String) key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
Object original = this.session.getAttribute(key);
|
||||
this.session.setAttribute(key, value);
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object remove(Object key) {
|
||||
if (key instanceof String) {
|
||||
String attrName = (String) key;
|
||||
Object original = this.session.getAttribute(attrName);
|
||||
this.session.removeAttribute(attrName);
|
||||
return original;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ?> m) {
|
||||
for (Entry<? extends String, ?> entry : m.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for (String attrName : this.session.getAttributeNames()) {
|
||||
remove(attrName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return this.session.getAttributeNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
return this.values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
Set<String> attrNames = keySet();
|
||||
Set<Entry<String, Object>> entries = new HashSet<>(attrNames.size());
|
||||
for (String attrName : attrNames) {
|
||||
Object value = this.session.getAttribute(attrName);
|
||||
entries.add(new AbstractMap.SimpleEntry<>(attrName, value));
|
||||
}
|
||||
return Collections.unmodifiableSet(entries);
|
||||
}
|
||||
|
||||
private class SessionValues extends AbstractCollection<Object> {
|
||||
|
||||
public Iterator<Object> iterator() {
|
||||
return new Iterator<Object>() {
|
||||
|
||||
private Iterator<Entry<String, Object>> i = entrySet().iterator();
|
||||
|
||||
public boolean hasNext() {
|
||||
return this.i.hasNext();
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
return this.i.next().getValue();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
this.i.remove();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return SpringSessionMap.this.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return SpringSessionMap.this.isEmpty();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
SpringSessionMap.this.clear();
|
||||
}
|
||||
|
||||
public boolean contains(Object v) {
|
||||
return SpringSessionMap.this.containsValue(v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts Spring Session's {@link Session} to a {@link WebSession}.
|
||||
*/
|
||||
private class SpringSessionWebSession implements WebSession {
|
||||
|
||||
private final S session;
|
||||
|
||||
private final Map<String, Object> attributes;
|
||||
|
||||
private AtomicReference<State> state = new AtomicReference<>();
|
||||
|
||||
private volatile transient Supplier<Mono<Void>> saveOperation = Mono::empty;
|
||||
|
||||
SpringSessionWebSession(S session, State state) {
|
||||
Assert.notNull(session, "session cannot be null");
|
||||
this.session = session;
|
||||
this.attributes = new SpringSessionMap(session);
|
||||
this.state.set(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.session.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> changeSessionId() {
|
||||
return Mono.defer(() -> {
|
||||
this.session.changeSessionId();
|
||||
return save();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.state.compareAndSet(State.NEW, State.STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
State value = this.state.get();
|
||||
return (State.STARTED.equals(value)
|
||||
|| (State.NEW.equals(value) && !getAttributes().isEmpty()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> save() {
|
||||
return this.saveOperation.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
return this.session.isExpired();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getCreationTime() {
|
||||
return this.session.getCreationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getLastAccessTime() {
|
||||
return this.session.getLastAccessedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getMaxIdleTime() {
|
||||
return this.session.getMaxInactiveInterval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxIdleTime(Duration maxIdleTime) {
|
||||
this.session.setMaxInactiveInterval(maxIdleTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Spring Session reactive web support.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.session.web.server.session;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.socket.handler.WebSocketConnectHandlerDecoratorFactory;
|
||||
@@ -55,7 +54,7 @@ import org.springframework.web.util.UrlPathHelper;
|
||||
* {@literal @Configuration}
|
||||
* {@literal @EnableScheduling}
|
||||
* {@literal @EnableWebSocketMessageBroker}
|
||||
* {@literal public class WebSocketConfig<S extends ExpiringSession> extends AbstractSessionWebSocketMessageBrokerConfigurer<S>} {
|
||||
* {@literal public class WebSocketConfig<S extends Session> extends AbstractSessionWebSocketMessageBrokerConfigurer<S>} {
|
||||
*
|
||||
* {@literal @Override}
|
||||
* protected void configureStompEndpoints(StompEndpointRegistry registry) {
|
||||
@@ -71,11 +70,11 @@ import org.springframework.web.util.UrlPathHelper;
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @param <S> the type of ExpiringSession
|
||||
* @param <S> the type of Session
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends ExpiringSession>
|
||||
public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends Session>
|
||||
extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Autowired
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.web.socket.server;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -31,7 +32,6 @@ import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.SimpMessageType;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
import org.springframework.messaging.support.ChannelInterceptorAdapter;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -41,12 +41,12 @@ import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
/**
|
||||
* <p>
|
||||
* Acts as a {@link ChannelInterceptor} and a {@link HandshakeInterceptor} to ensure the
|
||||
* {@link ExpiringSession#getLastAccessedTime()} is up to date.
|
||||
* {@link Session#getLastAccessedTime()} is up to date.
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Associates the {@link Session#getId()} with the WebSocket Session attributes when
|
||||
* the handshake is performed. This is later used when intercepting messages to ensure the
|
||||
* {@link ExpiringSession#getLastAccessedTime()} is updated.</li>
|
||||
* {@link Session#getLastAccessedTime()} is updated.</li>
|
||||
* <li>Intercepts {@link Message}'s that are have {@link SimpMessageType} that corresponds
|
||||
* to {@link #setMatchingMessageTypes(Set)} and updates the last accessed time of the
|
||||
* {@link Session}. If the {@link Session} is expired, the {@link Message} is prevented
|
||||
@@ -58,11 +58,11 @@ import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
* {@link ChannelInterceptor} and a {@link HandshakeInterceptor} .
|
||||
* </p>
|
||||
*
|
||||
* @param <S> the {@link ExpiringSession} type
|
||||
* @param <S> the {@link Session} type
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession>
|
||||
public final class SessionRepositoryMessageInterceptor<S extends Session>
|
||||
extends ChannelInterceptorAdapter implements HandshakeInterceptor {
|
||||
|
||||
private static final String SPRING_SESSION_ID_ATTR_NAME = "SPRING.SESSION.ID";
|
||||
@@ -88,7 +88,7 @@ public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession
|
||||
* <p>
|
||||
* Sets the {@link SimpMessageType} to match on. If the {@link Message} matches, then
|
||||
* {@link #preSend(Message, MessageChannel)} ensures the {@link Session} is not
|
||||
* expired and updates the {@link ExpiringSession#getLastAccessedTime()}
|
||||
* expired and updates the {@link Session#getLastAccessedTime()}
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
@@ -121,10 +121,10 @@ public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession
|
||||
String sessionId = sessionHeaders == null ? null
|
||||
: (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME);
|
||||
if (sessionId != null) {
|
||||
S session = this.sessionRepository.getSession(sessionId);
|
||||
S session = this.sessionRepository.findById(sessionId);
|
||||
if (session != null) {
|
||||
// update the last accessed time
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
this.sessionRepository.save(session);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapReactorSessionRepositoryTests {
|
||||
MapReactorSessionRepository repository;
|
||||
|
||||
MapSession session;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.repository = new MapReactorSessionRepository();
|
||||
this.session = new MapSession("session-id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorVarargsThenFound() {
|
||||
this.repository = new MapReactorSessionRepository(this.session);
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorVarargsWhenNullThenThrowsIllegalArgumentException() {
|
||||
Session[] sessions = null;
|
||||
new MapReactorSessionRepository(sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorIterableThenFound() {
|
||||
this.repository = new MapReactorSessionRepository(Arrays.asList(this.session));
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorIterableWhenNullThenThrowsIllegalArgumentException() {
|
||||
Iterable<Session> sessions = null;
|
||||
new MapReactorSessionRepository(sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorMapThenFound() {
|
||||
Map<String, Session> sessions = new HashMap<>();
|
||||
sessions.put(this.session.getId(), this.session);
|
||||
this.repository = new MapReactorSessionRepository(sessions);
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorMapWhenNullThenThrowsIllegalArgumentException() {
|
||||
Map<String, Session> sessions = null;
|
||||
new MapReactorSessionRepository(sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenNoSubscribersThenNotFound() {
|
||||
this.repository.save(this.session);
|
||||
|
||||
assertThat(this.repository.findById(this.session.getId()).block()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveWhenSubscriberThenFound() {
|
||||
this.repository.save(this.session).block();
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdWhenNotExpiredThenFound() {
|
||||
this.repository = new MapReactorSessionRepository(this.session);
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
assertThat(findByIdSession.getId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdWhenExpiredThenEmpty() {
|
||||
this.session.setMaxInactiveInterval(Duration.ofSeconds(1));
|
||||
this.session.setLastAccessedTime(Instant.now().minus(5, ChronoUnit.MINUTES));
|
||||
this.repository = new MapReactorSessionRepository(Arrays.asList(this.session));
|
||||
|
||||
assertThat(this.repository.findById(this.session.getId()).block()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenDefaultMaxInactiveIntervalThenDefaultMaxInactiveInterval() {
|
||||
Session session = this.repository.createSession().block();
|
||||
|
||||
assertThat(session).isInstanceOf(MapSession.class);
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(new MapSession().getMaxInactiveInterval());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenCustomMaxInactiveIntervalThenCustomMaxInactiveInterval() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval()
|
||||
.plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(
|
||||
(int) expectedMaxInterval.getSeconds());
|
||||
|
||||
Session session = this.repository.createSession().block();
|
||||
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expectedMaxInterval);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenNotYetSaved() {
|
||||
MapSession createSession = this.repository.createSession().block();
|
||||
|
||||
String originalId = createSession.getId();
|
||||
createSession.changeSessionId();
|
||||
|
||||
this.repository.save(createSession).block();
|
||||
|
||||
assertThat(this.repository.findById(originalId).block()).isNull();
|
||||
assertThat(this.repository.findById(createSession.getId()).block()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenSaved() {
|
||||
MapSession createSession = this.repository.createSession().block();
|
||||
|
||||
this.repository.save(createSession).block();
|
||||
|
||||
String originalId = createSession.getId();
|
||||
createSession.changeSessionId();
|
||||
|
||||
this.repository.save(createSession).block();
|
||||
|
||||
assertThat(this.repository.findById(originalId).block()).isNull();
|
||||
assertThat(this.repository.findById(createSession.getId()).block()).isNotNull();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class MapSessionRepositoryTests {
|
||||
MapSessionRepository repository;
|
||||
|
||||
MapSession session;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.repository = new MapSessionRepository();
|
||||
this.session = new MapSession();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionExpired() {
|
||||
this.session.setMaxInactiveInterval(Duration.ofSeconds(1));
|
||||
this.session.setLastAccessedTime(Instant.now().minus(5, ChronoUnit.MINUTES));
|
||||
this.repository.save(this.session);
|
||||
|
||||
assertThat(this.repository.findById(this.session.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionDefaultExpiration() {
|
||||
Session session = this.repository.createSession();
|
||||
|
||||
assertThat(session).isInstanceOf(MapSession.class);
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(new MapSession().getMaxInactiveInterval());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionCustomDefaultExpiration() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval()
|
||||
.plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(
|
||||
(int) expectedMaxInterval.getSeconds());
|
||||
|
||||
Session session = this.repository.createSession();
|
||||
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expectedMaxInterval);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenNotYetSaved() {
|
||||
MapSession createSession = this.repository.createSession();
|
||||
|
||||
String originalId = createSession.getId();
|
||||
createSession.changeSessionId();
|
||||
|
||||
this.repository.save(createSession);
|
||||
|
||||
assertThat(this.repository.findById(originalId)).isNull();
|
||||
assertThat(this.repository.findById(createSession.getId())).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionIdWhenSaved() {
|
||||
MapSession createSession = this.repository.createSession();
|
||||
|
||||
this.repository.save(createSession);
|
||||
|
||||
String originalId = createSession.getId();
|
||||
createSession.changeSessionId();
|
||||
|
||||
this.repository.save(createSession);
|
||||
|
||||
assertThat(this.repository.findById(originalId)).isNull();
|
||||
assertThat(this.repository.findById(createSession.getId())).isNotNull();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.session;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -30,12 +32,53 @@ public class MapSessionTests {
|
||||
@Before
|
||||
public void setup() {
|
||||
this.session = new MapSession();
|
||||
this.session.setLastAccessedTime(1413258262962L);
|
||||
this.session.setLastAccessedTime(Instant.ofEpochMilli(1413258262962L));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullSession() {
|
||||
new MapSession((ExpiringSession) null);
|
||||
new MapSession((Session) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeWhenNullThenNull() {
|
||||
String result = this.session.getAttribute("attrName");
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeOrDefaultWhenNullThenDefaultValue() {
|
||||
String defaultValue = "default";
|
||||
String result = this.session.getAttributeOrDefault("attrName", defaultValue);
|
||||
assertThat(result).isEqualTo(defaultValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAttributeOrDefaultWhenNotNullThenDefaultValue() {
|
||||
String defaultValue = "default";
|
||||
String attrValue = "value";
|
||||
String attrName = "attrName";
|
||||
this.session.setAttribute(attrName, attrValue);
|
||||
|
||||
String result = this.session.getAttributeOrDefault(attrName, defaultValue);
|
||||
|
||||
assertThat(result).isEqualTo(attrValue);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void getRequiredAttributeWhenNullThenException() {
|
||||
this.session.getRequiredAttribute("attrName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequiredAttributeWhenNotNullThenReturns() {
|
||||
String attrValue = "value";
|
||||
String attrName = "attrName";
|
||||
this.session.setAttribute(attrName, attrValue);
|
||||
|
||||
String result = this.session.getRequiredAttribute("attrName");
|
||||
|
||||
assertThat(result).isEqualTo(attrValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,46 +112,50 @@ public class MapSessionTests {
|
||||
|
||||
@Test
|
||||
public void isExpiredExact() {
|
||||
long now = 1413260062962L;
|
||||
Instant now = Instant.ofEpochMilli(1413260062962L);
|
||||
assertThat(this.session.isExpired(now)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isExpiredOneMsTooSoon() {
|
||||
long now = 1413260062961L;
|
||||
Instant now = Instant.ofEpochMilli(1413260062961L);
|
||||
assertThat(this.session.isExpired(now)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isExpiredOneMsAfter() {
|
||||
long now = 1413260062963L;
|
||||
Instant now = Instant.ofEpochMilli(1413260062963L);
|
||||
assertThat(this.session.isExpired(now)).isTrue();
|
||||
}
|
||||
|
||||
static class CustomSession implements ExpiringSession {
|
||||
static class CustomSession implements Session {
|
||||
|
||||
public long getCreationTime() {
|
||||
return 0;
|
||||
public Instant getCreationTime() {
|
||||
return Instant.EPOCH;
|
||||
}
|
||||
|
||||
public String changeSessionId() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return "id";
|
||||
}
|
||||
|
||||
public void setLastAccessedTime(long lastAccessedTime) {
|
||||
public void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public long getLastAccessedTime() {
|
||||
return 0;
|
||||
public Instant getLastAccessedTime() {
|
||||
return Instant.EPOCH;
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(int interval) {
|
||||
public void setMaxInactiveInterval(Duration interval) {
|
||||
|
||||
}
|
||||
|
||||
public int getMaxInactiveIntervalInSeconds() {
|
||||
return 0;
|
||||
public Duration getMaxInactiveInterval() {
|
||||
return Duration.ZERO;
|
||||
}
|
||||
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
@@ -34,8 +34,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.CookieSerializer;
|
||||
import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
@@ -65,7 +65,7 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
MockFilterChain chain;
|
||||
|
||||
@Autowired
|
||||
SessionRepositoryFilter<? extends ExpiringSession> sessionRepositoryFilter;
|
||||
SessionRepositoryFilter<? extends Session> sessionRepositoryFilter;
|
||||
|
||||
@Autowired
|
||||
CookieSerializer cookieSerializer;
|
||||
@@ -29,8 +29,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.MultiHttpSessionStrategy;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -58,7 +58,7 @@ public class EnableSpringHttpSessionCustomMultiHttpSessionStrategyTests {
|
||||
MockFilterChain chain;
|
||||
|
||||
@Autowired
|
||||
SessionRepositoryFilter<? extends ExpiringSession> sessionRepositoryFilter;
|
||||
SessionRepositoryFilter<? extends Session> sessionRepositoryFilter;
|
||||
|
||||
@Autowired
|
||||
MultiHttpSessionStrategy strategy;
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.springframework.session.security;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -30,13 +30,12 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.mock;
|
||||
@@ -56,42 +55,42 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
private static final String USER_NAME = "userName";
|
||||
|
||||
private static final User PRINCIPAL = new User(USER_NAME, "password",
|
||||
Collections.<GrantedAuthority>emptyList());
|
||||
Collections.emptyList());
|
||||
|
||||
private static final Date NOW = new Date();
|
||||
private static final Instant NOW = Instant.now();
|
||||
|
||||
@Mock
|
||||
private FindByIndexNameSessionRepository<ExpiringSession> sessionRepository;
|
||||
private FindByIndexNameSessionRepository<Session> sessionRepository;
|
||||
|
||||
@InjectMocks
|
||||
private SpringSessionBackedSessionRegistry<ExpiringSession> sessionRegistry;
|
||||
private SpringSessionBackedSessionRegistry<Session> sessionRegistry;
|
||||
|
||||
@Test
|
||||
public void sessionInformationForExistingSession() {
|
||||
ExpiringSession session = createSession(SESSION_ID, USER_NAME, NOW.getTime());
|
||||
when(this.sessionRepository.getSession(SESSION_ID)).thenReturn(session);
|
||||
Session session = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
when(this.sessionRepository.findById(SESSION_ID)).thenReturn(session);
|
||||
|
||||
SessionInformation sessionInfo = this.sessionRegistry
|
||||
.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(sessionInfo.getLastRequest()).isEqualTo(NOW);
|
||||
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionInformationForExpiredSession() {
|
||||
ExpiringSession session = createSession(SESSION_ID, USER_NAME, NOW.getTime());
|
||||
Session session = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
session.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR,
|
||||
Boolean.TRUE);
|
||||
when(this.sessionRepository.getSession(SESSION_ID)).thenReturn(session);
|
||||
when(this.sessionRepository.findById(SESSION_ID)).thenReturn(session);
|
||||
|
||||
SessionInformation sessionInfo = this.sessionRegistry
|
||||
.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(sessionInfo.getLastRequest()).isEqualTo(NOW);
|
||||
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isTrue();
|
||||
}
|
||||
@@ -126,8 +125,8 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
|
||||
@Test
|
||||
public void expireNow() {
|
||||
ExpiringSession session = createSession(SESSION_ID, USER_NAME, NOW.getTime());
|
||||
when(this.sessionRepository.getSession(SESSION_ID)).thenReturn(session);
|
||||
Session session = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
when(this.sessionRepository.findById(SESSION_ID)).thenReturn(session);
|
||||
|
||||
SessionInformation sessionInfo = this.sessionRegistry
|
||||
.getSessionInformation(SESSION_ID);
|
||||
@@ -136,16 +135,15 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
sessionInfo.expireNow();
|
||||
|
||||
assertThat(sessionInfo.isExpired()).isTrue();
|
||||
ArgumentCaptor<ExpiringSession> captor = ArgumentCaptor
|
||||
.forClass(ExpiringSession.class);
|
||||
ArgumentCaptor<Session> captor = ArgumentCaptor.forClass(Session.class);
|
||||
verify(this.sessionRepository).save(captor.capture());
|
||||
assertThat(captor.getValue().<Boolean>getAttribute(
|
||||
SpringSessionBackedSessionInformation.EXPIRED_ATTR))
|
||||
.isEqualTo(Boolean.TRUE);
|
||||
}
|
||||
|
||||
private ExpiringSession createSession(String sessionId, String userName,
|
||||
Long lastAccessed) {
|
||||
private Session createSession(String sessionId, String userName,
|
||||
Instant lastAccessed) {
|
||||
MapSession session = new MapSession(sessionId);
|
||||
session.setLastAccessedTime(lastAccessed);
|
||||
Authentication authentication = mock(Authentication.class);
|
||||
@@ -157,11 +155,11 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
}
|
||||
|
||||
private void setUpSessions() {
|
||||
ExpiringSession session1 = createSession(SESSION_ID, USER_NAME, NOW.getTime());
|
||||
Session session1 = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
session1.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR,
|
||||
Boolean.TRUE);
|
||||
ExpiringSession session2 = createSession(SESSION_ID2, USER_NAME, NOW.getTime());
|
||||
Map<String, ExpiringSession> sessions = new LinkedHashMap<>();
|
||||
Session session2 = createSession(SESSION_ID2, USER_NAME, NOW);
|
||||
Map<String, Session> sessions = new LinkedHashMap<>();
|
||||
sessions.put(session1.getId(), session1);
|
||||
sessions.put(session2.getId(), session2);
|
||||
when(this.sessionRepository.findByIndexNameAndIndexValue(
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
@@ -49,7 +50,6 @@ import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
@@ -74,11 +74,11 @@ public class SessionRepositoryFilterTests {
|
||||
@Mock
|
||||
private HttpSessionStrategy strategy;
|
||||
|
||||
private Map<String, ExpiringSession> sessions;
|
||||
private Map<String, Session> sessions;
|
||||
|
||||
private SessionRepository<ExpiringSession> sessionRepository;
|
||||
private SessionRepository<MapSession> sessionRepository;
|
||||
|
||||
private SessionRepositoryFilter<ExpiringSession> filter;
|
||||
private SessionRepositoryFilter<MapSession> filter;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
@@ -126,7 +126,7 @@ public class SessionRepositoryFilterTests {
|
||||
@Test
|
||||
public void doFilterCreateSetsLastAccessedTime() throws Exception {
|
||||
MapSession session = new MapSession();
|
||||
session.setLastAccessedTime(0L);
|
||||
session.setLastAccessedTime(Instant.EPOCH);
|
||||
this.sessionRepository = spy(this.sessionRepository);
|
||||
given(this.sessionRepository.createSession()).willReturn(session);
|
||||
this.filter = new SessionRepositoryFilter<>(
|
||||
@@ -422,7 +422,7 @@ public class SessionRepositoryFilterTests {
|
||||
public void doFilterSetsCookieIfChanged() throws Exception {
|
||||
this.sessionRepository = new MapSessionRepository() {
|
||||
@Override
|
||||
public ExpiringSession getSession(String id) {
|
||||
public MapSession findById(String id) {
|
||||
return createSession();
|
||||
}
|
||||
};
|
||||
@@ -539,7 +539,7 @@ public class SessionRepositoryFilterTests {
|
||||
// the old session was removed
|
||||
final String changedSessionId = getSessionCookie().getValue();
|
||||
assertThat(originalSessionId).isNotEqualTo(changedSessionId);
|
||||
assertThat(this.sessionRepository.getSession(originalSessionId)).isNull();
|
||||
assertThat(this.sessionRepository.findById(originalSessionId)).isNull();
|
||||
|
||||
nextRequest();
|
||||
|
||||
@@ -1051,7 +1051,7 @@ public class SessionRepositoryFilterTests {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.getSession(id)).isNotNull();
|
||||
.findById(id)).isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1066,7 +1066,7 @@ public class SessionRepositoryFilterTests {
|
||||
wrappedResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
"Error");
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.getSession(id)).isNotNull();
|
||||
.findById(id)).isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1080,7 +1080,7 @@ public class SessionRepositoryFilterTests {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.sendRedirect("/");
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.getSession(id)).isNotNull();
|
||||
.findById(id)).isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1094,7 +1094,7 @@ public class SessionRepositoryFilterTests {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.flushBuffer();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.getSession(id)).isNotNull();
|
||||
.findById(id)).isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1108,7 +1108,7 @@ public class SessionRepositoryFilterTests {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.getOutputStream().flush();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.getSession(id)).isNotNull();
|
||||
.findById(id)).isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1122,7 +1122,7 @@ public class SessionRepositoryFilterTests {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.getOutputStream().close();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.getSession(id)).isNotNull();
|
||||
.findById(id)).isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1136,7 +1136,7 @@ public class SessionRepositoryFilterTests {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.getWriter().flush();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.getSession(id)).isNotNull();
|
||||
.findById(id)).isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1150,7 +1150,7 @@ public class SessionRepositoryFilterTests {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.getWriter().close();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.getSession(id)).isNotNull();
|
||||
.findById(id)).isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1187,7 +1187,7 @@ public class SessionRepositoryFilterTests {
|
||||
});
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) this.chain.getRequest();
|
||||
Session session = this.sessionRepository.getSession(request.getSession().getId());
|
||||
Session session = this.sessionRepository.findById(request.getSession().getId());
|
||||
verify(this.strategy).onNewSession(eq(session), any(HttpServletRequest.class),
|
||||
any(HttpServletResponse.class));
|
||||
}
|
||||
@@ -1256,8 +1256,7 @@ public class SessionRepositoryFilterTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void doFilterRequestSessionNoRequestSessionNoSessionRepositoryInteractions()
|
||||
throws Exception {
|
||||
SessionRepository<ExpiringSession> sessionRepository = spy(
|
||||
new MapSessionRepository());
|
||||
SessionRepository<MapSession> sessionRepository = spy(new MapSessionRepository());
|
||||
|
||||
this.filter = new SessionRepositoryFilter<>(sessionRepository);
|
||||
|
||||
@@ -1284,8 +1283,7 @@ public class SessionRepositoryFilterTests {
|
||||
|
||||
@Test
|
||||
public void doFilterLazySessionCreation() throws Exception {
|
||||
SessionRepository<ExpiringSession> sessionRepository = spy(
|
||||
new MapSessionRepository());
|
||||
SessionRepository<MapSession> sessionRepository = spy(new MapSessionRepository());
|
||||
|
||||
this.filter = new SessionRepositoryFilter<>(sessionRepository);
|
||||
|
||||
@@ -1301,10 +1299,9 @@ public class SessionRepositoryFilterTests {
|
||||
|
||||
@Test
|
||||
public void doFilterLazySessionUpdates() throws Exception {
|
||||
ExpiringSession session = this.sessionRepository.createSession();
|
||||
MapSession session = this.sessionRepository.createSession();
|
||||
this.sessionRepository.save(session);
|
||||
SessionRepository<ExpiringSession> sessionRepository = spy(
|
||||
this.sessionRepository);
|
||||
SessionRepository<MapSession> sessionRepository = spy(this.sessionRepository);
|
||||
setSessionCookie(session.getId());
|
||||
|
||||
this.filter = new SessionRepositoryFilter<>(sessionRepository);
|
||||
@@ -1366,7 +1363,7 @@ public class SessionRepositoryFilterTests {
|
||||
// will not find the session)
|
||||
HttpSession session = wrappedRequest.getSession(false);
|
||||
verify(SessionRepositoryFilterTests.this.sessionRepository, times(1))
|
||||
.getSession(nonExistantSessionId);
|
||||
.findById(nonExistantSessionId);
|
||||
assertThat(session).isNull();
|
||||
assertThat(SessionRepositoryFilterTests.this.request
|
||||
.getAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR))
|
||||
@@ -1375,7 +1372,7 @@ public class SessionRepositoryFilterTests {
|
||||
// Second call should not reach the sessionRepository
|
||||
session = wrappedRequest.getSession(false);
|
||||
verify(SessionRepositoryFilterTests.this.sessionRepository, times(1))
|
||||
.getSession(nonExistantSessionId); // still only called once
|
||||
.findById(nonExistantSessionId); // still only called once
|
||||
assertThat(session).isNull();
|
||||
assertThat(SessionRepositoryFilterTests.this.request
|
||||
.getAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR))
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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.web.server.session;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.session.ReactorSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebSession;
|
||||
import org.springframework.web.server.session.WebSessionIdResolver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringSessionWebSessionManager}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SpringSessionWebSessionManagerTests<S extends Session> {
|
||||
|
||||
@Mock
|
||||
private ReactorSessionRepository<S> sessions;
|
||||
|
||||
@Mock
|
||||
private WebSessionIdResolver resolver;
|
||||
|
||||
@Mock
|
||||
private S createSession;
|
||||
|
||||
@Mock
|
||||
private S findByIdSession;
|
||||
|
||||
private Mono<S> createSessionMono;
|
||||
|
||||
private ServerWebExchange exchange = MockServerHttpRequest.get("/").toExchange();
|
||||
|
||||
private SpringSessionWebSessionManager manager;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
given(this.createSession.getId()).willReturn("createSession-id");
|
||||
given(this.findByIdSession.getId()).willReturn("findByIdSession-id");
|
||||
this.createSessionMono = Mono.just(this.createSession);
|
||||
given(this.sessions.createSession()).willReturn(this.createSessionMono);
|
||||
this.manager = new SpringSessionWebSessionManager(this.sessions);
|
||||
this.manager.setSessionIdResolver(this.resolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionWhenDefaultSessionIdResolverFoundSessionUsed() {
|
||||
String findByIdSessionId = this.findByIdSession.getId();
|
||||
this.exchange = MockServerHttpRequest.get("/")
|
||||
.cookie(new HttpCookie("SESSION", findByIdSessionId)).toExchange();
|
||||
this.manager = new SpringSessionWebSessionManager(this.sessions);
|
||||
given(this.sessions.findById(findByIdSessionId))
|
||||
.willReturn(Mono.just(this.findByIdSession));
|
||||
|
||||
WebSession webSession = this.manager.getSession(this.exchange).block();
|
||||
|
||||
assertThat(webSession.getId()).isEqualTo(findByIdSessionId);
|
||||
verify(this.sessions).findById(findByIdSessionId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionWhenNewThenCreateSessionInvoked() {
|
||||
WebSession webSession = this.manager.getSession(this.exchange).block();
|
||||
|
||||
assertThat(webSession.getId()).isEqualTo(this.createSession.getId());
|
||||
verify(this.sessions).createSession();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionWhenNewAndPutThenSetAttributeInvoked() {
|
||||
String attrName = "attrName";
|
||||
String attrValue = "attrValue";
|
||||
|
||||
WebSession webSession = this.manager.getSession(this.exchange).block();
|
||||
webSession.getAttributes().put(attrName, attrValue);
|
||||
|
||||
verify(this.createSession).setAttribute(attrName, attrValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionWhenInvalidIdThenCreateSessionInvoked() {
|
||||
String invalidId = "invalid";
|
||||
String createSessionId = this.createSession.getId();
|
||||
given(this.sessions.findById(any())).willReturn(Mono.empty());
|
||||
given(this.resolver.resolveSessionIds(this.exchange))
|
||||
.willReturn(Collections.singletonList(invalidId));
|
||||
|
||||
WebSession webSession = this.manager.getSession(this.exchange).block();
|
||||
|
||||
assertThat(webSession.getId()).isEqualTo(createSessionId);
|
||||
verify(this.sessions).findById(invalidId);
|
||||
|
||||
Mono<String> mono = Mono.just("toTest");
|
||||
StepVerifier.create(mono).expectNoEvent(Duration.ZERO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionWhenValidIdThenFoundSessionUsed() {
|
||||
String findByIdSessionId = this.findByIdSession.getId();
|
||||
given(this.sessions.findById(findByIdSessionId))
|
||||
.willReturn(Mono.just(this.findByIdSession));
|
||||
given(this.resolver.resolveSessionIds(this.exchange))
|
||||
.willReturn(Arrays.asList(findByIdSessionId));
|
||||
|
||||
WebSession webSession = this.manager.getSession(this.exchange).block();
|
||||
|
||||
assertThat(webSession.getId()).isEqualTo(findByIdSessionId);
|
||||
verify(this.sessions).findById(findByIdSessionId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://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.web.server.session;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.session.ReactorSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.web.server.WebSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringSessionWebSessionStore}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
|
||||
@Mock
|
||||
private ReactorSessionRepository<S> sessionRepository;
|
||||
|
||||
@Mock
|
||||
private S createSession;
|
||||
|
||||
@Mock
|
||||
private S findByIdSession;
|
||||
|
||||
private SpringSessionWebSessionStore<S> webSessionStore;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.webSessionStore = new SpringSessionWebSessionStore<>(this.sessionRepository);
|
||||
given(this.sessionRepository.findById(any()))
|
||||
.willReturn(Mono.just(this.findByIdSession));
|
||||
given(this.sessionRepository.createSession())
|
||||
.willReturn(Mono.just(this.createSession));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorWhenNullRepositoryThenThrowsIllegalArgumentException() {
|
||||
new SpringSessionWebSessionStore<S>(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenNoAttributesThenNotStarted() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
assertThat(createdWebSession.isStarted()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenAddAttributeThenStarted() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
assertThat(createdWebSession.isStarted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndSizeThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.size()).isEqualTo(0);
|
||||
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
|
||||
assertThat(attributes.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndIsEmptyThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.isEmpty()).isTrue();
|
||||
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
|
||||
assertThat(attributes.isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndContainsKeyAndNotStringThenFalse() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.containsKey(1L)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndContainsKeyAndNotFoundThenFalse() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.containsKey("a")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndContainsKeyAndFoundThenTrue() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.containsKey("a")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndPutThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
attributes.put("a", "b");
|
||||
|
||||
verify(this.createSession).setAttribute("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndPutNullThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
attributes.put("a", null);
|
||||
|
||||
verify(this.createSession).setAttribute("a", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndRemoveThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
attributes.remove("a");
|
||||
|
||||
verify(this.createSession).removeAttribute("a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndPutAllThenDelegatesToCreateSession() {
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
attributes.putAll(Collections.singletonMap("a", "b"));
|
||||
|
||||
verify(this.createSession).setAttribute("a", "b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndClearThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
attributes.clear();
|
||||
|
||||
verify(this.createSession).removeAttribute("a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndKeySetThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.keySet()).containsExactly("a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndValuesThenDelegatesToCreateSession() {
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton("a"));
|
||||
given(this.createSession.getAttribute("a")).willReturn("b");
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
|
||||
assertThat(attributes.values()).containsExactly("b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSessionWhenGetAttributesAndEntrySetThenDelegatesToCreateSession() {
|
||||
String attrName = "attrName";
|
||||
given(this.createSession.getAttributeNames())
|
||||
.willReturn(Collections.singleton(attrName));
|
||||
String attrValue = "attrValue";
|
||||
given(this.createSession.getAttribute(attrName)).willReturn(attrValue);
|
||||
WebSession createdWebSession = this.webSessionStore.createSession().block();
|
||||
|
||||
Map<String, Object> attributes = createdWebSession.getAttributes();
|
||||
Set<Map.Entry<String, Object>> entries = attributes.entrySet();
|
||||
|
||||
assertThat(entries)
|
||||
.containsExactly(new AbstractMap.SimpleEntry<>(attrName, attrValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeSessionWhenInvokedThenSessionSaved() {
|
||||
given(this.sessionRepository.save(this.createSession)).willReturn(Mono.empty());
|
||||
WebSession createdSession = this.webSessionStore.createSession().block();
|
||||
|
||||
this.webSessionStore.storeSession(createdSession).block();
|
||||
|
||||
verify(this.sessionRepository).save(this.createSession);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveSessionThenStarted() {
|
||||
String id = "id";
|
||||
WebSession retrievedWebSession = this.webSessionStore.retrieveSession(id).block();
|
||||
|
||||
assertThat(retrievedWebSession.isStarted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeSessionWhenInvokedThenSessionSaved() {
|
||||
String sessionId = "session-id";
|
||||
given(this.sessionRepository.delete(sessionId)).willReturn(Mono.empty());
|
||||
|
||||
this.webSessionStore.removeSession(sessionId).block();
|
||||
|
||||
verify(this.sessionRepository).delete(sessionId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.web.socket.server;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
@@ -38,13 +39,13 @@ import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.SimpMessageType;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.longThat;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -53,17 +54,17 @@ import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SessionRepositoryMessageInterceptorTests {
|
||||
@Mock
|
||||
SessionRepository<ExpiringSession> sessionRepository;
|
||||
SessionRepository<Session> sessionRepository;
|
||||
@Mock
|
||||
MessageChannel channel;
|
||||
@Mock
|
||||
ExpiringSession session;
|
||||
Session session;
|
||||
|
||||
Message<?> createMessage;
|
||||
|
||||
SimpMessageHeaderAccessor headers;
|
||||
|
||||
SessionRepositoryMessageInterceptor<ExpiringSession> interceptor;
|
||||
SessionRepositoryMessageInterceptor<Session> interceptor;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@@ -75,7 +76,7 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
setMessageType(SimpMessageType.MESSAGE);
|
||||
String sessionId = "http-session";
|
||||
setSessionId(sessionId);
|
||||
given(this.sessionRepository.getSession(sessionId)).willReturn(this.session);
|
||||
given(this.sessionRepository.findById(sessionId)).willReturn(this.session);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@@ -146,7 +147,7 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
|
||||
verify(this.sessionRepository).getSession(anyString());
|
||||
verify(this.sessionRepository).findById(anyString());
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
|
||||
verify(this.session).setLastAccessedTime(longThat(isAlmostNow()));
|
||||
verify(this.session).setLastAccessedTime(argThat(isAlmostNow()));
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@@ -168,7 +169,7 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
|
||||
verify(this.session).setLastAccessedTime(longThat(isAlmostNow()));
|
||||
verify(this.session).setLastAccessedTime(argThat(isAlmostNow()));
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@@ -179,19 +180,19 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
|
||||
verify(this.session).setLastAccessedTime(longThat(isAlmostNow()));
|
||||
verify(this.session).setLastAccessedTime(argThat(isAlmostNow()));
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preSendUnsubscribeUpdatesLastUpdateTime() {
|
||||
setMessageType(SimpMessageType.UNSUBSCRIBE);
|
||||
this.session.setLastAccessedTime(0L);
|
||||
this.session.setLastAccessedTime(Instant.EPOCH);
|
||||
|
||||
assertThat(this.interceptor.preSend(createMessage(), this.channel))
|
||||
.isSameAs(this.createMessage);
|
||||
|
||||
verify(this.session).setLastAccessedTime(longThat(isAlmostNow()));
|
||||
verify(this.session).setLastAccessedTime(argThat(isAlmostNow()));
|
||||
verify(this.sessionRepository).save(this.session);
|
||||
}
|
||||
|
||||
@@ -202,7 +203,7 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
|
||||
this.interceptor.preSend(createMessage(), this.channel);
|
||||
|
||||
verify(this.sessionRepository, times(0)).save(any(ExpiringSession.class));
|
||||
verify(this.sessionRepository, times(0)).save(any(Session.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -285,11 +286,11 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
return new AlmostNowMatcher();
|
||||
}
|
||||
|
||||
static class AlmostNowMatcher implements ArgumentMatcher<Long> {
|
||||
static class AlmostNowMatcher implements ArgumentMatcher<Instant> {
|
||||
|
||||
public boolean matches(Long argument) {
|
||||
public boolean matches(Instant argument) {
|
||||
long now = System.currentTimeMillis();
|
||||
long delta = now - argument;
|
||||
long delta = now - argument.toEpochMilli();
|
||||
return delta >= 0 && delta < TimeUnit.SECONDS.toMillis(3);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
apply plugin: 'io.spring.convention.spring-pom'
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
|
||||
description = "Aggregator for Spring Session and Spring Data Redis"
|
||||
description = "Spring Session Redis implementation"
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session')
|
||||
compile project(':spring-session-core')
|
||||
compile ("org.springframework.data:spring-data-redis") {
|
||||
exclude group: "org.slf4j", module: 'slf4j-api'
|
||||
exclude group: "org.slf4j", module: 'jcl-over-slf4j'
|
||||
}
|
||||
compile "redis.clients:jedis"
|
||||
compile "org.apache.commons:commons-pool2"
|
||||
|
||||
testCompile "javax.servlet:javax.servlet-api"
|
||||
testCompile "org.springframework:spring-web"
|
||||
testCompile "org.springframework.security:spring-security-core"
|
||||
|
||||
integrationTestCompile "redis.clients:jedis"
|
||||
integrationTestCompile "org.apache.commons:commons-pool2"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user