Compare commits
128 Commits
2.1.0.M1
...
2.1.3.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5fc4b57ad | ||
|
|
3b826e51a1 | ||
|
|
67063ec0c6 | ||
|
|
72198e9e80 | ||
|
|
b3ee28b972 | ||
|
|
2d498bf69d | ||
|
|
c365e4d941 | ||
|
|
f7f8d4f6c0 | ||
|
|
fd5115fae5 | ||
|
|
a4c39fde9f | ||
|
|
96391ce41a | ||
|
|
d48eebea99 | ||
|
|
57cd6c367d | ||
|
|
68f83b00eb | ||
|
|
a4a5b529ef | ||
|
|
f5ae38d94c | ||
|
|
b201ed971c | ||
|
|
70346b0a84 | ||
|
|
d4fd8b97b4 | ||
|
|
b3d01063d9 | ||
|
|
124565306b | ||
|
|
f709a6c787 | ||
|
|
c354927ef3 | ||
|
|
2db79e2bb8 | ||
|
|
3480c65c2b | ||
|
|
e0dc0262ef | ||
|
|
3b7da0c370 | ||
|
|
72984f9ca6 | ||
|
|
8a4872b919 | ||
|
|
6b6c6f27df | ||
|
|
640bee3fc4 | ||
|
|
3bfdb9be93 | ||
|
|
c8f3d1a1ec | ||
|
|
11ad1db6e7 | ||
|
|
7b87128db6 | ||
|
|
bf861933ed | ||
|
|
979e91256d | ||
|
|
05986d68b2 | ||
|
|
e17b047800 | ||
|
|
5ab2424b14 | ||
|
|
196919efbb | ||
|
|
717e16cb71 | ||
|
|
5f1b7d6722 | ||
|
|
4d3a01919c | ||
|
|
e408d7f557 | ||
|
|
f34acebf84 | ||
|
|
1aab3e8285 | ||
|
|
c3528996d2 | ||
|
|
3ccc3eb6e1 | ||
|
|
de76be95ac | ||
|
|
bc127ab3fc | ||
|
|
3e9f6a35c4 | ||
|
|
49daa3a9c7 | ||
|
|
a67bd634d9 | ||
|
|
2762f001bf | ||
|
|
93aee206fb | ||
|
|
3df3b30117 | ||
|
|
5fb0c4dd35 | ||
|
|
6fbce6e3e8 | ||
|
|
a3fd05326a | ||
|
|
4c6dc976b3 | ||
|
|
58ae28b0a0 | ||
|
|
3e98ecf234 | ||
|
|
41ed429f98 | ||
|
|
def15b05ca | ||
|
|
eae8592f2b | ||
|
|
81460ede09 | ||
|
|
ca4ec9a557 | ||
|
|
fd2165f471 | ||
|
|
ad1e57a1fe | ||
|
|
0ffcaa2d35 | ||
|
|
b61937def7 | ||
|
|
c523fb591d | ||
|
|
227fab2e42 | ||
|
|
7f7815d80c | ||
|
|
002136bad4 | ||
|
|
1085661984 | ||
|
|
12bb0741bb | ||
|
|
eecdcb49d9 | ||
|
|
3e1a22102d | ||
|
|
9f6e791e5d | ||
|
|
efc35eddad | ||
|
|
4c37ec9f4a | ||
|
|
1a3da5944d | ||
|
|
5d0775b802 | ||
|
|
603a258172 | ||
|
|
22ebe65931 | ||
|
|
55033bcb64 | ||
|
|
57955b7d7b | ||
|
|
d5da38f2e0 | ||
|
|
6cc4bcd13d | ||
|
|
dc43f5bd2d | ||
|
|
7584cbd54c | ||
|
|
0db1160dc4 | ||
|
|
10a18366f9 | ||
|
|
7ea5e2f3ee | ||
|
|
d3134ad065 | ||
|
|
6208d0298d | ||
|
|
c031ee278d | ||
|
|
8267a90fcc | ||
|
|
2113b330a7 | ||
|
|
c4ac68b777 | ||
|
|
0be2759e68 | ||
|
|
1181e52bb0 | ||
|
|
5277d945ed | ||
|
|
1fc0162fe9 | ||
|
|
9df259b1ae | ||
|
|
0c2f756533 | ||
|
|
de16c304ea | ||
|
|
3ce3962ebd | ||
|
|
3c4a309a0f | ||
|
|
38de434158 | ||
|
|
7ef0faf259 | ||
|
|
f65cee0a7b | ||
|
|
a2cd1e37fa | ||
|
|
b768042506 | ||
|
|
3140bd06b2 | ||
|
|
172c18d666 | ||
|
|
7fdf2876b2 | ||
|
|
87c2e53b5a | ||
|
|
268ba663e5 | ||
|
|
3f4873f0eb | ||
|
|
644239ee14 | ||
|
|
97e52de41b | ||
|
|
f4bbc18f94 | ||
|
|
dfe216b482 | ||
|
|
a976c9dd6d | ||
|
|
deb2863507 |
138
Jenkinsfile
vendored
138
Jenkinsfile
vendored
@@ -1,9 +1,9 @@
|
||||
def projectProperties = [
|
||||
[$class: 'BuildDiscarderProperty',
|
||||
strategy: [$class: 'LogRotator', numToKeepStr: '5']],
|
||||
pipelineTriggers([cron('@daily')])
|
||||
]
|
||||
properties(projectProperties)
|
||||
properties([
|
||||
buildDiscarder(logRotator(numToKeepStr: '10')),
|
||||
pipelineTriggers([
|
||||
cron('@daily')
|
||||
]),
|
||||
])
|
||||
|
||||
def SUCCESS = hudson.model.Result.SUCCESS.toString()
|
||||
currentBuild.result = SUCCESS
|
||||
@@ -11,34 +11,98 @@ currentBuild.result = SUCCESS
|
||||
try {
|
||||
parallel check: {
|
||||
stage('Check') {
|
||||
node {
|
||||
checkout scm
|
||||
try {
|
||||
sh "./gradlew clean check --refresh-dependencies --no-daemon"
|
||||
} catch(Exception e) {
|
||||
currentBuild.result = 'FAILED: check'
|
||||
throw e
|
||||
} finally {
|
||||
junit '**/build/*-results/*.xml'
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('ubuntu1804') {
|
||||
checkout scm
|
||||
try {
|
||||
sh './gradlew clean check --no-daemon --refresh-dependencies'
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: check'
|
||||
throw e
|
||||
}
|
||||
finally {
|
||||
junit '**/build/test-results/*/*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk9: {
|
||||
stage('JDK 9') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk9'}"]) {
|
||||
sh './gradlew clean test --no-daemon --refresh-dependencies'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk9'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk10: {
|
||||
stage('JDK 10') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk10'}"]) {
|
||||
sh './gradlew clean test --no-daemon --refresh-dependencies'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk10'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(currentBuild.result == 'SUCCESS') {
|
||||
if (currentBuild.result == 'SUCCESS') {
|
||||
parallel artifacts: {
|
||||
stage('Deploy Artifacts') {
|
||||
node {
|
||||
checkout scm
|
||||
withCredentials([file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')]) {
|
||||
withCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) {
|
||||
withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) {
|
||||
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
|
||||
sh "./gradlew deployArtifacts finalizeDeployArtifacts -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password='$SIGNING_PASSWORD' -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
|
||||
try {
|
||||
withCredentials([file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')]) {
|
||||
withCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) {
|
||||
withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) {
|
||||
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
|
||||
sh './gradlew deployArtifacts finalizeDeployArtifacts --stacktrace --no-daemon --refresh-dependencies -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password=$SIGNING_PASSWORD -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: artifacts'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -46,32 +110,38 @@ try {
|
||||
stage('Deploy Docs') {
|
||||
node {
|
||||
checkout scm
|
||||
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
|
||||
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
|
||||
try {
|
||||
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
|
||||
sh './gradlew deployDocs --stacktrace --no-daemon --refresh-dependencies -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: docs'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
def buildStatus = currentBuild.result
|
||||
def buildNotSuccess = !SUCCESS.equals(buildStatus)
|
||||
def buildNotSuccess = !SUCCESS.equals(buildStatus)
|
||||
def lastBuildNotSuccess = !SUCCESS.equals(currentBuild.previousBuild?.result)
|
||||
|
||||
if(buildNotSuccess || lastBuildNotSuccess) {
|
||||
|
||||
stage('Notifiy') {
|
||||
if (buildNotSuccess || lastBuildNotSuccess) {
|
||||
stage('Notify') {
|
||||
node {
|
||||
final def RECIPIENTS = [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']]
|
||||
|
||||
def subject = "${buildStatus}: Build ${env.JOB_NAME} ${env.BUILD_NUMBER} status is now ${buildStatus}"
|
||||
def details = """The build status changed to ${buildStatus}. For details see ${env.BUILD_URL}"""
|
||||
def details = "The build status changed to ${buildStatus}. For details see ${env.BUILD_URL}"
|
||||
|
||||
emailext (
|
||||
subject: subject,
|
||||
body: details,
|
||||
recipientProviders: RECIPIENTS,
|
||||
to: "$SPRING_SESSION_TEAM_EMAILS"
|
||||
emailext(
|
||||
subject: subject,
|
||||
body: details,
|
||||
recipientProviders: RECIPIENTS,
|
||||
to: "$SPRING_SESSION_TEAM_EMAILS"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
38
build.gradle
38
build.gradle
@@ -1,20 +1,38 @@
|
||||
buildscript {
|
||||
ext {
|
||||
releaseBuild = version.endsWith('RELEASE')
|
||||
snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
springBootVersion = '2.1.1.RELEASE'
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven { url 'https://repo.spring.io/plugins-release/' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.17.RELEASE'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.22.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'
|
||||
|
||||
|
||||
ext.releaseBuild = version.endsWith('RELEASE')
|
||||
ext.snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
ext.milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,9 +614,10 @@ 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 support implements `FindByIndexNameSessionRepository`.
|
||||
For example, Spring's Redis, JDBC and Hazelcast support all implement `FindByIndexNameSessionRepository`.
|
||||
|
||||
The `FindByIndexNameSessionRepository` adds a single method to look up all the sessions for a particular user.
|
||||
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.
|
||||
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-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.
|
||||
@@ -52,9 +52,7 @@ public class FindByIndexNameSessionRepositoryTests {
|
||||
// tag::findby-username[]
|
||||
String username = "username";
|
||||
Map<String, Session> sessionIdToSession = this.sessionRepository
|
||||
.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
username);
|
||||
.findByPrincipalName(username);
|
||||
// end::findby-username[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
|
||||
@@ -54,7 +53,7 @@ public class RememberMeSecurityConfiguration extends WebSecurityConfigurerAdapte
|
||||
|
||||
// tag::rememberme-bean[]
|
||||
@Bean
|
||||
RememberMeServices rememberMeServices() {
|
||||
public SpringSessionRememberMeServices rememberMeServices() {
|
||||
SpringSessionRememberMeServices rememberMeServices =
|
||||
new SpringSessionRememberMeServices();
|
||||
// optionally customize
|
||||
|
||||
@@ -16,18 +16,16 @@
|
||||
|
||||
package docs.security;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
@@ -81,7 +79,7 @@ public class RememberMeSecurityConfigurationTests<T extends Session> {
|
||||
.andReturn();
|
||||
// @formatter:on
|
||||
|
||||
HttpCookie cookie = getSessionCookie(result.getResponse());
|
||||
Cookie cookie = result.getResponse().getCookie("SESSION");
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(Integer.MAX_VALUE);
|
||||
T session = this.sessions
|
||||
.findById(new String(Base64.getDecoder().decode(cookie.getValue())));
|
||||
@@ -90,14 +88,5 @@ public class RememberMeSecurityConfigurationTests<T extends Session> {
|
||||
|
||||
}
|
||||
|
||||
private HttpCookie getSessionCookie(HttpServletResponse response) {
|
||||
for (HttpCookie cookie : HttpCookie.parse(response.getHeader(HttpHeaders.SET_COOKIE))) {
|
||||
if ("SESSION".equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
// end::class[]
|
||||
|
||||
@@ -16,18 +16,16 @@
|
||||
|
||||
package docs.security;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
@@ -81,7 +79,7 @@ public class RememberMeSecurityConfigurationXmlTests<T extends Session> {
|
||||
.andReturn();
|
||||
// @formatter:on
|
||||
|
||||
HttpCookie cookie = getSessionCookie(result.getResponse());
|
||||
Cookie cookie = result.getResponse().getCookie("SESSION");
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(Integer.MAX_VALUE);
|
||||
T session = this.sessions
|
||||
.findById(new String(Base64.getDecoder().decode(cookie.getValue())));
|
||||
@@ -90,14 +88,5 @@ public class RememberMeSecurityConfigurationXmlTests<T extends Session> {
|
||||
|
||||
}
|
||||
|
||||
private HttpCookie getSessionCookie(HttpServletResponse response) {
|
||||
for (HttpCookie cookie : HttpCookie.parse(response.getHeader(HttpHeaders.SET_COOKIE))) {
|
||||
if ("SESSION".equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
// end::class[]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,10 +30,11 @@ import org.springframework.session.security.SpringSessionBackedSessionRegistry;
|
||||
*/
|
||||
// tag::class[]
|
||||
@Configuration
|
||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
public class SecurityConfiguration<S extends Session>
|
||||
extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private FindByIndexNameSessionRepository<Session> sessionRepository;
|
||||
private FindByIndexNameSessionRepository<S> sessionRepository;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
@@ -47,7 +48,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpringSessionBackedSessionRegistry sessionRegistry() {
|
||||
public SpringSessionBackedSessionRegistry<S> sessionRegistry() {
|
||||
return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<module name="Checker">
|
||||
<!-- Supressions -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${configDir}/suppressions.xml"/>
|
||||
<property name="file" value="${config_loc}/suppressions.xml"/>
|
||||
</module>
|
||||
|
||||
<!-- Root Checks -->
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
^\Q/*\E$
|
||||
^\Q * Copyright 2014-\E20\d\d\Q the original author or authors.\E$
|
||||
^\Q *\E$
|
||||
^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$
|
||||
^\Q * you may not use this file except in compliance with the License.\E$
|
||||
^\Q * You may obtain a copy of the License at\E$
|
||||
^\Q *\E$
|
||||
^\Q * http://www.apache.org/licenses/LICENSE-2.0\E$
|
||||
^\Q *\E$
|
||||
^\Q * Unless required by applicable law or agreed to in writing, software\E$
|
||||
^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$
|
||||
^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$
|
||||
^\Q * See the License for the specific language governing permissions and\E$
|
||||
^\Q * limitations under the License.\E$
|
||||
^\Q */\E$
|
||||
^.*$
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
<!-- docs -->
|
||||
<suppress files="[\\/]docs[\\/]" checks="Javadoc*"/>
|
||||
<suppress files="[\\/]docs[\\/]" checks="AvoidStaticImport"/>
|
||||
<suppress files="[\\/]docs[\\/]" checks="InnerTypeLast"/>
|
||||
|
||||
<!-- samples -->
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
springBootVersion=2.0.3.RELEASE
|
||||
version=2.1.0.M1
|
||||
version=2.1.3.RELEASE
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.6'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Californium-M1'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.1.0.RC1'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Lovelace-RC1'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.1.0.M2'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.8.1'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Californium-SR4'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.1.4.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Lovelace-SR4'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.1.3.RELEASE'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.10.5'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.10.3') {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.11.1') {
|
||||
entry 'hazelcast'
|
||||
entry 'hazelcast-client'
|
||||
}
|
||||
|
||||
dependency 'com.h2database:h2:1.4.197'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:6.4.0.jre8'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:7.0.0.jre8'
|
||||
dependency 'com.zaxxer:HikariCP:3.3.0'
|
||||
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
|
||||
dependency 'io.lettuce:lettuce-core:5.1.0.M1'
|
||||
dependency 'javax.servlet:javax.servlet-api:3.1.0'
|
||||
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 'junit:junit:4.12'
|
||||
dependency 'mysql:mysql-connector-java:8.0.11'
|
||||
dependency 'mysql:mysql-connector-java:8.0.13'
|
||||
dependency 'org.apache.derby:derby:10.14.2.0'
|
||||
dependency 'org.assertj:assertj-core:3.10.0'
|
||||
dependency 'org.assertj:assertj-core:3.11.1'
|
||||
dependency 'org.hsqldb:hsqldb:2.4.1'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.2.6'
|
||||
dependency 'org.mockito:mockito-core:2.20.1'
|
||||
dependency 'org.postgresql:postgresql:42.2.4'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.3.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.9-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,20 +16,6 @@
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -44,11 +30,8 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
|
||||
@@ -63,7 +46,7 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
|
||||
public class FindByUsernameTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@@ -113,62 +96,6 @@ public class FindByUsernameTests {
|
||||
redisContainer().getFirstMappedPort());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<SetCookieHandlerFilter> testFilter() {
|
||||
FilterRegistrationBean<SetCookieHandlerFilter> registrationBean = new FilterRegistrationBean<>(
|
||||
new SetCookieHandlerFilter());
|
||||
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SetCookieHandlerFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(
|
||||
httpServletResponse) {
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value) {
|
||||
if (HttpHeaders.SET_COOKIE.equals(name)) {
|
||||
List<HttpCookie> cookies = HttpCookie.parse(value);
|
||||
if (!cookies.isEmpty()) {
|
||||
addCookie(toServletCookie(cookies.get(0)));
|
||||
}
|
||||
}
|
||||
super.setHeader(name, value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
chain.doFilter(request, responseWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
private static Cookie toServletCookie(HttpCookie httpCookie) {
|
||||
Cookie cookie = new Cookie(httpCookie.getName(), httpCookie.getValue());
|
||||
String domain = httpCookie.getDomain();
|
||||
if (domain != null) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
cookie.setMaxAge((int) httpCookie.getMaxAge());
|
||||
cookie.setPath(httpCookie.getPath());
|
||||
cookie.setSecure(httpCookie.getSecure());
|
||||
cookie.setHttpOnly(httpCookie.isHttpOnly());
|
||||
return cookie;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -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.
|
||||
@@ -44,10 +44,7 @@ public class IndexController {
|
||||
@RequestMapping("/")
|
||||
public String index(Principal principal, Model model) {
|
||||
Collection<? extends Session> usersSessions = this.sessions
|
||||
.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
principal.getName())
|
||||
.values();
|
||||
.findByPrincipalName(principal.getName()).values();
|
||||
model.addAttribute("sessions", usersSessions);
|
||||
return "index";
|
||||
}
|
||||
@@ -56,9 +53,8 @@ public class IndexController {
|
||||
@RequestMapping(value = "/sessions/{sessionIdToDelete}", method = RequestMethod.DELETE)
|
||||
public String removeSession(Principal principal,
|
||||
@PathVariable String sessionIdToDelete) {
|
||||
Set<String> usersSessionIds = this.sessions.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
principal.getName()).keySet();
|
||||
Set<String> usersSessionIds = this.sessions
|
||||
.findByPrincipalName(principal.getName()).keySet();
|
||||
if (usersSessionIds.contains(sessionIdToDelete)) {
|
||||
this.sessions.deleteById(sessionIdToDelete);
|
||||
}
|
||||
|
||||
@@ -16,20 +16,6 @@
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -42,11 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
|
||||
@@ -99,65 +80,4 @@ public class BootTests {
|
||||
login.assertAt();
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<SetCookieHandlerFilter> testFilter() {
|
||||
FilterRegistrationBean<SetCookieHandlerFilter> registrationBean = new FilterRegistrationBean<>(
|
||||
new SetCookieHandlerFilter());
|
||||
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SetCookieHandlerFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(
|
||||
httpServletResponse) {
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value) {
|
||||
if (HttpHeaders.SET_COOKIE.equals(name)) {
|
||||
List<HttpCookie> cookies = HttpCookie.parse(value);
|
||||
if (!cookies.isEmpty()) {
|
||||
addCookie(toServletCookie(cookies.get(0)));
|
||||
}
|
||||
}
|
||||
super.setHeader(name, value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
chain.doFilter(request, responseWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
private static Cookie toServletCookie(HttpCookie httpCookie) {
|
||||
Cookie cookie = new Cookie(httpCookie.getName(), httpCookie.getValue());
|
||||
String domain = httpCookie.getDomain();
|
||||
if (domain != null) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
cookie.setMaxAge((int) httpCookie.getMaxAge());
|
||||
cookie.setPath(httpCookie.getPath());
|
||||
cookie.setSecure(httpCookie.getSecure());
|
||||
cookie.setHttpOnly(httpCookie.isHttpOnly());
|
||||
return cookie;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -36,7 +36,7 @@ public class LoginPage extends BasePage {
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
|
||||
}
|
||||
|
||||
public Form form() {
|
||||
@@ -51,7 +51,7 @@ public class LoginPage extends BasePage {
|
||||
@FindBy(name = "password")
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(name = "submit")
|
||||
@FindBy(tagName = "button")
|
||||
private WebElement button;
|
||||
|
||||
public Form(SearchContext context) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,20 +16,8 @@
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -45,11 +33,8 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
|
||||
@@ -65,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@AutoConfigureMockMvc
|
||||
public class HttpRedisJsonTest {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@@ -135,62 +120,6 @@ public class HttpRedisJsonTest {
|
||||
redisContainer().getFirstMappedPort());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<SetCookieHandlerFilter> testFilter() {
|
||||
FilterRegistrationBean<SetCookieHandlerFilter> registrationBean = new FilterRegistrationBean<>(
|
||||
new SetCookieHandlerFilter());
|
||||
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SetCookieHandlerFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(
|
||||
httpServletResponse) {
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value) {
|
||||
if (HttpHeaders.SET_COOKIE.equals(name)) {
|
||||
List<HttpCookie> cookies = HttpCookie.parse(value);
|
||||
if (!cookies.isEmpty()) {
|
||||
addCookie(toServletCookie(cookies.get(0)));
|
||||
}
|
||||
}
|
||||
super.setHeader(name, value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
chain.doFilter(request, responseWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
private static Cookie toServletCookie(HttpCookie httpCookie) {
|
||||
Cookie cookie = new Cookie(httpCookie.getName(), httpCookie.getValue());
|
||||
String domain = httpCookie.getDomain();
|
||||
if (domain != null) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
cookie.setMaxAge((int) httpCookie.getMaxAge());
|
||||
cookie.setPath(httpCookie.getPath());
|
||||
cookie.setSecure(httpCookie.getSecure());
|
||||
cookie.setHttpOnly(httpCookie.isHttpOnly());
|
||||
return cookie;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest
|
||||
public class RedisSerializerTest {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@SpringSessionRedisOperations
|
||||
private RedisTemplate<Object, Object> sessionRedisTemplate;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,20 +16,6 @@
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -44,11 +30,8 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
|
||||
@@ -62,7 +45,7 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
|
||||
public class BootTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@@ -119,62 +102,6 @@ public class BootTests {
|
||||
redisContainer().getFirstMappedPort());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<SetCookieHandlerFilter> testFilter() {
|
||||
FilterRegistrationBean<SetCookieHandlerFilter> registrationBean = new FilterRegistrationBean<>(
|
||||
new SetCookieHandlerFilter());
|
||||
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SetCookieHandlerFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(
|
||||
httpServletResponse) {
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value) {
|
||||
if (HttpHeaders.SET_COOKIE.equals(name)) {
|
||||
List<HttpCookie> cookies = HttpCookie.parse(value);
|
||||
if (!cookies.isEmpty()) {
|
||||
addCookie(toServletCookie(cookies.get(0)));
|
||||
}
|
||||
}
|
||||
super.setHeader(name, value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
chain.doFilter(request, responseWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
private static Cookie toServletCookie(HttpCookie httpCookie) {
|
||||
Cookie cookie = new Cookie(httpCookie.getName(), httpCookie.getValue());
|
||||
String domain = httpCookie.getDomain();
|
||||
if (domain != null) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
cookie.setMaxAge((int) httpCookie.getMaxAge());
|
||||
cookie.setPath(httpCookie.getPath());
|
||||
cookie.setSecure(httpCookie.getSecure());
|
||||
cookie.setHttpOnly(httpCookie.isHttpOnly());
|
||||
return cookie;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -36,7 +36,7 @@ public class LoginPage extends BasePage {
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Please sign in");
|
||||
}
|
||||
|
||||
public Form form() {
|
||||
@@ -51,7 +51,7 @@ public class LoginPage extends BasePage {
|
||||
@FindBy(name = "password")
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(name = "submit")
|
||||
@FindBy(tagName = "button")
|
||||
private WebElement button;
|
||||
|
||||
public Form(SearchContext context) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
public class AttributeTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -23,5 +23,6 @@ dependencies {
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
testCompile "org.springframework.security:spring-security-test"
|
||||
testCompile "org.testcontainers:testcontainers"
|
||||
|
||||
integrationTestCompile "org.testcontainers:testcontainers"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,7 +42,7 @@ import org.springframework.web.socket.sockjs.client.SockJsClient;
|
||||
import org.springframework.web.socket.sockjs.client.Transport;
|
||||
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
@@ -52,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
public class ApplicationTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private String port;
|
||||
@@ -70,8 +70,8 @@ public class ApplicationTests {
|
||||
ListenableFuture<WebSocketSession> wsSession = sockJsClient.doHandshake(
|
||||
this.webSocketHandler, "ws://localhost:" + this.port + "/sockjs");
|
||||
|
||||
assertThatThrownBy(() -> wsSession.get().sendMessage(new TextMessage("a")))
|
||||
.isInstanceOf(ExecutionException.class);
|
||||
assertThatExceptionOfType(ExecutionException.class)
|
||||
.isThrownBy(() -> wsSession.get().sendMessage(new TextMessage("a")));
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
@@ -0,0 +1 @@
|
||||
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.29.3'
|
||||
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.32.0'
|
||||
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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Bean
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Bean
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -17,8 +17,6 @@ dependencies {
|
||||
testCompile "org.springframework.security:spring-security-test"
|
||||
testCompile "org.assertj:assertj-core"
|
||||
testCompile "org.springframework:spring-test"
|
||||
|
||||
integrationTestCompile "org.testcontainers:testcontainers"
|
||||
}
|
||||
|
||||
gretty {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -54,7 +54,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
@WebAppConfiguration
|
||||
public class RestMockMvcTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Autowired
|
||||
private SessionRepositoryFilter<? extends Session> sessionRepositoryFilter;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Pool Dolorier
|
||||
@@ -57,9 +57,9 @@ public class RestTests {
|
||||
public void unauthenticatedUserSentToLogInPage() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
|
||||
assertThatThrownBy(() -> getForUser(this.baseUrl + "/", headers, String.class))
|
||||
.isInstanceOf(HttpClientErrorException.class)
|
||||
.satisfies((e) -> assertThat(((HttpClientErrorException) e).getStatusCode())
|
||||
assertThatExceptionOfType(HttpClientErrorException.class)
|
||||
.isThrownBy(() -> getForUser(this.baseUrl + "/", headers, String.class))
|
||||
.satisfies((e) -> assertThat(e.getStatusCode())
|
||||
.isEqualTo(HttpStatus.UNAUTHORIZED));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Bean
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Bean
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
@Bean
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -2,15 +2,18 @@ rootProject.name = 'spring-session-build'
|
||||
|
||||
FileTree buildFiles = fileTree(rootDir) {
|
||||
include '**/*.gradle'
|
||||
exclude '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*'
|
||||
exclude 'build', '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*', 'out'
|
||||
exclude '**/grails3'
|
||||
gradle.startParameter.projectProperties.get('excludeProjects')?.split(',')?.each { excludeProject ->
|
||||
exclude excludeProject
|
||||
}
|
||||
}
|
||||
|
||||
String rootDirPath = rootDir.absolutePath + File.separator
|
||||
buildFiles.each { File buildFile ->
|
||||
buildFiles.each { buildFile ->
|
||||
if (buildFile.name == 'build.gradle') {
|
||||
String buildFilePath = buildFile.parentFile.absolutePath
|
||||
String projectPath = buildFilePath.replace(rootDirPath, '').replaceAll(File.separator, ':')
|
||||
String projectPath = buildFilePath.replace(rootDirPath, '').replace(File.separator, ':')
|
||||
include projectPath
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -6,6 +6,7 @@ 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,27 +19,22 @@ package org.springframework.session;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* Extends a basic {@link SessionRepository} to allow finding sessions by the specified
|
||||
* index name and index value.
|
||||
*
|
||||
* @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>
|
||||
* 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>
|
||||
* 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.
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -47,17 +42,34 @@ public interface FindByIndexNameSessionRepository<S extends Session>
|
||||
.concat(".PRINCIPAL_NAME_INDEX_NAME");
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Find a {@link Map} of the session id to the {@link Session} of all sessions that
|
||||
* contain the specified index name index value.
|
||||
*
|
||||
* @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 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.
|
||||
* @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.
|
||||
*/
|
||||
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 String originalId;
|
||||
private final String originalId;
|
||||
private Map<String, Object> sessionAttrs = new HashMap<>();
|
||||
private Instant creationTime = Instant.now();
|
||||
private Instant lastAccessedTime = this.creationTime;
|
||||
|
||||
@@ -81,7 +81,7 @@ public interface Session {
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> T getAttributeOrDefault(String name, T defaultValue) {
|
||||
T result = getAttribute(name);
|
||||
return (result != null ? result : defaultValue);
|
||||
return (result != null) ? result : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,8 +110,9 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
CookieSerializer cookieSerializer = (this.cookieSerializer != null
|
||||
? this.cookieSerializer : createDefaultCookieSerializer());
|
||||
CookieSerializer cookieSerializer = (this.cookieSerializer != null)
|
||||
? this.cookieSerializer
|
||||
: createDefaultCookieSerializer();
|
||||
this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -65,9 +65,8 @@ public class SpringSessionBackedSessionRegistry<S extends Session>
|
||||
@Override
|
||||
public List<SessionInformation> getAllSessions(Object principal,
|
||||
boolean includeExpiredSessions) {
|
||||
Collection<S> sessions = this.sessionRepository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
|
||||
name(principal)).values();
|
||||
Collection<S> sessions = this.sessionRepository
|
||||
.findByPrincipalName(name(principal)).values();
|
||||
List<SessionInformation> infos = new ArrayList<>();
|
||||
for (S session : sessions) {
|
||||
if (includeExpiredSessions || !Boolean.TRUE.equals(session
|
||||
|
||||
@@ -135,9 +135,9 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
int maxAge = getMaxAge(cookieValue);
|
||||
if (maxAge > -1) {
|
||||
sb.append("; Max-Age=").append(cookieValue.getCookieMaxAge());
|
||||
OffsetDateTime expires = (maxAge != 0
|
||||
OffsetDateTime expires = (maxAge != 0)
|
||||
? OffsetDateTime.now().plusSeconds(maxAge)
|
||||
: Instant.EPOCH.atOffset(ZoneOffset.UTC));
|
||||
: Instant.EPOCH.atOffset(ZoneOffset.UTC);
|
||||
sb.append("; Expires=")
|
||||
.append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ public class HeaderHttpSessionIdResolver implements HttpSessionIdResolver {
|
||||
@Override
|
||||
public List<String> resolveSessionIds(HttpServletRequest request) {
|
||||
String headerValue = request.getHeader(this.headerName);
|
||||
return (headerValue != null ? Collections.singletonList(headerValue)
|
||||
: Collections.emptyList());
|
||||
return (headerValue != null) ? Collections.singletonList(headerValue)
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
@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 {
|
||||
|
||||
|
||||
@@ -174,11 +174,11 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
}
|
||||
|
||||
private void trackContentLength(byte[] content) {
|
||||
checkContentLength(content != null ? content.length : 0);
|
||||
checkContentLength((content != null) ? content.length : 0);
|
||||
}
|
||||
|
||||
private void trackContentLength(char[] content) {
|
||||
checkContentLength(content != null ? content.length : 0);
|
||||
checkContentLength((content != null) ? content.length : 0);
|
||||
}
|
||||
|
||||
private void trackContentLength(int content) {
|
||||
@@ -257,13 +257,13 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.delegate.hashCode();
|
||||
public boolean equals(Object obj) {
|
||||
return this.delegate.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return this.delegate.equals(obj);
|
||||
public int hashCode() {
|
||||
return this.delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -502,13 +502,13 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.delegate.hashCode();
|
||||
public boolean equals(Object obj) {
|
||||
return this.delegate.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return this.delegate.equals(obj);
|
||||
public int hashCode() {
|
||||
return this.delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
@@ -46,7 +49,7 @@ import org.springframework.session.SessionRepository;
|
||||
* {@link org.springframework.session.SessionRepository}.
|
||||
*
|
||||
* The {@link SessionRepositoryFilter} uses a {@link HttpSessionIdResolver} (default
|
||||
* {@link CookieHttpSessionIdResolver} to bridge logic between an
|
||||
* {@link CookieHttpSessionIdResolver}) to bridge logic between an
|
||||
* {@link javax.servlet.http.HttpSession} and the
|
||||
* {@link org.springframework.session.Session} abstraction. Specifically:
|
||||
*
|
||||
@@ -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 1.3.4
|
||||
*/
|
||||
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,12 +87,6 @@ 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)
|
||||
|
||||
@@ -71,9 +71,9 @@ public final class WebSocketRegistryListener
|
||||
SessionDisconnectEvent e = (SessionDisconnectEvent) event;
|
||||
Map<String, Object> sessionAttributes = SimpMessageHeaderAccessor
|
||||
.getSessionAttributes(e.getMessage().getHeaders());
|
||||
String httpSessionId = (sessionAttributes != null
|
||||
String httpSessionId = (sessionAttributes != null)
|
||||
? SessionRepositoryMessageInterceptor.getSessionId(sessionAttributes)
|
||||
: null);
|
||||
: null;
|
||||
afterConnectionClosed(httpSessionId, e.getSessionId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,8 +117,9 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
|
||||
}
|
||||
Map<String, Object> sessionHeaders = SimpMessageHeaderAccessor
|
||||
.getSessionAttributes(message.getHeaders());
|
||||
String sessionId = (sessionHeaders != null
|
||||
? (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME) : null);
|
||||
String sessionId = (sessionHeaders != null)
|
||||
? (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME)
|
||||
: null;
|
||||
if (sessionId != null) {
|
||||
S session = this.sessionRepository.findById(sessionId);
|
||||
if (session != null) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
public class MapSessionTests {
|
||||
|
||||
@@ -38,9 +38,9 @@ public class MapSessionTests {
|
||||
|
||||
@Test
|
||||
public void constructorNullSession() {
|
||||
assertThatThrownBy(() -> new MapSession((Session) null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("session cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new MapSession((Session) null))
|
||||
.withMessage("session cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,9 +70,9 @@ public class MapSessionTests {
|
||||
|
||||
@Test
|
||||
public void getRequiredAttributeWhenNullThenException() {
|
||||
assertThatThrownBy(() -> this.session.getRequiredAttribute("attrName"))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("Required attribute 'attrName' is missing.");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.session.getRequiredAttribute("attrName"))
|
||||
.withMessage("Required attribute 'attrName' is missing.");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveMapSessionRepository}.
|
||||
@@ -60,9 +60,9 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void constructorMapWhenNullThenThrowsIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new ReactiveMapSessionRepository(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("sessions cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new ReactiveMapSessionRepository(null))
|
||||
.withMessage("sessions cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringHttpSessionConfiguration}.
|
||||
@@ -63,9 +63,9 @@ public class SpringHttpSessionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void noSessionRepositoryConfiguration() {
|
||||
assertThatThrownBy(() -> registerAndRefresh(EmptyConfiguration.class))
|
||||
.isInstanceOf(UnsatisfiedDependencyException.class)
|
||||
.hasMessageContaining("org.springframework.session.SessionRepository");
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> registerAndRefresh(EmptyConfiguration.class))
|
||||
.withMessageContaining("org.springframework.session.SessionRepository");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -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.
|
||||
@@ -17,6 +17,7 @@
|
||||
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;
|
||||
@@ -74,7 +75,9 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
|
||||
assertThat(
|
||||
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isFalse();
|
||||
}
|
||||
@@ -90,7 +93,9 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
.getSessionInformation(SESSION_ID);
|
||||
|
||||
assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID);
|
||||
assertThat(sessionInfo.getLastRequest().toInstant()).isEqualTo(NOW);
|
||||
assertThat(
|
||||
sessionInfo.getLastRequest().toInstant().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(NOW.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(sessionInfo.getPrincipal()).isEqualTo(USER_NAME);
|
||||
assertThat(sessionInfo.isExpired()).isTrue();
|
||||
}
|
||||
@@ -162,9 +167,8 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
Map<String, Session> sessions = new LinkedHashMap<>();
|
||||
sessions.put(session1.getId(), session1);
|
||||
sessions.put(session2.getId(), session2);
|
||||
when(this.sessionRepository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, USER_NAME))
|
||||
.thenReturn(sessions);
|
||||
when(this.sessionRepository.findByPrincipalName(USER_NAME))
|
||||
.thenReturn(sessions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -68,9 +68,10 @@ public class SpringSessionRememberMeServicesTests {
|
||||
@Test
|
||||
public void createWithNullParameter() {
|
||||
this.rememberMeServices = new SpringSessionRememberMeServices();
|
||||
assertThatThrownBy(() -> this.rememberMeServices.setRememberMeParameterName(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("rememberMeParameterName cannot be empty or null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(
|
||||
() -> this.rememberMeServices.setRememberMeParameterName(null))
|
||||
.withMessage("rememberMeParameterName cannot be empty or null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -18,14 +18,12 @@ package org.springframework.session.web.http;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.MapSession;
|
||||
@@ -85,7 +83,7 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
|
||||
assertThat(this.response.getHeaders("Set-Cookie")).hasSize(1);
|
||||
assertThat(this.response.getCookies()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -95,12 +93,11 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
this.strategy.setSessionId(this.request, this.response, newSession.getId());
|
||||
|
||||
List<ResponseCookie> cookies = ResponseCookieParser.parse(this.response);
|
||||
Cookie[] cookies = this.response.getCookies();
|
||||
assertThat(cookies).hasSize(2);
|
||||
|
||||
assertThat(base64Decode(cookies.get(0).getValue()))
|
||||
.isEqualTo(this.session.getId());
|
||||
assertThat(base64Decode(cookies.get(1).getValue())).isEqualTo(newSession.getId());
|
||||
assertThat(base64Decode(cookies[0].getValue())).isEqualTo(this.session.getId());
|
||||
assertThat(base64Decode(cookies[1].getValue())).isEqualTo(newSession.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,7 +105,7 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.request.setContextPath("/somethingunique");
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
|
||||
ResponseCookie sessionCookie = getCookie();
|
||||
Cookie sessionCookie = this.response.getCookie(this.cookieName);
|
||||
assertThat(sessionCookie.getPath())
|
||||
.isEqualTo(this.request.getContextPath() + "/");
|
||||
}
|
||||
@@ -131,7 +128,7 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.request.setContextPath("/somethingunique");
|
||||
this.strategy.expireSession(this.request, this.response);
|
||||
|
||||
ResponseCookie sessionCookie = getCookie();
|
||||
Cookie sessionCookie = this.response.getCookie(this.cookieName);
|
||||
assertThat(sessionCookie.getPath())
|
||||
.isEqualTo(this.request.getContextPath() + "/");
|
||||
}
|
||||
@@ -176,12 +173,8 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.request.setCookies(new Cookie(this.cookieName, base64Encode(value)));
|
||||
}
|
||||
|
||||
private ResponseCookie getCookie() {
|
||||
return ResponseCookieParser.parse(this.response, this.cookieName);
|
||||
}
|
||||
|
||||
private String getSessionId() {
|
||||
return base64Decode(getCookie().getValue());
|
||||
return base64Decode(this.response.getCookie(this.cookieName).getValue());
|
||||
}
|
||||
|
||||
private static String base64Encode(String value) {
|
||||
|
||||
@@ -26,14 +26,14 @@ import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import org.springframework.http.ResponseCookie;
|
||||
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;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultCookieSerializer}.
|
||||
@@ -214,9 +214,9 @@ public class DefaultCookieSerializerTests {
|
||||
@Test
|
||||
public void setDomainNameAndDomainNamePatternThrows() {
|
||||
this.serializer.setDomainName("example.com");
|
||||
assertThatThrownBy(() -> this.serializer.setDomainNamePattern(".*"))
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessage("Cannot set both domainName and domainNamePattern");
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> this.serializer.setDomainNamePattern(".*"))
|
||||
.withMessage("Cannot set both domainName and domainNamePattern");
|
||||
}
|
||||
|
||||
// --- domainNamePattern ---
|
||||
@@ -248,9 +248,9 @@ public class DefaultCookieSerializerTests {
|
||||
@Test
|
||||
public void setDomainNamePatternAndDomainNameThrows() {
|
||||
this.serializer.setDomainNamePattern(".*");
|
||||
assertThatThrownBy(() -> this.serializer.setDomainName("example.com"))
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessage("Cannot set both domainName and domainNamePattern");
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> this.serializer.setDomainName("example.com"))
|
||||
.withMessage("Cannot set both domainName and domainNamePattern");
|
||||
}
|
||||
|
||||
// --- cookieName ---
|
||||
@@ -274,9 +274,9 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
@Test
|
||||
public void setCookieNameNullThrows() {
|
||||
assertThatThrownBy(() -> this.serializer.setCookieName(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("cookieName cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.serializer.setCookieName(null))
|
||||
.withMessage("cookieName cannot be null");
|
||||
}
|
||||
|
||||
// --- cookiePath ---
|
||||
@@ -325,7 +325,7 @@ public class DefaultCookieSerializerTests {
|
||||
public void writeCookieCookieMaxAgeDefault() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(-1);
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -334,7 +334,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(100);
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -343,7 +343,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(""));
|
||||
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(0);
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -353,7 +353,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue);
|
||||
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(100);
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
}
|
||||
|
||||
// --- secure ---
|
||||
@@ -362,7 +362,7 @@ public class DefaultCookieSerializerTests {
|
||||
public void writeCookieDefaultInsecureRequest() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().isSecure()).isFalse();
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -372,7 +372,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().isSecure()).isTrue();
|
||||
assertThat(getCookie().getSecure()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -381,7 +381,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().isSecure()).isTrue();
|
||||
assertThat(getCookie().getSecure()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -391,7 +391,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().isSecure()).isFalse();
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -400,7 +400,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().isSecure()).isFalse();
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
}
|
||||
|
||||
// --- jvmRoute ---
|
||||
@@ -453,7 +453,7 @@ public class DefaultCookieSerializerTests {
|
||||
this.serializer.setRememberMeRequestAttribute("rememberMe");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(Integer.MAX_VALUE);
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -464,7 +464,7 @@ public class DefaultCookieSerializerTests {
|
||||
cookieValue.setCookieMaxAge(100);
|
||||
this.serializer.writeCookieValue(cookieValue);
|
||||
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(100);
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
}
|
||||
|
||||
// --- sameSite ---
|
||||
@@ -512,8 +512,8 @@ public class DefaultCookieSerializerTests {
|
||||
return new Cookie(name, value);
|
||||
}
|
||||
|
||||
private ResponseCookie getCookie() {
|
||||
return ResponseCookieParser.parse(this.response, this.cookieName);
|
||||
private MockCookie getCookie() {
|
||||
return (MockCookie) this.response.getCookie(this.cookieName);
|
||||
}
|
||||
|
||||
private String getCookieValue() {
|
||||
@@ -521,6 +521,9 @@ public class DefaultCookieSerializerTests {
|
||||
if (!this.useBase64Encoding) {
|
||||
return value;
|
||||
}
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(Base64.getDecoder().decode(value));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link HeaderHttpSessionIdResolver}.
|
||||
@@ -74,9 +74,9 @@ public class HeaderHttpSessionIdResolverTests {
|
||||
|
||||
@Test
|
||||
public void createResolverWithNullHeaderName() {
|
||||
assertThatThrownBy(() -> new HeaderHttpSessionIdResolver(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("headerName cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new HeaderHttpSessionIdResolver(null))
|
||||
.withMessage("headerName cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
final class ResponseCookieParser {
|
||||
|
||||
private ResponseCookieParser() {
|
||||
}
|
||||
|
||||
static List<ResponseCookie> parse(HttpServletResponse response) {
|
||||
return doParse(response, null);
|
||||
}
|
||||
|
||||
static ResponseCookie parse(HttpServletResponse response, String cookieName) {
|
||||
List<ResponseCookie> responseCookies = doParse(response, cookieName);
|
||||
return (!responseCookies.isEmpty() ? responseCookies.get(0) : null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<ResponseCookie> doParse(HttpServletResponse response,
|
||||
String cookieName) {
|
||||
List<ResponseCookie> responseCookies = new ArrayList<>();
|
||||
for (String setCookieHeader : response.getHeaders(HttpHeaders.SET_COOKIE)) {
|
||||
String[] cookieParts = setCookieHeader.split("\\s*=\\s*", 2);
|
||||
if (cookieParts.length != 2) {
|
||||
return null;
|
||||
}
|
||||
String name = cookieParts[0];
|
||||
if (cookieName != null && !name.equals(cookieName)) {
|
||||
continue;
|
||||
}
|
||||
String[] valueAndDirectives = cookieParts[1].split("\\s*;\\s*", 2);
|
||||
String value = valueAndDirectives[0];
|
||||
String[] directives = valueAndDirectives[1].split("\\s*;\\s*");
|
||||
String domain = null;
|
||||
int maxAge = -1;
|
||||
String path = null;
|
||||
boolean secure = false;
|
||||
boolean httpOnly = false;
|
||||
String sameSite = null;
|
||||
for (String directive : directives) {
|
||||
if (directive.startsWith("Domain")) {
|
||||
domain = directive.split("=")[1];
|
||||
}
|
||||
if (directive.startsWith("Max-Age")) {
|
||||
maxAge = Integer.parseInt(directive.split("=")[1]);
|
||||
}
|
||||
if (directive.startsWith("Path")) {
|
||||
path = directive.split("=")[1];
|
||||
}
|
||||
if (directive.startsWith("Secure")) {
|
||||
secure = true;
|
||||
}
|
||||
if (directive.startsWith("HttpOnly")) {
|
||||
httpOnly = true;
|
||||
}
|
||||
if (directive.startsWith("SameSite")) {
|
||||
sameSite = directive.split("=")[1];
|
||||
}
|
||||
}
|
||||
responseCookies.add(ResponseCookie.from(name, value).maxAge(maxAge).path(path)
|
||||
.domain(domain).secure(secure).httpOnly(httpOnly).sameSite(sameSite)
|
||||
.build());
|
||||
}
|
||||
return responseCookies;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,6 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
@@ -63,7 +62,7 @@ import org.springframework.session.SessionRepository;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
@@ -424,7 +423,7 @@ public class SessionRepositoryFilterTests {
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(getSessionCookie()).isNull();
|
||||
assertThat(this.response.getCookie("SESSION")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -442,7 +441,7 @@ public class SessionRepositoryFilterTests {
|
||||
wrappedRequest.getSession();
|
||||
}
|
||||
});
|
||||
assertThat(getSessionCookie()).isNotNull();
|
||||
assertThat(this.response.getCookie("SESSION")).isNotNull();
|
||||
|
||||
nextRequest();
|
||||
|
||||
@@ -454,7 +453,7 @@ public class SessionRepositoryFilterTests {
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(getSessionCookie()).isNotNull();
|
||||
assertThat(this.response.getCookie("SESSION")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -654,10 +653,10 @@ public class SessionRepositoryFilterTests {
|
||||
}
|
||||
});
|
||||
|
||||
ResponseCookie session = getSessionCookie();
|
||||
Cookie session = getSessionCookie();
|
||||
assertThat(session.isHttpOnly()).describedAs("Session Cookie should be HttpOnly")
|
||||
.isTrue();
|
||||
assertThat(session.isSecure())
|
||||
assertThat(session.getSecure())
|
||||
.describedAs("Session Cookie should be marked as Secure").isTrue();
|
||||
}
|
||||
|
||||
@@ -1171,6 +1170,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
|
||||
@@ -1197,6 +1213,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);
|
||||
@@ -1379,16 +1418,16 @@ public class SessionRepositoryFilterTests {
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void doesNotImplementOrdered() {
|
||||
assertThatThrownBy(() -> {
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> {
|
||||
Ordered o = (Ordered) this.filter;
|
||||
}).isInstanceOf(ClassCastException.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setHttpSessionIdResolverNull() {
|
||||
assertThatThrownBy(() -> this.filter.setHttpSessionIdResolver(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("httpSessionIdResolver cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.filter.setHttpSessionIdResolver(null))
|
||||
.withMessage("httpSessionIdResolver cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1510,13 +1549,13 @@ public class SessionRepositoryFilterTests {
|
||||
// --- helper methods
|
||||
|
||||
private void assertNewSession() {
|
||||
ResponseCookie cookie = getSessionCookie();
|
||||
Cookie cookie = getSessionCookie();
|
||||
assertThat(cookie).isNotNull();
|
||||
assertThat(cookie.getMaxAge().getSeconds()).isEqualTo(-1);
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(-1);
|
||||
assertThat(cookie.getValue()).isNotEqualTo("INVALID");
|
||||
assertThat(cookie.isHttpOnly()).describedAs("Cookie is expected to be HTTP Only")
|
||||
.isTrue();
|
||||
assertThat(cookie.isSecure())
|
||||
assertThat(cookie.getSecure())
|
||||
.describedAs(
|
||||
"Cookie secured is expected to be " + this.request.isSecure())
|
||||
.isEqualTo(this.request.isSecure());
|
||||
@@ -1526,15 +1565,15 @@ public class SessionRepositoryFilterTests {
|
||||
}
|
||||
|
||||
private void assertNoSession() {
|
||||
ResponseCookie cookie = getSessionCookie();
|
||||
Cookie cookie = getSessionCookie();
|
||||
assertThat(cookie).isNull();
|
||||
assertThat(this.request.getSession(false))
|
||||
.describedAs("The original HttpServletRequest HttpSession should be null")
|
||||
.isNull();
|
||||
}
|
||||
|
||||
private ResponseCookie getSessionCookie() {
|
||||
return ResponseCookieParser.parse(this.response, "SESSION");
|
||||
private Cookie getSessionCookie() {
|
||||
return this.response.getCookie("SESSION");
|
||||
}
|
||||
|
||||
private void setSessionCookie(String sessionId) {
|
||||
@@ -1557,9 +1596,6 @@ public class SessionRepositoryFilterTests {
|
||||
for (Cookie cookie : this.response.getCookies()) {
|
||||
nameToCookie.put(cookie.getName(), cookie);
|
||||
}
|
||||
ResponseCookieParser.parse(this.response)
|
||||
.forEach((responseCookie) -> nameToCookie.put(responseCookie.getName(),
|
||||
toServletCookie(responseCookie)));
|
||||
Cookie[] nextRequestCookies = new ArrayList<>(nameToCookie.values())
|
||||
.toArray(new Cookie[0]);
|
||||
|
||||
@@ -1590,19 +1626,6 @@ public class SessionRepositoryFilterTests {
|
||||
return new String(Base64.getDecoder().decode(value));
|
||||
}
|
||||
|
||||
private static Cookie toServletCookie(ResponseCookie responseCookie) {
|
||||
Cookie cookie = new Cookie(responseCookie.getName(), responseCookie.getValue());
|
||||
String domain = responseCookie.getDomain();
|
||||
if (domain != null) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
cookie.setMaxAge((int) responseCookie.getMaxAge().getSeconds());
|
||||
cookie.setPath(responseCookie.getPath());
|
||||
cookie.setSecure(responseCookie.isSecure());
|
||||
cookie.setHttpOnly(responseCookie.isHttpOnly());
|
||||
return cookie;
|
||||
}
|
||||
|
||||
private static class SessionRepositoryFilterDefaultOrder implements Ordered {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.springframework.session.Session;
|
||||
import org.springframework.web.server.WebSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -69,9 +69,9 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
|
||||
@Test
|
||||
public void constructorWhenNullRepositoryThenThrowsIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new SpringSessionWebSessionStore<S>(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("reactiveSessionRepository cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new SpringSessionWebSessionStore<S>(null))
|
||||
.withMessage("reactiveSessionRepository cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -253,17 +253,6 @@ 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";
|
||||
@@ -286,9 +275,9 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
|
||||
|
||||
@Test
|
||||
public void setClockWhenNullThenException() {
|
||||
assertThatThrownBy(() -> this.webSessionStore.setClock(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("clock cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.webSessionStore.setClock(null))
|
||||
.withMessage("clock cannot be null");
|
||||
}
|
||||
|
||||
@Test // gh-1114
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -56,9 +56,9 @@ public class WebSocketConnectHandlerDecoratorFactoryTests {
|
||||
|
||||
@Test
|
||||
public void constructorNullEventPublisher() {
|
||||
assertThatThrownBy(() -> new WebSocketConnectHandlerDecoratorFactory(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("eventPublisher cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new WebSocketConnectHandlerDecoratorFactory(null))
|
||||
.withMessage("eventPublisher cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
@@ -82,9 +82,9 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
|
||||
@Test
|
||||
public void preSendconstructorNullRepository() {
|
||||
assertThatThrownBy(() -> new SessionRepositoryMessageInterceptor<>(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("sessionRepository cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new SessionRepositoryMessageInterceptor<>(null))
|
||||
.withMessage("sessionRepository cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -134,17 +134,16 @@ public class SessionRepositoryMessageInterceptorTests {
|
||||
|
||||
@Test
|
||||
public void setMatchingMessageTypesNull() {
|
||||
assertThatThrownBy(() -> this.interceptor.setMatchingMessageTypes(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("matchingMessageTypes cannot be null or empty");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.interceptor.setMatchingMessageTypes(null))
|
||||
.withMessage("matchingMessageTypes cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMatchingMessageTypesEmpty() {
|
||||
assertThatThrownBy(
|
||||
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(
|
||||
() -> this.interceptor.setMatchingMessageTypes(Collections.emptySet()))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("matchingMessageTypes cannot be null or empty");
|
||||
.withMessage("matchingMessageTypes cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
*/
|
||||
public abstract class AbstractRedisITests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
private static final String DOCKER_IMAGE = "redis:5.0.3";
|
||||
|
||||
protected static class BaseConfig {
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -28,6 +30,7 @@ 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}.
|
||||
@@ -191,6 +194,31 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
assertThat(this.repository.findById(originalId).block()).isNull();
|
||||
}
|
||||
|
||||
// gh-1111
|
||||
@Test
|
||||
public void changeSessionSaveOldSessionInstance() {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
String sessionId = toSave.getId();
|
||||
|
||||
this.repository.save(toSave).block();
|
||||
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession session = this.repository
|
||||
.findById(sessionId).block();
|
||||
session.changeSessionId();
|
||||
session.setLastAccessedTime(Instant.now());
|
||||
this.repository.save(session).block();
|
||||
|
||||
toSave.setLastAccessedTime(Instant.now());
|
||||
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> this.repository.save(toSave).block())
|
||||
.withMessage("Session was invalidated");
|
||||
|
||||
assertThat(this.repository.findById(sessionId).block()).isNull();
|
||||
assertThat(this.repository.findById(session.getId()).block()).isNotNull();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRedisWebSession
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -190,9 +191,10 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
|
||||
String body = "RedisOperationsSessionRepositoryITests:sessions:expires:"
|
||||
+ toSave.getId();
|
||||
String channel = ":expired";
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
|
||||
body.getBytes("UTF-8"));
|
||||
String channel = "__keyevent@0__:expired";
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] pattern = new byte[] {};
|
||||
this.repository.onMessage(message, pattern);
|
||||
|
||||
@@ -358,9 +360,10 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
|
||||
String body = "RedisOperationsSessionRepositoryITests:sessions:expires:"
|
||||
+ toSave.getId();
|
||||
String channel = ":expired";
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
|
||||
body.getBytes("UTF-8"));
|
||||
String channel = "__keyevent@0__:expired";
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] pattern = new byte[] {};
|
||||
this.repository.onMessage(message, pattern);
|
||||
|
||||
@@ -581,6 +584,22 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
assertThat(this.repository.findById(originalId)).isNull();
|
||||
}
|
||||
|
||||
// gh-1137
|
||||
@Test
|
||||
public void changeSessionIdWhenSessionIsDeleted() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
String sessionId = toSave.getId();
|
||||
this.repository.save(toSave);
|
||||
|
||||
this.repository.deleteById(sessionId);
|
||||
|
||||
toSave.changeSessionId();
|
||||
this.repository.save(toSave);
|
||||
|
||||
assertThat(this.repository.findById(toSave.getId())).isNull();
|
||||
assertThat(this.repository.findById(sessionId)).isNull();
|
||||
}
|
||||
|
||||
private String getSecurityName() {
|
||||
return this.context.getAuthentication().getName();
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITest
|
||||
synchronized (this.lock) {
|
||||
this.lock.wait(TimeUnit.SECONDS.toMillis(1));
|
||||
}
|
||||
return (this.taskDispatched != null ? this.taskDispatched : Boolean.FALSE);
|
||||
return (this.taskDispatched != null) ? this.taskDispatched : Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -118,6 +118,15 @@ 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(() -> {
|
||||
@@ -134,24 +143,38 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(RedisSession session) {
|
||||
return session.saveDelta().and((s) -> {
|
||||
if (session.isNew) {
|
||||
session.setNew(false);
|
||||
}
|
||||
|
||||
s.onComplete();
|
||||
});
|
||||
Mono<Void> result = session.saveChangeSessionId().and(session.saveDelta())
|
||||
.and((s) -> {
|
||||
session.isNew = false;
|
||||
s.onComplete();
|
||||
});
|
||||
if (session.isNew) {
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
String sessionKey = getSessionKey(
|
||||
session.hasChangedSessionId() ? session.originalSessionId
|
||||
: session.getId());
|
||||
return this.sessionRedisOperations.hasKey(sessionKey)
|
||||
.flatMap((exists) -> exists ? result
|
||||
: Mono.error(new IllegalStateException(
|
||||
"Session was invalidated")));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<RedisSession> findById(String id) {
|
||||
String sessionKey = getSessionKey(id);
|
||||
|
||||
// @formatter:off
|
||||
return this.sessionRedisOperations.opsForHash().entries(sessionKey)
|
||||
.collectMap((e) -> e.getKey().toString(), Map.Entry::getValue)
|
||||
.filter((map) -> !map.isEmpty()).map(new SessionMapper(id))
|
||||
.filter((session) -> !session.isExpired()).map(RedisSession::new)
|
||||
.filter((map) -> !map.isEmpty())
|
||||
.map(new SessionMapper(id))
|
||||
.filter((session) -> !session.isExpired())
|
||||
.map(RedisSession::new)
|
||||
.switchIfEmpty(Mono.defer(() -> deleteById(id).then(Mono.empty())));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -276,12 +299,8 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
return this.cached.isExpired();
|
||||
}
|
||||
|
||||
public void setNew(boolean isNew) {
|
||||
this.isNew = isNew;
|
||||
}
|
||||
|
||||
public boolean isNew() {
|
||||
return this.isNew;
|
||||
private boolean hasChangedSessionId() {
|
||||
return !getId().equals(this.originalSessionId);
|
||||
}
|
||||
|
||||
private void flushImmediateIfNecessary() {
|
||||
@@ -296,38 +315,35 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
private Mono<Void> saveDelta() {
|
||||
String sessionId = getId();
|
||||
Mono<Void> changeSessionId = saveChangeSessionId(sessionId);
|
||||
|
||||
if (this.delta.isEmpty()) {
|
||||
return changeSessionId.and(Mono.empty());
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String sessionKey = getSessionKey(sessionId);
|
||||
|
||||
String sessionKey = getSessionKey(getId());
|
||||
Mono<Boolean> update = ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.opsForHash().putAll(sessionKey, this.delta);
|
||||
|
||||
Mono<Boolean> setTtl = ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.expire(sessionKey, getMaxInactiveInterval());
|
||||
|
||||
return changeSessionId.and(update).and(setTtl).and((s) -> {
|
||||
return update.and(setTtl).and((s) -> {
|
||||
this.delta.clear();
|
||||
s.onComplete();
|
||||
}).then();
|
||||
}
|
||||
|
||||
private Mono<Void> saveChangeSessionId(String sessionId) {
|
||||
if (sessionId.equals(this.originalSessionId)) {
|
||||
private Mono<Void> saveChangeSessionId() {
|
||||
if (!hasChangedSessionId()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String sessionId = getId();
|
||||
|
||||
Publisher<Void> replaceSessionId = (s) -> {
|
||||
this.originalSessionId = sessionId;
|
||||
s.onComplete();
|
||||
};
|
||||
|
||||
if (isNew()) {
|
||||
if (this.isNew) {
|
||||
return Mono.from(replaceSessionId);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.core.NestedExceptionUtils;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.core.BoundHashOperations;
|
||||
@@ -200,7 +202,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* <p>
|
||||
* One problem with relying on Redis expiration exclusively is that Redis makes no
|
||||
* guarantee of when the expired event will be fired if they key has not been accessed.
|
||||
* guarantee of when the expired event will be fired if the key has not been accessed.
|
||||
* Specifically the background task that Redis uses to clean up expired keys is a low
|
||||
* priority task and may not trigger the key expiration. For additional details see
|
||||
* <a href="http://redis.io/topics/notifications">Timing of expired events</a> section in
|
||||
@@ -211,7 +213,7 @@ import org.springframework.util.Assert;
|
||||
* To circumvent the fact that expired events are not guaranteed to happen we can ensure
|
||||
* that each key is accessed when it is expected to expire. This means that if the TTL is
|
||||
* expired on the key, Redis will remove the key and fire the expired event when we try to
|
||||
* access they key.
|
||||
* access the key.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
@@ -252,6 +254,11 @@ public class RedisOperationsSessionRepository implements
|
||||
|
||||
static PrincipalNameResolver PRINCIPAL_NAME_RESOLVER = new PrincipalNameResolver();
|
||||
|
||||
/**
|
||||
* The default Redis database used by Spring Session.
|
||||
*/
|
||||
public static final int DEFAULT_DATABASE = 0;
|
||||
|
||||
/**
|
||||
* The default namespace for each key and channel in Redis used by Spring Session.
|
||||
*/
|
||||
@@ -284,11 +291,19 @@ public class RedisOperationsSessionRepository implements
|
||||
*/
|
||||
static final String SESSION_ATTR_PREFIX = "sessionAttr:";
|
||||
|
||||
private int database = RedisOperationsSessionRepository.DEFAULT_DATABASE;
|
||||
|
||||
/**
|
||||
* The namespace for every key used by Spring Session in Redis.
|
||||
*/
|
||||
private String namespace = DEFAULT_NAMESPACE + ":";
|
||||
|
||||
private String sessionCreatedChannelPrefix;
|
||||
|
||||
private String sessionDeletedChannel;
|
||||
|
||||
private String sessionExpiredChannel;
|
||||
|
||||
private final RedisOperations<Object, Object> sessionRedisOperations;
|
||||
|
||||
private final RedisSessionExpirationPolicy expirationPolicy;
|
||||
@@ -325,6 +340,7 @@ public class RedisOperationsSessionRepository implements
|
||||
this.sessionRedisOperations = sessionRedisOperations;
|
||||
this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations,
|
||||
this::getExpirationsKey, this::getSessionKey);
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,6 +391,27 @@ public class RedisOperationsSessionRepository implements
|
||||
this.redisFlushMode = redisFlushMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the database index to use. Defaults to {@link #DEFAULT_DATABASE}.
|
||||
* @param database the database index to use
|
||||
*/
|
||||
public void setDatabase(int database) {
|
||||
this.database = database;
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
private void configureSessionChannels() {
|
||||
this.sessionCreatedChannelPrefix = this.namespace + "event:" + this.database
|
||||
+ ":created:";
|
||||
this.sessionDeletedChannel = "__keyevent@" + this.database + "__:del";
|
||||
this.sessionExpiredChannel = "__keyevent@" + this.database + "__:expired";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RedisOperations} used for sessions.
|
||||
* @return the {@link RedisOperations} used for sessions
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public RedisOperations<Object, Object> getSessionRedisOperations() {
|
||||
return this.sessionRedisOperations;
|
||||
}
|
||||
@@ -495,7 +532,7 @@ public class RedisOperationsSessionRepository implements
|
||||
|
||||
String channel = new String(messageChannel);
|
||||
|
||||
if (channel.startsWith(getSessionCreatedChannelPrefix())) {
|
||||
if (channel.startsWith(this.sessionCreatedChannelPrefix)) {
|
||||
// TODO: is this thread safe?
|
||||
Map<Object, Object> loaded = (Map<Object, Object>) this.defaultSerializer
|
||||
.deserialize(message.getBody());
|
||||
@@ -508,8 +545,8 @@ public class RedisOperationsSessionRepository implements
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isDeleted = channel.endsWith(":del");
|
||||
if (isDeleted || channel.endsWith(":expired")) {
|
||||
boolean isDeleted = channel.equals(this.sessionDeletedChannel);
|
||||
if (isDeleted || channel.equals(this.sessionExpiredChannel)) {
|
||||
int beginIndex = body.lastIndexOf(":") + 1;
|
||||
int endIndex = body.length();
|
||||
String sessionId = body.substring(beginIndex, endIndex);
|
||||
@@ -572,6 +609,7 @@ public class RedisOperationsSessionRepository implements
|
||||
public void setRedisKeyNamespace(String namespace) {
|
||||
Assert.hasText(namespace, "namespace cannot be null or empty");
|
||||
this.namespace = namespace.trim() + ":";
|
||||
configureSessionChannels();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -603,17 +641,33 @@ public class RedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
private String getExpiredKeyPrefix() {
|
||||
return this.namespace + "sessions:" + "expires:";
|
||||
return this.namespace + "sessions:expires:";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the prefix for the channel that SessionCreatedEvent are published to. The
|
||||
* suffix is the session id of the session that was created.
|
||||
*
|
||||
* @return the prefix for the channel that SessionCreatedEvent are published to
|
||||
* Gets the prefix for the channel that {@link SessionCreatedEvent}s are published to.
|
||||
* The suffix is the session id of the session that was created.
|
||||
* @return the prefix for the channel that {@link SessionCreatedEvent}s are published
|
||||
* to
|
||||
*/
|
||||
public String getSessionCreatedChannelPrefix() {
|
||||
return this.namespace + "event:created:";
|
||||
return this.sessionCreatedChannelPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the channel that {@link SessionDeletedEvent}s are published to.
|
||||
* @return the name for the channel that {@link SessionDeletedEvent}s are published to
|
||||
*/
|
||||
public String getSessionDeletedChannel() {
|
||||
return this.sessionDeletedChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the channel that {@link SessionExpiredEvent}s are published to.
|
||||
* @return the name for the channel that {@link SessionExpiredEvent}s are published to
|
||||
*/
|
||||
public String getSessionExpiredChannel() {
|
||||
return this.sessionExpiredChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -797,9 +851,10 @@ public class RedisOperationsSessionRepository implements
|
||||
|
||||
this.delta = new HashMap<>(this.delta.size());
|
||||
|
||||
Long originalExpiration = (this.originalLastAccessTime != null
|
||||
? this.originalLastAccessTime.plus(getMaxInactiveInterval()).toEpochMilli()
|
||||
: null);
|
||||
Long originalExpiration = (this.originalLastAccessTime != null)
|
||||
? this.originalLastAccessTime.plus(getMaxInactiveInterval())
|
||||
.toEpochMilli()
|
||||
: null;
|
||||
RedisOperationsSessionRepository.this.expirationPolicy
|
||||
.onExpirationUpdated(originalExpiration, this);
|
||||
}
|
||||
@@ -813,8 +868,16 @@ public class RedisOperationsSessionRepository implements
|
||||
originalSessionIdKey, sessionIdKey);
|
||||
String originalExpiredKey = getExpiredKey(this.originalSessionId);
|
||||
String expiredKey = getExpiredKey(sessionId);
|
||||
RedisOperationsSessionRepository.this.sessionRedisOperations.rename(
|
||||
originalExpiredKey, expiredKey);
|
||||
try {
|
||||
RedisOperationsSessionRepository.this.sessionRedisOperations.rename(
|
||||
originalExpiredKey, expiredKey);
|
||||
}
|
||||
catch (NonTransientDataAccessException ex) {
|
||||
if (!"ERR no such key".equals(NestedExceptionUtils
|
||||
.getMostSpecificCause(ex).getMessage())) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.originalSessionId = sessionId;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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 {@link RedisOperations} instance used by Spring Session's
|
||||
* {@link RedisOperationsSessionRepository}.
|
||||
* Annotation used to inject the Redis accessor used by Spring Session's Redis session
|
||||
* repository.
|
||||
*
|
||||
* @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,
|
||||
|
||||
@@ -37,7 +37,10 @@ import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.PatternTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
@@ -54,6 +57,7 @@ import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
@@ -115,6 +119,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
sessionRepository.setRedisFlushMode(this.redisFlushMode);
|
||||
int database = resolveDatabase();
|
||||
sessionRepository.setDatabase(database);
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -128,9 +134,9 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
if (this.redisSubscriptionExecutor != null) {
|
||||
container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
|
||||
}
|
||||
container.addMessageListener(sessionRepository(),
|
||||
Arrays.asList(new PatternTopic("__keyevent@*:del"),
|
||||
new PatternTopic("__keyevent@*:expired")));
|
||||
container.addMessageListener(sessionRepository(), Arrays.asList(
|
||||
new ChannelTopic(sessionRepository().getSessionDeletedChannel()),
|
||||
new ChannelTopic(sessionRepository().getSessionExpiredChannel())));
|
||||
container.addMessageListener(sessionRepository(),
|
||||
Collections.singletonList(new PatternTopic(
|
||||
sessionRepository().getSessionCreatedChannelPrefix() + "*")));
|
||||
@@ -256,6 +262,18 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
private int resolveDatabase() {
|
||||
if (ClassUtils.isPresent("io.lettuce.core.RedisClient", null)
|
||||
&& this.redisConnectionFactory instanceof LettuceConnectionFactory) {
|
||||
return ((LettuceConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||
}
|
||||
if (ClassUtils.isPresent("redis.clients.jedis.Jedis", null)
|
||||
&& this.redisConnectionFactory instanceof JedisConnectionFactory) {
|
||||
return ((JedisConnectionFactory) this.redisConnectionFactory).getDatabase();
|
||||
}
|
||||
return RedisOperationsSessionRepository.DEFAULT_DATABASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that Redis is configured to send keyspace notifications. This is important
|
||||
* to ensure that expiration and deletion of sessions trigger SessionDestroyedEvents.
|
||||
|
||||
@@ -21,6 +21,7 @@ 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;
|
||||
@@ -64,6 +65,8 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
private ReactiveRedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private RedisSerializer<Object> defaultRedisSerializer;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
@@ -107,6 +110,13 @@ 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;
|
||||
@@ -134,10 +144,11 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
private ReactiveRedisTemplate<String, Object> createReactiveRedisTemplate() {
|
||||
RedisSerializer<String> keySerializer = new StringRedisSerializer();
|
||||
RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer(
|
||||
this.classLoader);
|
||||
RedisSerializer<Object> defaultSerializer = (this.defaultRedisSerializer != null)
|
||||
? this.defaultRedisSerializer
|
||||
: new JdkSerializationRedisSerializer(this.classLoader);
|
||||
RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext
|
||||
.<String, Object>newSerializationContext(valueSerializer)
|
||||
.<String, Object>newSerializationContext(defaultSerializer)
|
||||
.key(keySerializer).hashKey(keySerializer).build();
|
||||
return new ReactiveRedisTemplate<>(this.redisConnectionFactory,
|
||||
serializationContext);
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepo
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -80,9 +80,9 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void constructorWithNullReactiveRedisOperations() {
|
||||
assertThatThrownBy(() -> new ReactiveRedisOperationsSessionRepository(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("sessionRedisOperations cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new ReactiveRedisOperationsSessionRepository(null))
|
||||
.withMessageContaining("sessionRedisOperations cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -95,16 +95,16 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void nullRedisKeyNamespace() {
|
||||
assertThatThrownBy(() -> this.repository.setRedisKeyNamespace(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("namespace cannot be null or empty");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.repository.setRedisKeyNamespace(null))
|
||||
.withMessage("namespace cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyRedisKeyNamespace() {
|
||||
assertThatThrownBy(() -> this.repository.setRedisKeyNamespace(""))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("namespace cannot be null or empty");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.repository.setRedisKeyNamespace(""))
|
||||
.withMessage("namespace cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -125,9 +125,9 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void nullRedisFlushMode() {
|
||||
assertThatThrownBy(() -> this.repository.setRedisFlushMode(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("redisFlushMode cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.repository.setRedisFlushMode(null))
|
||||
.withMessage("redisFlushMode cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -183,6 +183,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void saveSessionNothingChanged() {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any()))
|
||||
.willReturn(Mono.just(true));
|
||||
|
||||
@@ -191,12 +192,14 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
StepVerifier.create(this.repository.save(session)).verifyComplete();
|
||||
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verifyZeroInteractions(this.redisOperations);
|
||||
verifyZeroInteractions(this.hashOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveLastAccessChanged() {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any()))
|
||||
@@ -206,6 +209,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
session.setLastAccessedTime(Instant.ofEpochMilli(12345678L));
|
||||
Mono.just(session).subscribe(this.repository::save);
|
||||
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
@@ -219,6 +223,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void saveSetAttribute() {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any()))
|
||||
@@ -229,6 +234,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
session.setAttribute(attrName, "attrValue");
|
||||
Mono.just(session).subscribe(this.repository::save);
|
||||
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
@@ -242,6 +248,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void saveRemoveAttribute() {
|
||||
given(this.redisOperations.hasKey(anyString())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.opsForHash()).willReturn(this.hashOperations);
|
||||
given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true));
|
||||
given(this.redisOperations.expire(anyString(), any()))
|
||||
@@ -252,6 +259,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
session.removeAttribute(attrName);
|
||||
Mono.just(session).subscribe(this.repository::save);
|
||||
|
||||
verify(this.redisOperations).hasKey(anyString());
|
||||
verify(this.redisOperations).opsForHash();
|
||||
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
|
||||
verify(this.redisOperations).expire(anyString(), any());
|
||||
@@ -338,12 +346,16 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
.isEqualTo(expected.getAttribute(attribute1));
|
||||
assertThat(session.<String>getAttribute(attribute2))
|
||||
.isEqualTo(expected.getAttribute(attribute2));
|
||||
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getCreationTime()
|
||||
.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expected.getMaxInactiveInterval());
|
||||
assertThat(session.getLastAccessedTime())
|
||||
.isEqualTo(expected.getLastAccessedTime());
|
||||
}).verifyComplete();
|
||||
assertThat(
|
||||
session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getLastAccessedTime()
|
||||
.truncatedTo(ChronoUnit.MILLIS));
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.session.data.redis;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@@ -58,7 +59,7 @@ import org.springframework.session.data.redis.RedisOperationsSessionRepository.R
|
||||
import org.springframework.session.events.AbstractSessionEvent;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
@@ -114,9 +115,9 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void setApplicationEventPublisherNull() {
|
||||
assertThatThrownBy(() -> this.redisRepository.setApplicationEventPublisher(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("applicationEventPublisher cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.redisRepository.setApplicationEventPublisher(null))
|
||||
.withMessage("applicationEventPublisher cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -431,12 +432,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
.isEqualTo(expected.getAttribute(attribute1));
|
||||
assertThat(session.<String>getAttribute(attribute2))
|
||||
.isEqualTo(expected.getAttribute(attribute2));
|
||||
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getCreationTime().truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expected.getMaxInactiveInterval());
|
||||
assertThat(session.getLastAccessedTime())
|
||||
.isEqualTo(expected.getLastAccessedTime());
|
||||
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(expected.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -497,9 +498,11 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
RedisSession session = sessionIdToSessions.get(sessionId);
|
||||
assertThat(session).isNotNull();
|
||||
assertThat(session.getId()).isEqualTo(sessionId);
|
||||
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessed);
|
||||
assertThat(session.getLastAccessedTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(lastAccessed.truncatedTo(ChronoUnit.MILLIS));
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(maxInactive);
|
||||
assertThat(session.getCreationTime()).isEqualTo(createdTime);
|
||||
assertThat(session.getCreationTime().truncatedTo(ChronoUnit.MILLIS))
|
||||
.isEqualTo(createdTime.truncatedTo(ChronoUnit.MILLIS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -522,14 +525,15 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageCreated() throws Exception {
|
||||
public void onMessageCreated() {
|
||||
MapSession session = this.cached;
|
||||
byte[] pattern = "".getBytes("UTF-8");
|
||||
String channel = "spring:session:event:created:" + session.getId();
|
||||
byte[] pattern = "".getBytes(StandardCharsets.UTF_8);
|
||||
String channel = "spring:session:event:0:created:" + session.getId();
|
||||
JdkSerializationRedisSerializer defaultSerailizer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setDefaultSerializer(defaultSerailizer);
|
||||
byte[] body = defaultSerailizer.serialize(new HashMap());
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
|
||||
@@ -539,16 +543,16 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
assertThat(this.event.getValue().getSessionId()).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
// gh-309
|
||||
@Test
|
||||
public void onMessageCreatedCustomSerializer() throws Exception {
|
||||
@Test // gh-309
|
||||
public void onMessageCreatedCustomSerializer() {
|
||||
MapSession session = this.cached;
|
||||
byte[] pattern = "".getBytes("UTF-8");
|
||||
byte[] pattern = "".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] body = new byte[0];
|
||||
String channel = "spring:session:event:created:" + session.getId();
|
||||
String channel = "spring:session:event:0:created:" + session.getId();
|
||||
given(this.defaultSerializer.deserialize(body))
|
||||
.willReturn(new HashMap<String, Object>());
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
|
||||
this.redisRepository.onMessage(message, pattern);
|
||||
@@ -559,7 +563,7 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageDeletedSessionFound() throws Exception {
|
||||
public void onMessageDeletedSessionFound() {
|
||||
String deletedId = "deleted-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(deletedId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
@@ -570,10 +574,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
String channel = "__keyevent@0__:del";
|
||||
String body = "spring:session:sessions:expires:" + deletedId;
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verify(this.redisOperations).boundHashOps(eq(getKey(deletedId)));
|
||||
verify(this.boundHashOperations).entries();
|
||||
@@ -586,7 +592,7 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageDeletedSessionNotFound() throws Exception {
|
||||
public void onMessageDeletedSessionNotFound() {
|
||||
String deletedId = "deleted-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(deletedId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
@@ -594,10 +600,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
String channel = "__keyevent@0__:del";
|
||||
String body = "spring:session:sessions:expires:" + deletedId;
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verify(this.redisOperations).boundHashOps(eq(getKey(deletedId)));
|
||||
verify(this.boundHashOperations).entries();
|
||||
@@ -608,7 +616,7 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageExpiredSessionFound() throws Exception {
|
||||
public void onMessageExpiredSessionFound() {
|
||||
String expiredId = "expired-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(expiredId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
@@ -619,10 +627,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
String channel = "__keyevent@0__:expired";
|
||||
String body = "spring:session:sessions:expires:" + expiredId;
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verify(this.redisOperations).boundHashOps(eq(getKey(expiredId)));
|
||||
verify(this.boundHashOperations).entries();
|
||||
@@ -635,7 +645,7 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageExpiredSessionNotFound() throws Exception {
|
||||
public void onMessageExpiredSessionNotFound() {
|
||||
String expiredId = "expired-id";
|
||||
given(this.redisOperations.boundHashOps(getKey(expiredId)))
|
||||
.willReturn(this.boundHashOperations);
|
||||
@@ -643,10 +653,12 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
String channel = "__keyevent@0__:expired";
|
||||
String body = "spring:session:sessions:expires:" + expiredId;
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.onMessage(message, "".getBytes("UTF-8"));
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
verify(this.redisOperations).boundHashOps(eq(getKey(expiredId)));
|
||||
verify(this.boundHashOperations).entries();
|
||||
@@ -829,9 +841,9 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void setRedisFlushModeNull() {
|
||||
assertThatThrownBy(() -> this.redisRepository.setRedisFlushMode(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("redisFlushMode cannot be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.redisRepository.setRedisFlushMode(null))
|
||||
.withMessage("redisFlushMode cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -856,16 +868,16 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void setRedisKeyNamespaceNullNamespace() {
|
||||
assertThatThrownBy(() -> this.redisRepository.setRedisKeyNamespace(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("namespace cannot be null or empty");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.redisRepository.setRedisKeyNamespace(null))
|
||||
.withMessage("namespace cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRedisKeyNamespaceEmptyNamespace() {
|
||||
assertThatThrownBy(() -> this.redisRepository.setRedisKeyNamespace(" "))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("namespace cannot be null or empty");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> this.redisRepository.setRedisKeyNamespace(" "))
|
||||
.withMessage("namespace cannot be null or empty");
|
||||
}
|
||||
|
||||
@Test // gh-1120
|
||||
@@ -881,6 +893,62 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
assertThat(session.getAttributeNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageCreatedInOtherDatabase() {
|
||||
JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.setDefaultSerializer(serializer);
|
||||
|
||||
MapSession session = this.cached;
|
||||
String channel = "spring:session:event:created:1:" + session.getId();
|
||||
byte[] body = serializer.serialize(new HashMap());
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8), body);
|
||||
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(this.event.getAllValues()).isEmpty();
|
||||
verifyZeroInteractions(this.publisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageDeletedInOtherDatabase() {
|
||||
JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.setDefaultSerializer(serializer);
|
||||
|
||||
MapSession session = this.cached;
|
||||
String channel = "__keyevent@1__:del";
|
||||
String body = "spring:session:sessions:expires:" + session.getId();
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(this.event.getAllValues()).isEmpty();
|
||||
verifyZeroInteractions(this.publisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onMessageExpiredInOtherDatabase() {
|
||||
JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
|
||||
this.redisRepository.setApplicationEventPublisher(this.publisher);
|
||||
this.redisRepository.setDefaultSerializer(serializer);
|
||||
|
||||
MapSession session = this.cached;
|
||||
String channel = "__keyevent@1__:expired";
|
||||
String body = "spring:session:sessions:expires:" + session.getId();
|
||||
DefaultMessage message = new DefaultMessage(
|
||||
channel.getBytes(StandardCharsets.UTF_8),
|
||||
body.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
this.redisRepository.onMessage(message, "".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
assertThat(this.event.getAllValues()).isEmpty();
|
||||
verifyZeroInteractions(this.publisher);
|
||||
}
|
||||
|
||||
private String getKey(String id) {
|
||||
return "spring:session:sessions:" + id;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.springframework.session.data.redis.config.annotation.SpringSessionRed
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -184,10 +184,10 @@ public class RedisHttpSessionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void multipleConnectionFactoryRedisConfig() {
|
||||
assertThatThrownBy(() -> registerAndRefresh(RedisConfig.class,
|
||||
MultipleConnectionFactoryRedisConfig.class))
|
||||
.isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("expected single matching bean but found 2");
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> registerAndRefresh(RedisConfig.class,
|
||||
MultipleConnectionFactoryRedisConfig.class))
|
||||
.withMessageContaining("expected single matching bean but found 2");
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
|
||||
@@ -27,13 +27,16 @@ 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;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
@@ -70,6 +73,22 @@ 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);
|
||||
@@ -175,10 +194,40 @@ public class RedisWebSessionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void multipleConnectionFactoryRedisConfig() {
|
||||
assertThatThrownBy(() -> registerAndRefresh(RedisConfig.class,
|
||||
MultipleConnectionFactoryRedisConfig.class))
|
||||
.isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("expected single matching bean but found 2");
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> registerAndRefresh(RedisConfig.class,
|
||||
MultipleConnectionFactoryRedisConfig.class))
|
||||
.withMessageContaining("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) {
|
||||
@@ -201,6 +250,18 @@ 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 {
|
||||
|
||||
@@ -275,4 +336,15 @@ public class RedisWebSessionConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableRedisWebSession
|
||||
static class CustomRedisSerializerConfig {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("unchecked")
|
||||
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
|
||||
return mock(RedisSerializer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ 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"
|
||||
|
||||
@@ -224,7 +224,7 @@ public abstract class AbstractHazelcastRepositoryITests {
|
||||
|
||||
assertThat(this.repository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username))
|
||||
.isNotNull();
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.3")
|
||||
"hazelcast/hazelcast:3.11.1")
|
||||
.withExposedPorts(5701)
|
||||
.withEnv("JAVA_OPTS",
|
||||
"-Dhazelcast.config=/opt/hazelcast/config_ext/hazelcast.xml")
|
||||
|
||||
@@ -122,7 +122,7 @@ public class EnableHazelcastHttpSessionEventsTests<S extends Session> {
|
||||
assertThat(this.registry.<SessionExpiredEvent>getEvent(sessionToSave.getId()))
|
||||
.isInstanceOf(SessionExpiredEvent.class);
|
||||
|
||||
assertThat(this.repository.<Session>findById(sessionToSave.getId())).isNull();
|
||||
assertThat(this.repository.findById(sessionToSave.getId())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -147,24 +147,18 @@ public class EnableHazelcastHttpSessionEventsTests<S extends Session> {
|
||||
|
||||
@Test
|
||||
public void saveUpdatesTimeToLiveTest() throws InterruptedException {
|
||||
Object lock = new Object();
|
||||
|
||||
S sessionToSave = this.repository.createSession();
|
||||
|
||||
sessionToSave.setMaxInactiveInterval(Duration.ofSeconds(3));
|
||||
this.repository.save(sessionToSave);
|
||||
|
||||
synchronized (lock) {
|
||||
lock.wait(sessionToSave.getMaxInactiveInterval().minusMillis(500).toMillis());
|
||||
}
|
||||
Thread.sleep(2000);
|
||||
|
||||
// Get and save the session like SessionRepositoryFilter would.
|
||||
S sessionToUpdate = this.repository.findById(sessionToSave.getId());
|
||||
sessionToUpdate.setLastAccessedTime(Instant.now());
|
||||
this.repository.save(sessionToUpdate);
|
||||
|
||||
synchronized (lock) {
|
||||
lock.wait(sessionToUpdate.getMaxInactiveInterval().minusMillis(100).toMillis());
|
||||
}
|
||||
Thread.sleep(2000);
|
||||
|
||||
assertThat(this.repository.findById(sessionToUpdate.getId())).isNotNull();
|
||||
}
|
||||
@@ -200,6 +194,7 @@ public class EnableHazelcastHttpSessionEventsTests<S extends Session> {
|
||||
public SessionEventRegistry sessionEventRegistry() {
|
||||
return new SessionEventRegistry();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.11.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.11.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.11.xsd">
|
||||
|
||||
<group>
|
||||
<name>spring-session-it-test-map-name</name>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ryuk.container.timeout=120
|
||||
@@ -20,6 +20,7 @@ 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;
|
||||
|
||||
@@ -29,10 +30,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> {
|
||||
public class SessionUpdateEntryProcessor
|
||||
extends AbstractEntryProcessor<String, MapSession> implements Offloadable {
|
||||
|
||||
private Instant lastAccessedTime;
|
||||
|
||||
@@ -66,6 +68,11 @@ class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSess
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExecutorName() {
|
||||
return OFFLOADABLE_EXECUTOR;
|
||||
}
|
||||
|
||||
void setLastAccessedTime(Instant lastAccessedTime) {
|
||||
this.lastAccessedTime = lastAccessedTime;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.springframework.session.MapSession;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository.HazelcastSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
@@ -78,9 +78,9 @@ public class HazelcastSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void constructorNullHazelcastInstance() {
|
||||
assertThatThrownBy(() -> new HazelcastSessionRepository(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("HazelcastInstance must not be null");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new HazelcastSessionRepository(null))
|
||||
.withMessage("HazelcastInstance must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.session.hazelcast.config.annotation.SpringSessionHaze
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -62,10 +62,10 @@ public class HazelcastHttpSessionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void noHazelcastInstanceConfiguration() {
|
||||
assertThatThrownBy(
|
||||
() -> registerAndRefresh(NoHazelcastInstanceConfiguration.class))
|
||||
.isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("HazelcastInstance");
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(
|
||||
() -> registerAndRefresh(NoHazelcastInstanceConfiguration.class))
|
||||
.withMessageContaining("HazelcastInstance");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -206,10 +206,9 @@ public class HazelcastHttpSessionConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void multipleHazelcastInstanceConfiguration() {
|
||||
assertThatThrownBy(
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
|
||||
() -> registerAndRefresh(MultipleHazelcastInstanceConfiguration.class))
|
||||
.isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("expected single matching bean but found 2");
|
||||
.withMessageContaining("expected single matching bean but found 2");
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user