Compare commits
61 Commits
2.1.0.RC1
...
2.0.8.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe106ea7bb | ||
|
|
44ba9a97b7 | ||
|
|
0bdb106c30 | ||
|
|
43014247eb | ||
|
|
7e8917ac47 | ||
|
|
ad90867590 | ||
|
|
32c28013f3 | ||
|
|
c5b43f096c | ||
|
|
82759642c3 | ||
|
|
74c5260754 | ||
|
|
55b4f6f017 | ||
|
|
9099bd5d3a | ||
|
|
afa1f0890e | ||
|
|
ed3f6abf5d | ||
|
|
6c322631d4 | ||
|
|
9575be9b7d | ||
|
|
eae239febf | ||
|
|
b86b34ca2e | ||
|
|
c0a2220d3b | ||
|
|
c9d6ef7f01 | ||
|
|
c2c1311830 | ||
|
|
8c97a73b36 | ||
|
|
0886e237b6 | ||
|
|
c57a286e35 | ||
|
|
4c5f22900d | ||
|
|
8a8f379b37 | ||
|
|
dc4a0ce61b | ||
|
|
5944648c25 | ||
|
|
4502724e8c | ||
|
|
3c8cce652e | ||
|
|
425df2261f | ||
|
|
7c6b143964 | ||
|
|
39c640f456 | ||
|
|
f190da3757 | ||
|
|
34060cf0f6 | ||
|
|
ff10709f18 | ||
|
|
6e471f6441 | ||
|
|
a03be43450 | ||
|
|
7e8f500df0 | ||
|
|
b27742ce3e | ||
|
|
af44e71af0 | ||
|
|
055c2bcb93 | ||
|
|
3335deb5d5 | ||
|
|
1079e9e016 | ||
|
|
5fa52be8d1 | ||
|
|
c90952031f | ||
|
|
ce308ca513 | ||
|
|
c8f78e510e | ||
|
|
748ba70a01 | ||
|
|
dc1c7cdf02 | ||
|
|
3a972bef76 | ||
|
|
6b7fc3af08 | ||
|
|
535160bc92 | ||
|
|
606e08007e | ||
|
|
06fa33e48b | ||
|
|
d7c2e8e79c | ||
|
|
6bec95a298 | ||
|
|
9249a140c9 | ||
|
|
7f6dc801e0 | ||
|
|
83d46ad685 | ||
|
|
21cef2b7fa |
45
Jenkinsfile
vendored
45
Jenkinsfile
vendored
@@ -11,7 +11,7 @@ currentBuild.result = SUCCESS
|
||||
try {
|
||||
parallel check: {
|
||||
stage('Check') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
timeout(time: 30, unit: 'MINUTES') {
|
||||
node('ubuntu1804') {
|
||||
checkout scm
|
||||
try {
|
||||
@@ -28,38 +28,19 @@ try {
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk10: {
|
||||
stage('JDK 10') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('ubuntu1804') {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk10'}"]) {
|
||||
sh './gradlew clean test integrationTest --no-daemon --refresh-dependencies'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk10'
|
||||
throw e
|
||||
}
|
||||
springio: {
|
||||
stage('Spring IO') {
|
||||
node {
|
||||
checkout scm
|
||||
try {
|
||||
sh "./gradlew clean springIoCheck --stacktrace --no-daemon --refresh-dependencies -PplatformVersion=Cairo-BUILD-SNAPSHOT -PexcludeProjects='**/samples/**'"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk11: {
|
||||
stage('JDK 11') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('ubuntu1804') {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk11'}"]) {
|
||||
sh './gradlew clean test integrationTest --no-daemon --refresh-dependencies'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk11'
|
||||
throw e
|
||||
}
|
||||
catch(e) {
|
||||
currentBuild.result = 'FAILED: springio'
|
||||
throw e
|
||||
}
|
||||
finally {
|
||||
junit '**/build/spring-io*-results/*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
build.gradle
38
build.gradle
@@ -1,38 +1,20 @@
|
||||
buildscript {
|
||||
ext {
|
||||
releaseBuild = version.endsWith('RELEASE')
|
||||
snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
springBootVersion = '2.1.0.M3'
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven { url 'https://repo.spring.io/plugins-release/' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.19.RELEASE'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.18.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
}
|
||||
repositories {
|
||||
maven { url 'https://repo.spring.io/plugins-release' }
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
|
||||
group = 'org.springframework.session'
|
||||
description = 'Spring Session'
|
||||
|
||||
gradle.taskGraph.whenReady { graph ->
|
||||
def jacocoEnabled = graph.allTasks.any { it instanceof JacocoReport }
|
||||
subprojects {
|
||||
plugins.withType(JavaPlugin) {
|
||||
sourceCompatibility = 1.8
|
||||
}
|
||||
plugins.withType(JacocoPlugin) {
|
||||
tasks.withType(Test) {
|
||||
jacoco.enabled = jacocoEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ext.releaseBuild = version.endsWith('RELEASE')
|
||||
ext.snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
ext.milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
|
||||
|
||||
@@ -614,10 +614,9 @@ Spring Session's most basic API for using a `Session` is the `SessionRepository`
|
||||
This API is intentionally very simple, so that it is easy to provide additional implementations with basic functionality.
|
||||
|
||||
Some `SessionRepository` implementations may choose to implement `FindByIndexNameSessionRepository` also.
|
||||
For example, Spring's Redis, JDBC and Hazelcast support all implement `FindByIndexNameSessionRepository`.
|
||||
For example, Spring's Redis support implements `FindByIndexNameSessionRepository`.
|
||||
|
||||
The `FindByIndexNameSessionRepository` provides a method to look up all the sessions with a given index name and index value.
|
||||
As a common use case that is supported by all provided `FindByIndexNameSessionRepository` implementations, there's a convenient method to look up all the sessions for a particular user.
|
||||
The `FindByIndexNameSessionRepository` adds a single method to look up all the sessions for a particular user.
|
||||
This is done by ensuring that the session attribute with the name `FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` is populated with the username.
|
||||
It is the responsibility of the developer to ensure the attribute is populated since Spring Session is not aware of the authentication mechanism being used.
|
||||
An example of how this might be used can be seen below:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -52,7 +52,9 @@ public class FindByIndexNameSessionRepositoryTests {
|
||||
// tag::findby-username[]
|
||||
String username = "username";
|
||||
Map<String, Session> sessionIdToSession = this.sessionRepository
|
||||
.findByPrincipalName(username);
|
||||
.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
username);
|
||||
// end::findby-username[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -43,7 +43,6 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv
|
||||
|
||||
/**
|
||||
* @author rwinch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = RememberMeSecurityConfiguration.class)
|
||||
@@ -87,6 +86,5 @@ public class RememberMeSecurityConfigurationTests<T extends Session> {
|
||||
.isEqualTo(Duration.ofDays(30));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// end::class[]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -43,7 +43,6 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv
|
||||
|
||||
/**
|
||||
* @author rwinch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@@ -87,6 +86,5 @@ public class RememberMeSecurityConfigurationXmlTests<T extends Session> {
|
||||
.isEqualTo(Duration.ofDays(30));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// end::class[]
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
version=2.1.0.RC1
|
||||
springBootVersion=2.0.6.RELEASE
|
||||
version=2.0.8.RELEASE
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.6'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Californium-RELEASE'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.1.0.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Lovelace-RELEASE'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.1.0.RELEASE'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.9.0-rc2'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR14'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.0.11.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR12'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.0.10.RELEASE'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.10.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.10.5') {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.9.4') {
|
||||
entry 'hazelcast'
|
||||
entry 'hazelcast-client'
|
||||
}
|
||||
@@ -17,16 +17,16 @@ dependencyManagement {
|
||||
dependency 'com.h2database:h2:1.4.197'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:7.0.0.jre8'
|
||||
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
|
||||
dependency 'io.lettuce:lettuce-core:5.1.0.RELEASE'
|
||||
dependency 'io.lettuce:lettuce-core:5.1.3.RELEASE'
|
||||
dependency 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
dependency 'javax.servlet:javax.servlet-api:4.0.1'
|
||||
dependency 'javax.servlet:javax.servlet-api:3.1.0'
|
||||
dependency 'junit:junit:4.12'
|
||||
dependency 'mysql:mysql-connector-java:8.0.12'
|
||||
dependency 'mysql:mysql-connector-java:8.0.13'
|
||||
dependency 'org.apache.derby:derby:10.14.2.0'
|
||||
dependency 'org.assertj:assertj-core:3.11.1'
|
||||
dependency 'org.hsqldb:hsqldb:2.4.1'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.3.0'
|
||||
dependency 'org.mockito:mockito-core:2.22.0'
|
||||
dependency 'org.mockito:mockito-core:2.23.4'
|
||||
dependency 'org.postgresql:postgresql:42.2.5'
|
||||
}
|
||||
}
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -44,7 +44,10 @@ public class IndexController {
|
||||
@RequestMapping("/")
|
||||
public String index(Principal principal, Model model) {
|
||||
Collection<? extends Session> usersSessions = this.sessions
|
||||
.findByPrincipalName(principal.getName()).values();
|
||||
.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
principal.getName())
|
||||
.values();
|
||||
model.addAttribute("sessions", usersSessions);
|
||||
return "index";
|
||||
}
|
||||
@@ -53,8 +56,9 @@ public class IndexController {
|
||||
@RequestMapping(value = "/sessions/{sessionIdToDelete}", method = RequestMethod.DELETE)
|
||||
public String removeSession(Principal principal,
|
||||
@PathVariable String sessionIdToDelete) {
|
||||
Set<String> usersSessionIds = this.sessions
|
||||
.findByPrincipalName(principal.getName()).keySet();
|
||||
Set<String> usersSessionIds = this.sessions.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
principal.getName()).keySet();
|
||||
if (usersSessionIds.contains(sessionIdToDelete)) {
|
||||
this.sessions.deleteById(sessionIdToDelete);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -34,7 +34,6 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
|
||||
|
||||
/**
|
||||
* @author Eddú Meléndez
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@AutoConfigureMockMvc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -36,7 +36,7 @@ public class LoginPage extends BasePage {
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
|
||||
}
|
||||
|
||||
public Form form() {
|
||||
@@ -51,7 +51,7 @@ public class LoginPage extends BasePage {
|
||||
@FindBy(name = "password")
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(tagName = "button")
|
||||
@FindBy(name = "submit")
|
||||
private WebElement button;
|
||||
|
||||
public Form(SearchContext context) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -36,7 +36,7 @@ public class LoginPage extends BasePage {
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
|
||||
}
|
||||
|
||||
public Form form() {
|
||||
@@ -51,7 +51,7 @@ public class LoginPage extends BasePage {
|
||||
@FindBy(name = "password")
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(tagName = "button")
|
||||
@FindBy(name = "submit")
|
||||
private WebElement button;
|
||||
|
||||
public Form(SearchContext context) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -23,6 +23,5 @@ dependencies {
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
testCompile "org.springframework.security:spring-security-test"
|
||||
|
||||
integrationTestCompile "org.testcontainers:testcontainers"
|
||||
testCompile "org.testcontainers:testcontainers"
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -5,7 +5,7 @@ dependencyManagement {
|
||||
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 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
|
||||
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.32.0'
|
||||
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.29.3'
|
||||
dependency 'org.slf4j:jcl-over-slf4j:1.7.25'
|
||||
dependency 'org.slf4j:log4j-over-slf4j:1.7.25'
|
||||
dependency 'org.webjars:bootstrap:2.3.2'
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -36,7 +36,7 @@ public class LoginPage extends BasePage {
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
|
||||
}
|
||||
|
||||
public Form form() {
|
||||
@@ -51,7 +51,7 @@ public class LoginPage extends BasePage {
|
||||
@FindBy(name = "password")
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(tagName = "button")
|
||||
@FindBy(name = "submit")
|
||||
private WebElement button;
|
||||
|
||||
public Form(SearchContext context) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -42,7 +42,8 @@ public class SessionConfig {
|
||||
int port = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
config.getNetworkConfig()
|
||||
.setPort(port);
|
||||
.setPort(port)
|
||||
.getJoin().getMulticastConfig().setEnabled(false);
|
||||
|
||||
System.out.println("Hazelcast port #: " + port);
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -17,6 +17,8 @@ dependencies {
|
||||
testCompile "org.springframework.security:spring-security-test"
|
||||
testCompile "org.assertj:assertj-core"
|
||||
testCompile "org.springframework:spring-test"
|
||||
|
||||
integrationTestCompile "org.testcontainers:testcontainers"
|
||||
}
|
||||
|
||||
gretty {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -34,7 +34,7 @@ public class LoginPage extends BasePage {
|
||||
@FindBy(name = "password")
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(tagName = "button")
|
||||
@FindBy(css = "input[type='submit']")
|
||||
private WebElement button;
|
||||
|
||||
public LoginPage(WebDriver driver) {
|
||||
@@ -47,7 +47,7 @@ public class LoginPage extends BasePage {
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
|
||||
}
|
||||
|
||||
public HomePage login(String user, String password) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -64,7 +64,9 @@ public class Initializer implements ServletContextListener {
|
||||
private HazelcastInstance createHazelcastInstance() {
|
||||
Config config = new Config();
|
||||
|
||||
config.getNetworkConfig().setPort(getAvailablePort());
|
||||
config.getNetworkConfig()
|
||||
.setPort(getAvailablePort())
|
||||
.getJoin().getMulticastConfig().setEnabled(false);
|
||||
|
||||
config.getMapConfig(SESSION_MAP_NAME)
|
||||
.setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,16 +1,13 @@
|
||||
rootProject.name = 'spring-session'
|
||||
rootProject.name = 'spring-session-build'
|
||||
|
||||
FileTree buildFiles = fileTree(rootDir) {
|
||||
include '**/*.gradle'
|
||||
exclude 'build', '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*', 'out'
|
||||
exclude '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*'
|
||||
exclude '**/grails3'
|
||||
gradle.startParameter.projectProperties.get('excludeProjects')?.split(',')?.each { excludeProject ->
|
||||
exclude excludeProject
|
||||
}
|
||||
}
|
||||
|
||||
String rootDirPath = rootDir.absolutePath + File.separator
|
||||
buildFiles.each { buildFile ->
|
||||
buildFiles.each { File buildFile ->
|
||||
if (buildFile.name == 'build.gradle') {
|
||||
String buildFilePath = buildFile.parentFile.absolutePath
|
||||
String projectPath = buildFilePath.replace(rootDirPath, '').replace(File.separator, ':')
|
||||
|
||||
@@ -6,7 +6,6 @@ dependencies {
|
||||
compile "org.springframework:spring-jcl"
|
||||
|
||||
optional "io.projectreactor:reactor-core"
|
||||
optional "javax.annotation:javax.annotation-api"
|
||||
optional "javax.servlet:javax.servlet-api"
|
||||
optional "org.springframework:spring-context"
|
||||
optional "org.springframework:spring-jdbc"
|
||||
|
||||
@@ -19,22 +19,27 @@ package org.springframework.session;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Extends a basic {@link SessionRepository} to allow finding sessions by the specified
|
||||
* index name and index value.
|
||||
* Extends a basic {@link SessionRepository} to allow finding a session id by the
|
||||
* principal name. The principal name is defined by the {@link Session} attribute with the
|
||||
* name {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME}.
|
||||
*
|
||||
* @param <S> the type of Session being managed by this
|
||||
* {@link FindByIndexNameSessionRepository}
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public interface FindByIndexNameSessionRepository<S extends Session>
|
||||
extends SessionRepository<S> {
|
||||
|
||||
/**
|
||||
* A session index that contains the current principal name (i.e. username).
|
||||
* <p>
|
||||
* It is the responsibility of the developer to ensure the index is populated since
|
||||
* Spring Session is not aware of the authentication mechanism being used.
|
||||
* A common session attribute that contains the current principal name (i.e.
|
||||
* username).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It is the responsibility of the developer to ensure the attribute is populated
|
||||
* since Spring Session is not aware of the authentication mechanism being used.
|
||||
* </p>
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -42,34 +47,17 @@ public interface FindByIndexNameSessionRepository<S extends Session>
|
||||
.concat(".PRINCIPAL_NAME_INDEX_NAME");
|
||||
|
||||
/**
|
||||
* Find a {@link Map} of the session id to the {@link Session} of all sessions that
|
||||
* contain the specified index name index value.
|
||||
* Find a Map of the session id to the {@link Session} of all sessions that contain
|
||||
* the session attribute with the name
|
||||
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} and the value of
|
||||
* the specified principal name.
|
||||
*
|
||||
* @param indexName the name of the index (i.e.
|
||||
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME})
|
||||
* @param indexValue the value of the index to search for.
|
||||
* @return a {@code Map} (never {@code null}) of the session id to the {@code Session}
|
||||
* of all sessions that contain the specified index name and index value. If no
|
||||
* results are found, an empty {@code Map} is returned.
|
||||
* @return a Map (never null) of the session id to the {@link Session} of all sessions
|
||||
* that contain the session specified index name and the value of the specified index
|
||||
* name. If no results are found, an empty Map is returned.
|
||||
*/
|
||||
Map<String, S> findByIndexNameAndIndexValue(String indexName, String indexValue);
|
||||
|
||||
/**
|
||||
* Find a {@link Map} of the session id to the {@link Session} of all sessions that
|
||||
* contain the index with the name
|
||||
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} and the
|
||||
* specified principal name.
|
||||
*
|
||||
* @param principalName the principal name
|
||||
* @return a {@code Map} (never {@code null}) of the session id to the {@code Session}
|
||||
* of all sessions that contain the specified principal name. If no results are found,
|
||||
* an empty {@code Map} is returned.
|
||||
* @since 2.1.0
|
||||
*/
|
||||
default Map<String, S> findByPrincipalName(String principalName) {
|
||||
|
||||
return findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class MapSession implements Session, Serializable {
|
||||
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS = 1800;
|
||||
|
||||
private String id;
|
||||
private final String originalId;
|
||||
private String originalId;
|
||||
private Map<String, Object> sessionAttrs = new HashMap<>();
|
||||
private Instant creationTime = Instant.now();
|
||||
private Instant lastAccessedTime = this.creationTime;
|
||||
@@ -132,6 +132,10 @@ public final class MapSession implements Session, Serializable {
|
||||
return this.originalId;
|
||||
}
|
||||
|
||||
void setOriginalId(String originalId) {
|
||||
this.originalId = originalId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String changeSessionId() {
|
||||
String changedId = generateId();
|
||||
|
||||
@@ -73,6 +73,7 @@ public class MapSessionRepository implements SessionRepository<MapSession> {
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ public class ReactiveMapSessionRepository implements ReactiveSessionRepository<M
|
||||
return Mono.fromRunnable(() -> {
|
||||
if (!session.getId().equals(session.getOriginalId())) {
|
||||
this.sessions.remove(session.getOriginalId());
|
||||
session.setOriginalId(session.getId());
|
||||
}
|
||||
this.sessions.put(session.getId(), new MapSession(session));
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -65,8 +65,9 @@ public class SpringSessionBackedSessionRegistry<S extends Session>
|
||||
@Override
|
||||
public List<SessionInformation> getAllSessions(Object principal,
|
||||
boolean includeExpiredSessions) {
|
||||
Collection<S> sessions = this.sessionRepository
|
||||
.findByPrincipalName(name(principal)).values();
|
||||
Collection<S> sessions = this.sessionRepository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
name(principal)).values();
|
||||
List<SessionInformation> infos = new ArrayList<>();
|
||||
for (S session : sessions) {
|
||||
if (includeExpiredSessions || !Boolean.TRUE.equals(session
|
||||
|
||||
@@ -16,13 +16,8 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -46,22 +41,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultCookieSerializer.class);
|
||||
|
||||
private static final BitSet domainValid = new BitSet(128);
|
||||
|
||||
static {
|
||||
for (char c = '0'; c <= '9'; c++) {
|
||||
domainValid.set(c);
|
||||
}
|
||||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
domainValid.set(c);
|
||||
}
|
||||
for (char c = 'A'; c <= 'Z'; c++) {
|
||||
domainValid.set(c);
|
||||
}
|
||||
domainValid.set('.');
|
||||
domainValid.set('-');
|
||||
}
|
||||
|
||||
private String cookieName = "SESSION";
|
||||
|
||||
private Boolean useSecureCookie;
|
||||
@@ -82,8 +61,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
|
||||
private String rememberMeRequestAttribute;
|
||||
|
||||
private String sameSite = "Lax";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
@@ -98,8 +75,7 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (this.cookieName.equals(cookie.getName())) {
|
||||
String sessionId = (this.useBase64Encoding
|
||||
? base64Decode(cookie.getValue())
|
||||
: cookie.getValue());
|
||||
? base64Decode(cookie.getValue()) : cookie.getValue());
|
||||
if (sessionId == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -125,43 +101,38 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
HttpServletRequest request = cookieValue.getRequest();
|
||||
HttpServletResponse response = cookieValue.getResponse();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.cookieName).append('=');
|
||||
String value = getValue(cookieValue);
|
||||
if (value != null && value.length() > 0) {
|
||||
validateValue(value);
|
||||
sb.append(value);
|
||||
}
|
||||
int maxAge = getMaxAge(cookieValue);
|
||||
if (maxAge > -1) {
|
||||
sb.append("; Max-Age=").append(cookieValue.getCookieMaxAge());
|
||||
OffsetDateTime expires = (maxAge != 0)
|
||||
? OffsetDateTime.now().plusSeconds(maxAge)
|
||||
: Instant.EPOCH.atOffset(ZoneOffset.UTC);
|
||||
sb.append("; Expires=")
|
||||
.append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));
|
||||
}
|
||||
String domain = getDomainName(request);
|
||||
if (domain != null && domain.length() > 0) {
|
||||
validateDomain(domain);
|
||||
sb.append("; Domain=").append(domain);
|
||||
}
|
||||
String path = getCookiePath(request);
|
||||
if (path != null && path.length() > 0) {
|
||||
validatePath(path);
|
||||
sb.append("; Path=").append(path);
|
||||
}
|
||||
if (isSecureCookie(request)) {
|
||||
sb.append("; Secure");
|
||||
}
|
||||
if (this.useHttpOnlyCookie) {
|
||||
sb.append("; HttpOnly");
|
||||
}
|
||||
if (this.sameSite != null) {
|
||||
sb.append("; SameSite=").append(this.sameSite);
|
||||
String requestedCookieValue = cookieValue.getCookieValue();
|
||||
String actualCookieValue = (this.jvmRoute != null)
|
||||
? requestedCookieValue + this.jvmRoute
|
||||
: requestedCookieValue;
|
||||
|
||||
Cookie sessionCookie = new Cookie(this.cookieName, this.useBase64Encoding
|
||||
? base64Encode(actualCookieValue) : actualCookieValue);
|
||||
sessionCookie.setSecure(isSecureCookie(request));
|
||||
sessionCookie.setPath(getCookiePath(request));
|
||||
String domainName = getDomainName(request);
|
||||
if (domainName != null) {
|
||||
sessionCookie.setDomain(domainName);
|
||||
}
|
||||
|
||||
response.addHeader("Set-Cookie", sb.toString());
|
||||
if (this.useHttpOnlyCookie) {
|
||||
sessionCookie.setHttpOnly(true);
|
||||
}
|
||||
|
||||
if (cookieValue.getCookieMaxAge() < 0) {
|
||||
if (this.rememberMeRequestAttribute != null
|
||||
&& request.getAttribute(this.rememberMeRequestAttribute) != null) {
|
||||
// the cookie is only written at time of session creation, so we rely on
|
||||
// session expiration rather than cookie expiration if remember me is enabled
|
||||
cookieValue.setCookieMaxAge(Integer.MAX_VALUE);
|
||||
}
|
||||
else if (this.cookieMaxAge != null) {
|
||||
cookieValue.setCookieMaxAge(this.cookieMaxAge);
|
||||
}
|
||||
}
|
||||
sessionCookie.setMaxAge(cookieValue.getCookieMaxAge());
|
||||
|
||||
response.addCookie(sessionCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,81 +163,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
return new String(encodedCookieBytes);
|
||||
}
|
||||
|
||||
private String getValue(CookieValue cookieValue) {
|
||||
String requestedCookieValue = cookieValue.getCookieValue();
|
||||
String actualCookieValue = requestedCookieValue;
|
||||
if (this.jvmRoute != null) {
|
||||
actualCookieValue = requestedCookieValue + this.jvmRoute;
|
||||
}
|
||||
if (this.useBase64Encoding) {
|
||||
actualCookieValue = base64Encode(actualCookieValue);
|
||||
}
|
||||
return actualCookieValue;
|
||||
}
|
||||
|
||||
private void validateValue(String value) {
|
||||
int start = 0;
|
||||
int end = value.length();
|
||||
if ((end > 1) && (value.charAt(0) == '"') && (value.charAt(end - 1) == '"')) {
|
||||
start = 1;
|
||||
end--;
|
||||
}
|
||||
char[] chars = value.toCharArray();
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = chars[i];
|
||||
if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c
|
||||
|| c == 0x7f) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid character in cookie value: " + Integer.toString(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getMaxAge(CookieValue cookieValue) {
|
||||
int maxAge = cookieValue.getCookieMaxAge();
|
||||
if (maxAge < 0) {
|
||||
if (this.rememberMeRequestAttribute != null && cookieValue.getRequest()
|
||||
.getAttribute(this.rememberMeRequestAttribute) != null) {
|
||||
// the cookie is only written at time of session creation, so we rely on
|
||||
// session expiration rather than cookie expiration if remember me is
|
||||
// enabled
|
||||
cookieValue.setCookieMaxAge(Integer.MAX_VALUE);
|
||||
}
|
||||
else if (this.cookieMaxAge != null) {
|
||||
cookieValue.setCookieMaxAge(this.cookieMaxAge);
|
||||
}
|
||||
}
|
||||
return cookieValue.getCookieMaxAge();
|
||||
}
|
||||
|
||||
private void validateDomain(String domain) {
|
||||
int i = 0;
|
||||
int cur = -1;
|
||||
int prev;
|
||||
char[] chars = domain.toCharArray();
|
||||
while (i < chars.length) {
|
||||
prev = cur;
|
||||
cur = chars[i];
|
||||
if (!domainValid.get(cur)
|
||||
|| ((prev == '.' || prev == -1) && (cur == '.' || cur == '-'))
|
||||
|| (prev == '-' && cur == '.')) {
|
||||
throw new IllegalArgumentException("Invalid cookie domain: " + domain);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (cur == '.' || cur == '-') {
|
||||
throw new IllegalArgumentException("Invalid cookie domain: " + domain);
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePath(String path) {
|
||||
for (char ch : path.toCharArray()) {
|
||||
if (ch < 0x20 || ch > 0x7E || ch == ';') {
|
||||
throw new IllegalArgumentException("Invalid cookie path: " + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if a Cookie marked as secure should be used. The default is to use the value
|
||||
* of {@link HttpServletRequest#isSecure()}.
|
||||
@@ -422,16 +318,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
this.rememberMeRequestAttribute = rememberMeRequestAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for the {@code SameSite} cookie directive. The default value is
|
||||
* {@code Lax}.
|
||||
* @param sameSite the SameSite directive value
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public void setSameSite(String sameSite) {
|
||||
this.sameSite = sameSite;
|
||||
}
|
||||
|
||||
private String getDomainName(HttpServletRequest request) {
|
||||
if (this.domainName != null) {
|
||||
return this.domainName;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -24,13 +24,8 @@ import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpSessionBindingEvent;
|
||||
import javax.servlet.http.HttpSessionBindingListener;
|
||||
import javax.servlet.http.HttpSessionContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.session.Session;
|
||||
|
||||
/**
|
||||
@@ -38,14 +33,11 @@ import org.springframework.session.Session;
|
||||
*
|
||||
* @param <S> the {@link Session} type
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.1
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(HttpSessionAdapter.class);
|
||||
|
||||
private S session;
|
||||
|
||||
private final ServletContext servletContext;
|
||||
@@ -137,28 +129,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
@Override
|
||||
public void setAttribute(String name, Object value) {
|
||||
checkState();
|
||||
Object oldValue = this.session.getAttribute(name);
|
||||
this.session.setAttribute(name, value);
|
||||
if (value != oldValue) {
|
||||
if (oldValue instanceof HttpSessionBindingListener) {
|
||||
try {
|
||||
((HttpSessionBindingListener) oldValue).valueUnbound(
|
||||
new HttpSessionBindingEvent(this, name, oldValue));
|
||||
}
|
||||
catch (Throwable th) {
|
||||
logger.error("Error invoking session binding event listener", th);
|
||||
}
|
||||
}
|
||||
if (value instanceof HttpSessionBindingListener) {
|
||||
try {
|
||||
((HttpSessionBindingListener) value)
|
||||
.valueBound(new HttpSessionBindingEvent(this, name, value));
|
||||
}
|
||||
catch (Throwable th) {
|
||||
logger.error("Error invoking session binding event listener", th);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,17 +140,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
checkState();
|
||||
Object oldValue = this.session.getAttribute(name);
|
||||
this.session.removeAttribute(name);
|
||||
if (oldValue instanceof HttpSessionBindingListener) {
|
||||
try {
|
||||
((HttpSessionBindingListener) oldValue)
|
||||
.valueUnbound(new HttpSessionBindingEvent(this, name, oldValue));
|
||||
}
|
||||
catch (Throwable th) {
|
||||
logger.error("Error invoking session binding event listener", th);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -28,7 +28,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public interface HttpSessionIdResolver {
|
||||
|
||||
|
||||
@@ -21,8 +21,11 @@ import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -71,6 +74,7 @@ import org.springframework.session.SessionRepository;
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
|
||||
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
|
||||
@@ -205,6 +209,8 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
private boolean requestedSessionCached;
|
||||
|
||||
private String requestedSessionId;
|
||||
|
||||
private Boolean requestedSessionIdValid;
|
||||
|
||||
private boolean requestedSessionInvalidated;
|
||||
@@ -277,7 +283,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
return isRequestedSessionIdValid(requestedSession);
|
||||
}
|
||||
|
||||
return this.requestedSessionIdValid;
|
||||
}
|
||||
|
||||
@@ -351,8 +356,16 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
@Override
|
||||
public String getRequestedSessionId() {
|
||||
S requestedSession = getRequestedSession();
|
||||
return (requestedSession != null) ? requestedSession.getId() : null;
|
||||
if (this.requestedSessionId == null) {
|
||||
getRequestedSession();
|
||||
}
|
||||
return this.requestedSessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestDispatcher getRequestDispatcher(String path) {
|
||||
RequestDispatcher requestDispatcher = super.getRequestDispatcher(path);
|
||||
return new SessionCommittingRequestDispatcher(requestDispatcher);
|
||||
}
|
||||
|
||||
private S getRequestedSession() {
|
||||
@@ -360,10 +373,14 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
List<String> sessionIds = SessionRepositoryFilter.this.httpSessionIdResolver
|
||||
.resolveSessionIds(this);
|
||||
for (String sessionId : sessionIds) {
|
||||
if (this.requestedSessionId == null) {
|
||||
this.requestedSessionId = sessionId;
|
||||
}
|
||||
S session = SessionRepositoryFilter.this.sessionRepository
|
||||
.findById(sessionId);
|
||||
if (session != null) {
|
||||
this.requestedSession = session;
|
||||
this.requestedSessionId = sessionId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -375,6 +392,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
private void clearRequestedSessionCache() {
|
||||
this.requestedSessionCached = false;
|
||||
this.requestedSession = null;
|
||||
this.requestedSessionId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,6 +417,35 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures session is committed before issuing an include.
|
||||
*
|
||||
* @since 2.0.8
|
||||
*/
|
||||
private final class SessionCommittingRequestDispatcher
|
||||
implements RequestDispatcher {
|
||||
|
||||
private final RequestDispatcher delegate;
|
||||
|
||||
SessionCommittingRequestDispatcher(RequestDispatcher delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forward(ServletRequest request, ServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
this.delegate.forward(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void include(ServletRequest request, ServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
SessionRepositoryRequestWrapper.this.commitSession();
|
||||
this.delegate.include(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,6 +87,12 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
|
||||
return Mono.just(session);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.session.security;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -75,9 +74,7 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(
|
||||
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isFalse();
|
||||
}
|
||||
@@ -93,9 +90,7 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(
|
||||
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isTrue();
|
||||
}
|
||||
@@ -167,8 +162,9 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
Map<String, Session> sessions = new LinkedHashMap<>();
|
||||
sessions.put(session1.getId(), session1);
|
||||
sessions.put(session2.getId(), session2);
|
||||
when(this.sessionRepository.findByPrincipalName(USER_NAME))
|
||||
.thenReturn(sessions);
|
||||
when(this.sessionRepository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, USER_NAME))
|
||||
.thenReturn(sessions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import org.springframework.mock.web.MockCookie;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
@@ -467,39 +466,6 @@ public class DefaultCookieSerializerTests {
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
}
|
||||
|
||||
// --- sameSite ---
|
||||
|
||||
@Test
|
||||
public void writeCookieDefaultSameSiteLax() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteLax() {
|
||||
this.serializer.setSameSite("Lax");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteStrict() {
|
||||
this.serializer.setSameSite("Strict");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Strict");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteNull() {
|
||||
this.serializer.setSameSite(null);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isNull();
|
||||
}
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
this.cookieName = cookieName;
|
||||
this.serializer.setCookieName(cookieName);
|
||||
@@ -512,8 +478,8 @@ public class DefaultCookieSerializerTests {
|
||||
return new Cookie(name, value);
|
||||
}
|
||||
|
||||
private MockCookie getCookie() {
|
||||
return (MockCookie) this.response.getCookie(this.cookieName);
|
||||
private Cookie getCookie() {
|
||||
return this.response.getCookie(this.cookieName);
|
||||
}
|
||||
|
||||
private String getCookieValue() {
|
||||
|
||||
@@ -27,8 +27,6 @@ import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletContext;
|
||||
@@ -38,8 +36,6 @@ import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpSessionBindingEvent;
|
||||
import javax.servlet.http.HttpSessionBindingListener;
|
||||
import javax.servlet.http.HttpSessionContext;
|
||||
|
||||
import org.assertj.core.data.Offset;
|
||||
@@ -1170,6 +1166,23 @@ public class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // gh-1243
|
||||
public void doFilterInclude() throws Exception {
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest,
|
||||
HttpServletResponse wrappedResponse)
|
||||
throws IOException, ServletException {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedRequest.getRequestDispatcher("/").include(wrappedRequest,
|
||||
wrappedResponse);
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- HttpSessionIdResolver
|
||||
|
||||
@Test
|
||||
@@ -1196,6 +1209,29 @@ public class SessionRepositoryFilterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // gh-1229
|
||||
public void doFilterAdapterGetRequestedSessionIdForInvalidSession() throws Exception {
|
||||
SessionRepository<MapSession> sessionRepository = new MapSessionRepository(
|
||||
new HashMap<>());
|
||||
|
||||
this.filter = new SessionRepositoryFilter<>(sessionRepository);
|
||||
this.filter.setHttpSessionIdResolver(this.strategy);
|
||||
final String expectedId = "HttpSessionIdResolver-requested-id1";
|
||||
final String otherId = "HttpSessionIdResolver-requested-id2";
|
||||
|
||||
given(this.strategy.resolveSessionIds(any(HttpServletRequest.class)))
|
||||
.willReturn(Arrays.asList(expectedId, otherId));
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest,
|
||||
HttpServletResponse wrappedResponse) {
|
||||
assertThat(wrappedRequest.getRequestedSessionId()).isEqualTo(expectedId);
|
||||
assertThat(wrappedRequest.isRequestedSessionIdValid()).isFalse();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterAdapterOnNewSession() throws Exception {
|
||||
this.filter.setHttpSessionIdResolver(this.strategy);
|
||||
@@ -1390,122 +1426,6 @@ public class SessionRepositoryFilterTests {
|
||||
.hasMessage("httpSessionIdResolver cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingListenerBindListener() throws Exception {
|
||||
String bindingListenerName = "bindingListener";
|
||||
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.setAttribute(bindingListenerName, bindingListener);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
assertThat(bindingListener.getCounter()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingListenerBindListenerThenUnbind() throws Exception {
|
||||
String bindingListenerName = "bindingListener";
|
||||
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.setAttribute(bindingListenerName, bindingListener);
|
||||
session.removeAttribute(bindingListenerName);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
assertThat(bindingListener.getCounter()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingListenerBindSameListenerTwice() throws Exception {
|
||||
String bindingListenerName = "bindingListener";
|
||||
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.setAttribute(bindingListenerName, bindingListener);
|
||||
session.setAttribute(bindingListenerName, bindingListener);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
assertThat(bindingListener.getCounter()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingListenerBindListenerOverwrite() throws Exception {
|
||||
String bindingListenerName = "bindingListener";
|
||||
CountingHttpSessionBindingListener bindingListener1 = new CountingHttpSessionBindingListener();
|
||||
CountingHttpSessionBindingListener bindingListener2 = new CountingHttpSessionBindingListener();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.setAttribute(bindingListenerName, bindingListener1);
|
||||
session.setAttribute(bindingListenerName, bindingListener2);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
assertThat(bindingListener1.getCounter()).isEqualTo(0);
|
||||
assertThat(bindingListener2.getCounter()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingListenerBindThrowsException() throws Exception {
|
||||
String bindingListenerName = "bindingListener";
|
||||
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
bindingListener.setThrowException();
|
||||
session.setAttribute(bindingListenerName, bindingListener);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
assertThat(bindingListener.getCounter()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingListenerBindListenerThenUnbindThrowsException() throws Exception {
|
||||
String bindingListenerName = "bindingListener";
|
||||
CountingHttpSessionBindingListener bindingListener = new CountingHttpSessionBindingListener();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
HttpSession session = wrappedRequest.getSession();
|
||||
session.setAttribute(bindingListenerName, bindingListener);
|
||||
bindingListener.setThrowException();
|
||||
session.removeAttribute(bindingListenerName);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
assertThat(bindingListener.getCounter()).isEqualTo(1);
|
||||
}
|
||||
|
||||
// --- helper methods
|
||||
|
||||
private void assertNewSession() {
|
||||
@@ -1608,39 +1528,4 @@ public class SessionRepositoryFilterTests {
|
||||
|
||||
}
|
||||
|
||||
private static class CountingHttpSessionBindingListener
|
||||
implements HttpSessionBindingListener {
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
private final AtomicBoolean throwException = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void valueBound(HttpSessionBindingEvent event) {
|
||||
if (this.throwException.get()) {
|
||||
this.throwException.compareAndSet(true, false);
|
||||
throw new RuntimeException("bind exception");
|
||||
}
|
||||
this.counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueUnbound(HttpSessionBindingEvent event) {
|
||||
if (this.throwException.get()) {
|
||||
this.throwException.compareAndSet(true, false);
|
||||
throw new RuntimeException("unbind exception");
|
||||
}
|
||||
this.counter.decrementAndGet();
|
||||
}
|
||||
|
||||
int getCounter() {
|
||||
return this.counter.get();
|
||||
}
|
||||
|
||||
void setThrowException() {
|
||||
this.throwException.compareAndSet(false, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -253,6 +253,17 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
.containsExactly(new AbstractMap.SimpleEntry<>(attrName, attrValue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeSessionWhenInvokedThenSessionSaved() {
|
||||
given(this.sessionRepository.save(this.createSession)).willReturn(Mono.empty());
|
||||
WebSession createdSession = this.webSessionStore.createWebSession()
|
||||
.block();
|
||||
|
||||
this.webSessionStore.storeSession(createdSession).block();
|
||||
|
||||
verify(this.sessionRepository).save(this.createSession);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveSessionThenStarted() {
|
||||
String id = "id";
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ReactiveRedisOperationsSessionRepository}.
|
||||
@@ -210,10 +209,7 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
this.repository.save(session).block();
|
||||
|
||||
toSave.setLastAccessedTime(Instant.now());
|
||||
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> this.repository.save(toSave).block())
|
||||
.withMessage("Session was invalidated");
|
||||
this.repository.save(toSave).block();
|
||||
|
||||
assertThat(this.repository.findById(sessionId).block()).isNull();
|
||||
assertThat(this.repository.findById(session.getId()).block()).isNotNull();
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -118,15 +118,6 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
this.redisFlushMode = redisFlushMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ReactiveRedisOperations} used for sessions.
|
||||
* @return the {@link ReactiveRedisOperations} used for sessions
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public ReactiveRedisOperations<String, Object> getSessionRedisOperations() {
|
||||
return this.sessionRedisOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<RedisSession> createSession() {
|
||||
return Mono.defer(() -> {
|
||||
@@ -156,9 +147,7 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
session.hasChangedSessionId() ? session.originalSessionId
|
||||
: session.getId());
|
||||
return this.sessionRedisOperations.hasKey(sessionKey)
|
||||
.flatMap((exists) -> exists ? result
|
||||
: Mono.error(new IllegalStateException(
|
||||
"Session was invalidated")));
|
||||
.flatMap((exists) -> exists ? result : Mono.empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -23,14 +23,14 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
|
||||
/**
|
||||
* Annotation used to inject the Redis accessor used by Spring Session's Redis session
|
||||
* repository.
|
||||
* Annotation used to inject the {@link RedisOperations} instance used by Spring Session's
|
||||
* {@link RedisOperationsSessionRepository}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @see org.springframework.session.data.redis.RedisOperationsSessionRepository#getSessionRedisOperations()
|
||||
* @see org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository#getSessionRedisOperations()
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.Map;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -65,8 +64,6 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
private ReactiveRedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
@@ -110,13 +107,6 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
this.redisConnectionFactory = redisConnectionFactoryToUse;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("springSessionDefaultRedisSerializer")
|
||||
public void setDefaultRedisSerializer(
|
||||
RedisSerializer<Object> defaultRedisSerializer) {
|
||||
this.defaultRedisSerializer = defaultRedisSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
@@ -144,11 +134,10 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
private ReactiveRedisTemplate<String, Object> createReactiveRedisTemplate() {
|
||||
RedisSerializer<String> keySerializer = new StringRedisSerializer();
|
||||
RedisSerializer<Object> defaultSerializer = (this.defaultRedisSerializer != null)
|
||||
? this.defaultRedisSerializer
|
||||
: new JdkSerializationRedisSerializer(this.classLoader);
|
||||
RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer(
|
||||
this.classLoader);
|
||||
RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext
|
||||
.<String, Object>newSerializationContext(defaultSerializer)
|
||||
.<String, Object>newSerializationContext(valueSerializer)
|
||||
.key(keySerializer).hashKey(keySerializer).build();
|
||||
return new ReactiveRedisTemplate<>(this.redisConnectionFactory,
|
||||
serializationContext);
|
||||
|
||||
@@ -346,16 +346,12 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
.isEqualTo(expected.getAttribute(attribute1));
|
||||
assertThat(session.<String>getAttribute(attribute2))
|
||||
.isEqualTo(expected.getAttribute(attribute2));
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getCreationTime()
|
||||
.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expected.getMaxInactiveInterval());
|
||||
assertThat(
|
||||
session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getLastAccessedTime()
|
||||
.truncatedTo(ChronoUnit.MILLIS));
|
||||
}).verifyComplete();
|
||||
assertThat(session.getLastAccessedTime())
|
||||
.isEqualTo(expected.getLastAccessedTime());
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -432,12 +432,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
.isEqualTo(expected.getAttribute(attribute1));
|
||||
assertThat(session.<String>getAttribute(attribute2))
|
||||
.isEqualTo(expected.getAttribute(attribute2));
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getCreationTime().truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expected.getMaxInactiveInterval());
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getLastAccessedTime())
|
||||
.isEqualTo(expected.getLastAccessedTime());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -498,11 +498,9 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
RedisSession session = sessionIdToSessions.get(sessionId);
|
||||
assertThat(session).isNotNull();
|
||||
assertThat(session.getId()).isEqualTo(sessionId);
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(lastAccessed.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessed);
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(maxInactive);
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(createdTime.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getCreationTime()).isEqualTo(createdTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -27,12 +27,9 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -73,22 +70,6 @@ public class RedisWebSessionConfigurationTests {
|
||||
assertThat(repository).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void springSessionRedisOperationsResolvingConfiguration() {
|
||||
registerAndRefresh(RedisConfig.class,
|
||||
SpringSessionRedisOperationsResolvingConfig.class);
|
||||
|
||||
ReactiveRedisOperationsSessionRepository repository = this.context
|
||||
.getBean(ReactiveRedisOperationsSessionRepository.class);
|
||||
assertThat(repository).isNotNull();
|
||||
ReactiveRedisOperations<String, Object> springSessionRedisOperations = this.context
|
||||
.getBean(SpringSessionRedisOperationsResolvingConfig.class)
|
||||
.getSpringSessionRedisOperations();
|
||||
assertThat(springSessionRedisOperations).isNotNull();
|
||||
assertThat((ReactiveRedisOperations) ReflectionTestUtils.getField(repository,
|
||||
"sessionRedisOperations")).isEqualTo(springSessionRedisOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customNamespace() {
|
||||
registerAndRefresh(RedisConfig.class, CustomNamespaceConfig.class);
|
||||
@@ -200,36 +181,6 @@ public class RedisWebSessionConfigurationTests {
|
||||
.hasMessageContaining("expected single matching bean but found 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void customRedisSerializerConfig() {
|
||||
registerAndRefresh(RedisConfig.class, CustomRedisSerializerConfig.class);
|
||||
|
||||
ReactiveRedisOperationsSessionRepository repository = this.context
|
||||
.getBean(ReactiveRedisOperationsSessionRepository.class);
|
||||
RedisSerializer<Object> redisSerializer = this.context
|
||||
.getBean("springSessionDefaultRedisSerializer", RedisSerializer.class);
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(redisSerializer).isNotNull();
|
||||
ReactiveRedisOperations redisOperations = (ReactiveRedisOperations) ReflectionTestUtils
|
||||
.getField(repository, "sessionRedisOperations");
|
||||
assertThat(redisOperations).isNotNull();
|
||||
RedisSerializationContext serializationContext = redisOperations
|
||||
.getSerializationContext();
|
||||
assertThat(ReflectionTestUtils.getField(
|
||||
serializationContext.getValueSerializationPair().getReader(),
|
||||
"serializer")).isEqualTo(redisSerializer);
|
||||
assertThat(ReflectionTestUtils.getField(
|
||||
serializationContext.getValueSerializationPair().getWriter(),
|
||||
"serializer")).isEqualTo(redisSerializer);
|
||||
assertThat(ReflectionTestUtils.getField(
|
||||
serializationContext.getHashValueSerializationPair().getReader(),
|
||||
"serializer")).isEqualTo(redisSerializer);
|
||||
assertThat(ReflectionTestUtils.getField(
|
||||
serializationContext.getHashValueSerializationPair().getWriter(),
|
||||
"serializer")).isEqualTo(redisSerializer);
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
@@ -250,18 +201,6 @@ public class RedisWebSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisWebSession
|
||||
static class SpringSessionRedisOperationsResolvingConfig {
|
||||
|
||||
@SpringSessionRedisOperations
|
||||
private ReactiveRedisOperations<String, Object> springSessionRedisOperations;
|
||||
|
||||
public ReactiveRedisOperations<String, Object> getSpringSessionRedisOperations() {
|
||||
return this.springSessionRedisOperations;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisWebSession(redisNamespace = REDIS_NAMESPACE)
|
||||
static class CustomNamespaceConfig {
|
||||
|
||||
@@ -336,15 +275,4 @@ public class RedisWebSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisWebSession
|
||||
static class CustomRedisSerializerConfig {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
|
||||
return mock(RedisSerializer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ apply plugin: 'io.spring.convention.spring-module'
|
||||
dependencies {
|
||||
compile project(':spring-session-core')
|
||||
compile "com.hazelcast:hazelcast"
|
||||
compile "javax.annotation:javax.annotation-api"
|
||||
compile "org.springframework:spring-context"
|
||||
|
||||
testCompile "javax.servlet:javax.servlet-api"
|
||||
|
||||
@@ -48,7 +48,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
public class HazelcastClientRepositoryITests extends AbstractHazelcastRepositoryITests {
|
||||
|
||||
private static GenericContainer container = new GenericContainer<>(
|
||||
"hazelcast/hazelcast:3.10.5")
|
||||
"hazelcast/hazelcast:3.9.4")
|
||||
.withExposedPorts(5701)
|
||||
.withEnv("JAVA_OPTS",
|
||||
"-Dhazelcast.config=/opt/hazelcast/config_ext/hazelcast.xml")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.9.xsd">
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config http://www.hazelcast.com/schema/config/hazelcast-config-3.9.xsd">
|
||||
|
||||
<user-code-deployment enabled="true">
|
||||
<class-cache-mode>ETERNAL</class-cache-mode>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.9.xsd">
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config http://www.hazelcast.com/schema/config/hazelcast-config-3.9.xsd">
|
||||
|
||||
<group>
|
||||
<name>spring-session-it-test-idle-time-map-name</name>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.9.xsd">
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config http://www.hazelcast.com/schema/config/hazelcast-config-3.9.xsd">
|
||||
|
||||
<group>
|
||||
<name>spring-session-it-test-map-name</name>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -20,7 +20,6 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import com.hazelcast.core.Offloadable;
|
||||
import com.hazelcast.map.AbstractEntryProcessor;
|
||||
import com.hazelcast.map.EntryProcessor;
|
||||
|
||||
@@ -30,11 +29,11 @@ import org.springframework.session.MapSession;
|
||||
* Hazelcast {@link EntryProcessor} responsible for handling updates to session.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0.5
|
||||
* @since 1.3.4
|
||||
* @see HazelcastSessionRepository#save(HazelcastSessionRepository.HazelcastSession)
|
||||
*/
|
||||
class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSession>
|
||||
implements Offloadable {
|
||||
public class SessionUpdateEntryProcessor
|
||||
extends AbstractEntryProcessor<String, MapSession> {
|
||||
|
||||
private Instant lastAccessedTime;
|
||||
|
||||
@@ -68,11 +67,6 @@ class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSess
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExecutorName() {
|
||||
return OFFLOADABLE_EXECUTOR;
|
||||
}
|
||||
|
||||
void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.lastAccessedTime = lastAccessedTime;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -39,7 +38,6 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -174,8 +172,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
assertThat(session.isChanged()).isFalse();
|
||||
assertThat(session.getDelta()).isEmpty();
|
||||
assertThat(session.isExpired()).isFalse();
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(lastAccessedTime.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessedTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -746,7 +743,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
assertThat(session.<String>getAttribute("testName")).isEqualTo("testValue2");
|
||||
}
|
||||
|
||||
@Test // gh-1031
|
||||
@Test // gh-1151
|
||||
public void saveDeleted() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
this.repository.save(session);
|
||||
@@ -758,7 +755,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
assertThat(this.repository.findById(session.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // gh-1031
|
||||
@Test // gh-1151
|
||||
public void saveDeletedAddAttribute() {
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
|
||||
this.repository.save(session);
|
||||
@@ -771,29 +768,19 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
assertThat(this.repository.findById(session.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test // gh-1133
|
||||
public void sessionFromStoreResolvesAttributesLazily() {
|
||||
@Test // gh-1203
|
||||
public void saveWithLargeAttribute() {
|
||||
String attributeName = "largeAttribute";
|
||||
int arraySize = 4000;
|
||||
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
session.setAttribute("attribute1", "value1");
|
||||
session.setAttribute("attribute2", "value2");
|
||||
session.setAttribute(attributeName, new byte[arraySize]);
|
||||
this.repository.save(session);
|
||||
session = this.repository.findById(session.getId());
|
||||
MapSession delegate = (MapSession) ReflectionTestUtils.getField(session,
|
||||
"delegate");
|
||||
|
||||
assertThat((String) session.getAttribute("attribute1")).isEqualTo("value1");
|
||||
assertThat(delegate).isNotNull();
|
||||
assertThat(ReflectionTestUtils
|
||||
.getField((Supplier) delegate.getAttribute("attribute1"), "value"))
|
||||
.isEqualTo("value1");
|
||||
assertThat(ReflectionTestUtils
|
||||
.getField((Supplier) delegate.getAttribute("attribute2"), "value"))
|
||||
.isNull();
|
||||
assertThat((String) session.getAttribute("attribute2")).isEqualTo("value2");
|
||||
assertThat(ReflectionTestUtils
|
||||
.getField((Supplier) delegate.getAttribute("attribute2"), "value"))
|
||||
.isEqualTo("value2");
|
||||
assertThat(session).isNotNull();
|
||||
assertThat((byte[]) session.getAttribute(attributeName)).hasSize(arraySize);
|
||||
}
|
||||
|
||||
private String getSecurityName() {
|
||||
@@ -813,5 +800,4 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class MariaDb10JdbcOperationsSessionRepositoryITests
|
||||
private static class MariaDb10Container extends MariaDBContainer<MariaDb10Container> {
|
||||
|
||||
MariaDb10Container() {
|
||||
super("mariadb:10.3.9");
|
||||
super("mariadb:10.3.11");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -86,7 +86,7 @@ public class MariaDb5JdbcOperationsSessionRepositoryITests
|
||||
private static class MariaDb5Container extends MariaDBContainer<MariaDb5Container> {
|
||||
|
||||
MariaDb5Container() {
|
||||
super("mariadb:5.5.61");
|
||||
super("mariadb:5.5.62");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -85,7 +85,7 @@ public class MySql5JdbcOperationsSessionRepositoryITests
|
||||
private static class MySql5Container extends MySQLContainer<MySql5Container> {
|
||||
|
||||
MySql5Container() {
|
||||
super("mysql:5.7.23");
|
||||
super("mysql:5.7.24");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -85,7 +85,7 @@ public class MySql8JdbcOperationsSessionRepositoryITests
|
||||
private static class MySql8Container extends MySQLContainer<MySql8Container> {
|
||||
|
||||
MySql8Container() {
|
||||
super("mysql:8.0.12");
|
||||
super("mysql:8.0.13");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -86,7 +86,7 @@ public class PostgreSql10JdbcOperationsSessionRepositoryITests
|
||||
extends PostgreSQLContainer<PostgreSql10Container> {
|
||||
|
||||
PostgreSql10Container() {
|
||||
super("postgres:10.5");
|
||||
super("postgres:10.6");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class PostgreSql9JdbcOperationsSessionRepositoryITests
|
||||
extends PostgreSQLContainer<PostgreSql9Container> {
|
||||
|
||||
PostgreSql9Container() {
|
||||
super("postgres:9.6.10");
|
||||
super("postgres:9.6.11");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -86,9 +86,7 @@ public class SqlServerJdbcOperationsSessionRepositoryITests
|
||||
extends MSSQLServerContainer<SqlServer2007Container> {
|
||||
|
||||
SqlServer2007Container() {
|
||||
super("microsoft/mssql-server-linux:2017-CU10");
|
||||
withStartupTimeoutSeconds(240);
|
||||
withConnectTimeoutSeconds(240);
|
||||
super("mcr.microsoft.com/mssql/server:2017-CU12");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
microsoft/mssql-server-linux:2017-CU10
|
||||
mcr.microsoft.com/mssql/server:2017-CU12
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -28,7 +28,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -53,9 +52,7 @@ import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionOperations;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
@@ -129,7 +126,6 @@ import org.springframework.util.StringUtils;
|
||||
* target database type.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Craig Andrews
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class JdbcOperationsSessionRepository implements
|
||||
@@ -193,17 +189,10 @@ public class JdbcOperationsSessionRepository implements
|
||||
|
||||
private final JdbcOperations jdbcOperations;
|
||||
|
||||
private final TransactionOperations transactionOperations;
|
||||
|
||||
private final ResultSetExtractor<List<JdbcSession>> extractor = new SessionResultSetExtractor();
|
||||
|
||||
private TransactionOperations transactionOperations = new TransactionOperations() {
|
||||
|
||||
@Override
|
||||
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
|
||||
return action.doInTransaction(null);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The name of database table used by Spring Session to store sessions.
|
||||
*/
|
||||
@@ -240,29 +229,14 @@ public class JdbcOperationsSessionRepository implements
|
||||
/**
|
||||
* Create a new {@link JdbcOperationsSessionRepository} instance which uses the
|
||||
* provided {@link JdbcOperations} to manage sessions.
|
||||
* <p>
|
||||
* The created instance will execute all data access operations in a transaction with
|
||||
* propagation level of {@link TransactionDefinition#PROPAGATION_REQUIRES_NEW}.
|
||||
* @param jdbcOperations the {@link JdbcOperations} to use
|
||||
* @param transactionManager the {@link PlatformTransactionManager} to use
|
||||
*/
|
||||
public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations,
|
||||
PlatformTransactionManager transactionManager) {
|
||||
this(jdbcOperations);
|
||||
Assert.notNull(transactionManager, "TransactionManager must not be null");
|
||||
this.transactionOperations = createTransactionTemplate(transactionManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JdbcOperationsSessionRepository} instance which uses the
|
||||
* provided {@link JdbcOperations} to manage sessions.
|
||||
* <p>
|
||||
* The created instance will not execute data access operations in a transaction.
|
||||
* @param jdbcOperations the {@link JdbcOperations} to use
|
||||
*/
|
||||
public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations) {
|
||||
Assert.notNull(jdbcOperations, "JdbcOperations must not be null");
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
this.transactionOperations = createTransactionTemplate(transactionManager);
|
||||
this.conversionService = createDefaultConversionService();
|
||||
prepareQueries();
|
||||
}
|
||||
@@ -532,7 +506,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
public void setValues(PreparedStatement ps, int i) throws SQLException {
|
||||
String attributeName = attributeNames.get(i);
|
||||
ps.setString(1, attributeName);
|
||||
setObjectAsBlob(ps, 2, session.getAttribute(attributeName));
|
||||
serialize(ps, 2, session.getAttribute(attributeName));
|
||||
ps.setString(3, session.getId());
|
||||
}
|
||||
|
||||
@@ -547,7 +521,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
this.jdbcOperations.update(this.createSessionAttributeQuery, (ps) -> {
|
||||
String attributeName = attributeNames.get(0);
|
||||
ps.setString(1, attributeName);
|
||||
setObjectAsBlob(ps, 2, session.getAttribute(attributeName));
|
||||
serialize(ps, 2, session.getAttribute(attributeName));
|
||||
ps.setString(3, session.getId());
|
||||
});
|
||||
}
|
||||
@@ -561,7 +535,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
@Override
|
||||
public void setValues(PreparedStatement ps, int i) throws SQLException {
|
||||
String attributeName = attributeNames.get(i);
|
||||
setObjectAsBlob(ps, 1, session.getAttribute(attributeName));
|
||||
serialize(ps, 1, session.getAttribute(attributeName));
|
||||
ps.setString(2, session.primaryKey);
|
||||
ps.setString(3, attributeName);
|
||||
}
|
||||
@@ -576,7 +550,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
else {
|
||||
this.jdbcOperations.update(this.updateSessionAttributeQuery, (ps) -> {
|
||||
String attributeName = attributeNames.get(0);
|
||||
setObjectAsBlob(ps, 1, session.getAttribute(attributeName));
|
||||
serialize(ps, 1, session.getAttribute(attributeName));
|
||||
ps.setString(2, session.primaryKey);
|
||||
ps.setString(3, attributeName);
|
||||
});
|
||||
@@ -659,17 +633,19 @@ public class JdbcOperationsSessionRepository implements
|
||||
getQuery(DELETE_SESSIONS_BY_EXPIRY_TIME_QUERY);
|
||||
}
|
||||
|
||||
private void setObjectAsBlob(PreparedStatement ps, int paramIndex, Object object)
|
||||
private void serialize(PreparedStatement ps, int paramIndex, Object attributeValue)
|
||||
throws SQLException {
|
||||
byte[] bytes = (byte[]) this.conversionService.convert(object,
|
||||
TypeDescriptor.valueOf(Object.class),
|
||||
TypeDescriptor.valueOf(byte[].class));
|
||||
this.lobHandler.getLobCreator().setBlobAsBytes(ps, paramIndex, bytes);
|
||||
this.lobHandler.getLobCreator().setBlobAsBytes(ps, paramIndex,
|
||||
(byte[]) this.conversionService.convert(attributeValue,
|
||||
TypeDescriptor.valueOf(Object.class),
|
||||
TypeDescriptor.valueOf(byte[].class)));
|
||||
}
|
||||
|
||||
private Object getBlobAsObject(ResultSet rs, String columnName) throws SQLException {
|
||||
byte[] bytes = this.lobHandler.getBlobAsBytes(rs, columnName);
|
||||
return this.conversionService.convert(bytes, TypeDescriptor.valueOf(byte[].class),
|
||||
private Object deserialize(ResultSet rs, String columnName)
|
||||
throws SQLException {
|
||||
return this.conversionService.convert(
|
||||
this.lobHandler.getBlobAsBytes(rs, columnName),
|
||||
TypeDescriptor.valueOf(byte[].class),
|
||||
TypeDescriptor.valueOf(Object.class));
|
||||
}
|
||||
|
||||
@@ -679,28 +655,6 @@ public class JdbcOperationsSessionRepository implements
|
||||
|
||||
}
|
||||
|
||||
private static <T> Supplier<T> value(T value) {
|
||||
return (value != null) ? () -> value : null;
|
||||
}
|
||||
|
||||
private static <T> Supplier<T> lazily(Supplier<T> supplier) {
|
||||
Supplier<T> lazySupplier = new Supplier<T>() {
|
||||
|
||||
private T value;
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
if (this.value == null) {
|
||||
this.value = supplier.get();
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (supplier != null) ? lazySupplier : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Session} to use for {@link JdbcOperationsSessionRepository}.
|
||||
*
|
||||
@@ -770,8 +724,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String attributeName) {
|
||||
Supplier<T> supplier = this.delegate.getAttribute(attributeName);
|
||||
return (supplier != null) ? supplier.get() : null;
|
||||
return this.delegate.getAttribute(attributeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -806,7 +759,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
? oldDeltaValue
|
||||
: DeltaValue.UPDATED);
|
||||
}
|
||||
this.delegate.setAttribute(attributeName, value(attributeValue));
|
||||
this.delegate.setAttribute(attributeName, attributeValue);
|
||||
if (PRINCIPAL_NAME_INDEX_NAME.equals(attributeName) ||
|
||||
SPRING_SECURITY_CONTEXT.equals(attributeName)) {
|
||||
this.changed = true;
|
||||
@@ -898,8 +851,7 @@ public class JdbcOperationsSessionRepository implements
|
||||
}
|
||||
String attributeName = rs.getString("ATTRIBUTE_NAME");
|
||||
if (attributeName != null) {
|
||||
Object attributeValue = getBlobAsObject(rs, "ATTRIBUTE_BYTES");
|
||||
session.delegate.setAttribute(attributeName, lazily(() -> attributeValue));
|
||||
session.delegate.setAttribute(attributeName, deserialize(rs, "ATTRIBUTE_BYTES"));
|
||||
}
|
||||
sessions.add(session);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 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.
|
||||
@@ -35,6 +35,9 @@ import org.springframework.core.serializer.support.DeserializingConverter;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
import org.springframework.jdbc.support.MetaDataAccessException;
|
||||
import org.springframework.jdbc.support.lob.DefaultLobHandler;
|
||||
import org.springframework.jdbc.support.lob.LobHandler;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
@@ -102,6 +105,11 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
if (this.lobHandler != null) {
|
||||
sessionRepository.setLobHandler(this.lobHandler);
|
||||
}
|
||||
else if (requiresTemporaryLob(this.dataSource)) {
|
||||
DefaultLobHandler lobHandler = new DefaultLobHandler();
|
||||
lobHandler.setCreateTemporaryLob(true);
|
||||
sessionRepository.setLobHandler(lobHandler);
|
||||
}
|
||||
if (this.springSessionConversionService != null) {
|
||||
sessionRepository.setConversionService(this.springSessionConversionService);
|
||||
}
|
||||
@@ -115,6 +123,17 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
private static boolean requiresTemporaryLob(DataSource dataSource) {
|
||||
try {
|
||||
String productName = JdbcUtils.extractDatabaseMetaData(dataSource,
|
||||
"getDatabaseProductName");
|
||||
return "Oracle".equalsIgnoreCase(JdbcUtils.commonDatabaseName(productName));
|
||||
}
|
||||
catch (MetaDataAccessException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
|
||||
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
|
||||
}
|
||||
|
||||
@@ -42,10 +42,7 @@ import org.springframework.transaction.TransactionDefinition;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.endsWith;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.ArgumentMatchers.startsWith;
|
||||
@@ -91,7 +88,7 @@ public class JdbcOperationsSessionRepositoryTests {
|
||||
assertThatThrownBy(
|
||||
() -> new JdbcOperationsSessionRepository(this.jdbcOperations, null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("TransactionManager must not be null");
|
||||
.hasMessage("Property 'transactionManager' is required");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -691,89 +688,6 @@ public class JdbcOperationsSessionRepositoryTests {
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveNewWithoutTransaction() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository
|
||||
.createSession();
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.jdbcOperations, times(1)).update(
|
||||
startsWith("INSERT INTO SPRING_SESSION"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveUpdatedWithoutTransaction() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(
|
||||
"primaryKey", new MapSession());
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
|
||||
this.repository.save(session);
|
||||
|
||||
verify(this.jdbcOperations, times(1)).update(startsWith("UPDATE SPRING_SESSION"),
|
||||
isA(PreparedStatementSetter.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void findByIdWithoutTransaction() {
|
||||
given(this.jdbcOperations.query(anyString(), any(PreparedStatementSetter.class),
|
||||
any(ResultSetExtractor.class))).willReturn(Collections.emptyList());
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
this.repository.findById("testSessionId");
|
||||
|
||||
verify(this.jdbcOperations, times(1)).query(endsWith("WHERE S.SESSION_ID = ?"),
|
||||
isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteByIdWithoutTransaction() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
this.repository.deleteById("testSessionId");
|
||||
|
||||
verify(this.jdbcOperations, times(1)).update(
|
||||
eq("DELETE FROM SPRING_SESSION WHERE SESSION_ID = ?"), anyString());
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void findByIndexNameAndIndexValueWithoutTransaction() {
|
||||
given(this.jdbcOperations.query(anyString(), any(PreparedStatementSetter.class),
|
||||
any(ResultSetExtractor.class))).willReturn(Collections.emptyList());
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
this.repository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
"testIndexValue");
|
||||
|
||||
verify(this.jdbcOperations, times(1)).query(
|
||||
endsWith("WHERE S.PRINCIPAL_NAME = ?"),
|
||||
isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class));
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanUpExpiredSessionsWithoutTransaction() {
|
||||
this.repository = new JdbcOperationsSessionRepository(this.jdbcOperations);
|
||||
this.repository.cleanUpExpiredSessions();
|
||||
|
||||
verify(this.jdbcOperations, times(1)).update(
|
||||
eq("DELETE FROM SPRING_SESSION WHERE EXPIRY_TIME < ?"), anyLong());
|
||||
verifyZeroInteractions(this.jdbcOperations);
|
||||
verifyZeroInteractions(this.transactionManager);
|
||||
}
|
||||
|
||||
private void assertPropagationRequiresNew() {
|
||||
ArgumentCaptor<TransactionDefinition> argument =
|
||||
ArgumentCaptor.forClass(TransactionDefinition.class);
|
||||
|
||||
Reference in New Issue
Block a user