Compare commits
213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
546febd959 | ||
|
|
ea857025bc | ||
|
|
782167b306 | ||
|
|
17005c51a7 | ||
|
|
54859070f3 | ||
|
|
3d03c02924 | ||
|
|
4f9d55feec | ||
|
|
7e7d08a99f | ||
|
|
f6a5bdacb8 | ||
|
|
79de4f9a0d | ||
|
|
9a379fd5bc | ||
|
|
be58c00838 | ||
|
|
3475043bf0 | ||
|
|
c6470c6f48 | ||
|
|
6faa67a64e | ||
|
|
340b614860 | ||
|
|
79b092d8f0 | ||
|
|
2e91024a56 | ||
|
|
e359468abc | ||
|
|
4ec6a9a08b | ||
|
|
084d1c7286 | ||
|
|
a4ff3682f6 | ||
|
|
35f09d0da7 | ||
|
|
566b388b2f | ||
|
|
78b72f2d1b | ||
|
|
52f59a83e4 | ||
|
|
6809168541 | ||
|
|
02bb998f97 | ||
|
|
57ffb90a0c | ||
|
|
402272d5aa | ||
|
|
1dbaffef5e | ||
|
|
8ae0e0b314 | ||
|
|
d94d58d96b | ||
|
|
cc41ea5271 | ||
|
|
e6a88ccf4c | ||
|
|
36f1cd5302 | ||
|
|
3bd892ec16 | ||
|
|
dcd5342204 | ||
|
|
48c32228de | ||
|
|
ec8ef595a2 | ||
|
|
b98c5216b4 | ||
|
|
eb0f89a18a | ||
|
|
83e0f4f24a | ||
|
|
397d4e5ca1 | ||
|
|
f7b7737896 | ||
|
|
2a5b221d70 | ||
|
|
5a462b3594 | ||
|
|
c252c00a85 | ||
|
|
2a7b6ec4f7 | ||
|
|
044e5bbe3c | ||
|
|
f247a78c32 | ||
|
|
c0f708d0b3 | ||
|
|
c2a3ffdb00 | ||
|
|
856a9b2c51 | ||
|
|
0d24033749 | ||
|
|
7a5f9bc29e | ||
|
|
094ffe8a60 | ||
|
|
55102aa8bb | ||
|
|
f2e9634789 | ||
|
|
7dccade893 | ||
|
|
39408ff42a | ||
|
|
a5a3bc5d0b | ||
|
|
90c08340fa | ||
|
|
13208dd3b5 | ||
|
|
0975d4d47e | ||
|
|
0c0bfa4414 | ||
|
|
8b40e8cce8 | ||
|
|
408f2da108 | ||
|
|
3b7bb7ec12 | ||
|
|
4c849caccd | ||
|
|
ea84b8bd22 | ||
|
|
0c9fbedd05 | ||
|
|
08495b1321 | ||
|
|
60cd6d8be6 | ||
|
|
5503057523 | ||
|
|
8fc2f1130a | ||
|
|
f5d63efcf1 | ||
|
|
ed76514f30 | ||
|
|
e1c0d62d33 | ||
|
|
8041358d18 | ||
|
|
cdd85cb349 | ||
|
|
381a07cb8c | ||
|
|
0e89539e20 | ||
|
|
f00a1b9c6f | ||
|
|
39cecb0902 | ||
|
|
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 |
@@ -40,5 +40,5 @@ appropriate to the circumstances. Maintainers are obligated to maintain confiden
|
||||
with regard to the reporter of an incident.
|
||||
|
||||
This Code of Conduct is adapted from the
|
||||
http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
|
||||
http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
|
||||
https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
|
||||
https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
|
||||
|
||||
@@ -10,8 +10,8 @@ By participating, you are expected to uphold this code. Please report unaccepta
|
||||
== Using GitHub issues
|
||||
|
||||
We use GitHub issues to track bugs and enhancements. If you have a general usage question
|
||||
please ask on http://stackoverflow.com[Stack Overflow]. The Spring Session team and the
|
||||
broader community monitor the http://stackoverflow.com/tags/spring-session[`spring-session`]
|
||||
please ask on https://stackoverflow.com[Stack Overflow]. The Spring Session team and the
|
||||
broader community monitor the https://stackoverflow.com/tags/spring-session[`spring-session`]
|
||||
tag.
|
||||
|
||||
If you are reporting a bug, please help to speed up problem diagnosis by providing as much
|
||||
|
||||
172
Jenkinsfile
vendored
172
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,67 +11,167 @@ 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('linux') {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk8'}"]) {
|
||||
sh './gradlew clean check --no-daemon --refresh-dependencies --stacktrace'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: check'
|
||||
throw e
|
||||
}
|
||||
finally {
|
||||
junit '**/build/test-results/*/*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk9: {
|
||||
stage('JDK 9') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('linux') {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk9'}"]) {
|
||||
sh './gradlew clean test --no-daemon --refresh-dependencies --stacktrace'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk9'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk10: {
|
||||
stage('JDK 10') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('linux') {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk10'}"]) {
|
||||
sh './gradlew clean test --no-daemon --refresh-dependencies --stacktrace'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk10'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk11: {
|
||||
stage('JDK 11') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('linux') {
|
||||
checkout scm
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk11'}"]) {
|
||||
sh './gradlew clean test integrationTest --no-daemon --refresh-dependencies --stacktrace'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk11'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
jdk12: {
|
||||
stage('JDK 12') {
|
||||
timeout(time: 45, unit: 'MINUTES') {
|
||||
node('linux') {
|
||||
checkout scm
|
||||
try {
|
||||
withEnv(["JAVA_HOME=${tool 'openjdk12'}"]) {
|
||||
sh './gradlew clean test integrationTest --no-daemon --refresh-dependencies --stacktrace'
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
currentBuild.result = 'FAILED: jdk12'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(currentBuild.result == 'SUCCESS') {
|
||||
if (currentBuild.result == 'SUCCESS') {
|
||||
parallel artifacts: {
|
||||
stage('Deploy Artifacts') {
|
||||
node {
|
||||
node('linux') {
|
||||
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"
|
||||
sh "git clean -dfx"
|
||||
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')]) {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk8'}"]) {
|
||||
sh './gradlew deployArtifacts finalizeDeployArtifacts --no-daemon --refresh-dependencies --stacktrace -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
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
docs: {
|
||||
stage('Deploy Docs') {
|
||||
node {
|
||||
node('linux') {
|
||||
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"
|
||||
sh "git clean -dfx"
|
||||
try {
|
||||
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk8'}"]) {
|
||||
sh './gradlew deployDocs --no-daemon --refresh-dependencies --stacktrace -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
@@ -193,7 +193,7 @@
|
||||
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
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -25,8 +25,8 @@ By participating, you are expected to uphold this code. Please report unaccepta
|
||||
|
||||
== Spring Session Project Site
|
||||
|
||||
You can find the documentation, issue management, support, samples, and guides for using Spring Session at http://projects.spring.io/spring-session/
|
||||
You can find the documentation, issue management, support, samples, and guides for using Spring Session at https://projects.spring.io/spring-session/
|
||||
|
||||
== License
|
||||
|
||||
Spring Session is Open Source software released under the http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
|
||||
Spring Session is Open Source software released under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
|
||||
|
||||
38
build.gradle
38
build.gradle
@@ -1,20 +1,40 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.17.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
ext {
|
||||
releaseBuild = version.endsWith('RELEASE')
|
||||
snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
springBootVersion = '2.2.0.M2'
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url 'https://repo.spring.io/plugins-release' }
|
||||
gradlePluginPortal()
|
||||
maven { url 'https://repo.spring.io/plugins-release/' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.25.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.2.RELEASE'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
apply plugin: 'io.spring.nohttp'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
group = 'org.springframework.session'
|
||||
description = 'Spring Session'
|
||||
|
||||
subprojects {
|
||||
plugins.withType(JavaPlugin) {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
ext.releaseBuild = version.endsWith('RELEASE')
|
||||
ext.snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
ext.milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
|
||||
tasks.withType(Test) {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
= Spring Session - find by username
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to find sessions by username.
|
||||
|
||||
NOTE: The completed guide can be found in the <<findbyusername-sample, findbyusername application>>.
|
||||
|
||||
|
||||
[[findbyusername-assumptions]]
|
||||
== Assumptions
|
||||
|
||||
The guide assumes you have already added Spring Session using the built in Redis configuration support to your application.
|
||||
The guide also assumes you have already applied Spring Security to your application.
|
||||
However, we the guide will be somewhat general purpose and can be applied to any technology with minimal changes we will discuss.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you need to learn how to add Spring Session to your project, please refer to the listing of link:../#samples[samples and guides]
|
||||
====
|
||||
|
||||
== About the Sample
|
||||
|
||||
Our sample is using this feature to invalidate the users session that might have been compromised.
|
||||
Consider the following scenario:
|
||||
|
||||
* User goes to library and authenticates to the application
|
||||
* User goes home and realizes they forgot to log out
|
||||
* User can log in and terminate the session from the library using clues like the location, created time, last accessed time, etc.
|
||||
|
||||
Wouldn't it be nice if we could allow the user to invalidate the session at the library from any device they authenticate with?
|
||||
This sample demonstrates how this is possible.
|
||||
|
||||
[[findbyindexnamesessionrepository]]
|
||||
== FindByIndexNameSessionRepository
|
||||
|
||||
In order to look up a user by their username, you must first choose a `SessionRepository` that implements link:../#api-findbyindexnamesessionrepository[FindByIndexNameSessionRepository].
|
||||
Our sample application assumes that the Redis support is already setup, so we are ready to go.
|
||||
|
||||
== Mapping the username
|
||||
|
||||
`FindByIndexNameSessionRepository` can only find a session by the username, if the developer instructs Spring Session what user is associated with the `Session`.
|
||||
This is done by ensuring that the session attribute with the name `FindByUsernameSessionRepository.PRINCIPAL_NAME_INDEX_NAME` is populated with the username.
|
||||
|
||||
Generally, speaking this can be done with the following code immediately after the user authenticates:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{docs-test-dir}docs/FindByIndexNameSessionRepositoryTests.java[tags=set-username]
|
||||
----
|
||||
|
||||
== Mapping the username with Spring Security
|
||||
|
||||
Since we are using Spring Security, the user name is automatically indexed for us.
|
||||
This means we will not have to perform any steps to ensure the user name is indexed.
|
||||
|
||||
== Adding Additional Data to Session
|
||||
|
||||
It may be nice to associate additional information (i.e. IP Address, the browser, location, etc) to the session.
|
||||
This makes it easier for the user to know which session they are looking at.
|
||||
|
||||
To do this simply determine which session attribute you want to use and what information you wish to provide.
|
||||
Then create a Java bean that is added as a session attribute.
|
||||
For example, our sample application includes the location and access type of the session
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}boot/findbyusername/src/main/java/sample/session/SessionDetails.java[tags=class]
|
||||
----
|
||||
|
||||
We then inject that information into the session on each HTTP request using a `SessionDetailsFilter`.
|
||||
For example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}boot/findbyusername/src/main/java/sample/session/SessionDetailsFilter.java[tags=dofilterinternal]
|
||||
----
|
||||
|
||||
We obtain the information we want and then set the `SessionDetails` as an attribute in the `Session`.
|
||||
When we retrieve the `Session` by username, we can then use the session to access our `SessionDetails` just like any other session attribute.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
You might be wondering at this point why Spring Session does not provide `SessionDetails` functionality out of the box.
|
||||
The reason, is twofold.
|
||||
The first is that it is very trivial for applications to implement this themselves.
|
||||
The second reason is that the information that is populated in the session (and how frequently that information is updated) is highly application dependent.
|
||||
====
|
||||
|
||||
== Finding sessions for a specific user
|
||||
|
||||
We can now find all the sessions for a specific user.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}boot/findbyusername/src/main/java/sample/mvc/IndexController.java[tags=findbyusername]
|
||||
----
|
||||
|
||||
In our instance, we find all sessions for the currently logged in user.
|
||||
However, this could easily be modified for an administrator to use a form to specify which user to look up.
|
||||
|
||||
[[findbyusername-sample]]
|
||||
== findbyusername Sample Application
|
||||
|
||||
=== Running the findbyusername Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `RedisConnectionFactory` to point to a Redis server.
|
||||
Another option is to use https://www.docker.com/[Docker] to run Redis on localhost. See https://hub.docker.com/_/redis/[Docker Redis repository] for detailed instructions.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :spring-session-sample-boot-findbyusername:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
You should also see a listing of active sessions for the currently logged in user.
|
||||
|
||||
Let's emulate the flow we discussed in the <<About the Sample>> section
|
||||
|
||||
* Open a new incognito window and navigate to http://localhost:8080/
|
||||
* Enter the following to log in:
|
||||
** **Username** _user_
|
||||
** **Password** _password_
|
||||
* Terminate your original session
|
||||
* Refresh the original window and see you are logged out
|
||||
@@ -1,133 +0,0 @@
|
||||
= Spring Session - Grails
|
||||
Eric Helgeson
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Grails 3.1
|
||||
|
||||
NOTE: Grails 3.1 is based off spring boot 1.3 so much of the advanced configuration and options can be found in the boot docs as well.
|
||||
|
||||
NOTE: The completed guide can be found in the <<grails3-sample, Grails 3 sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
We assume you are working with a working Grails 3.1 web profile.
|
||||
Add the following dependencies:
|
||||
|
||||
.build.gradle
|
||||
[source,groovy]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compile 'org.springframework.boot:spring-boot-starter-redis'
|
||||
compile 'org.springframework.session:spring-session:{spring-session-version}'
|
||||
}
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.build.gradle
|
||||
[source,groovy]
|
||||
----
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://repo.spring.io/libs-snapshot'
|
||||
}
|
||||
}
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.build.gradle
|
||||
[source,groovy]
|
||||
----
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://repo.spring.io/libs-milestone'
|
||||
}
|
||||
}
|
||||
----
|
||||
endif::[]
|
||||
|
||||
[[grails3-redis-configuration]]
|
||||
== Configuring the Redis Connection
|
||||
|
||||
Spring Boot automatically creates a `RedisConnectionFactory` that connects Spring Session to a Redis Server on localhost on port 6379 (default port).
|
||||
In a production environment you need to ensure to update your configuration to point to your Redis server.
|
||||
For example, you can include the following in your *application.yml*
|
||||
|
||||
.grails-app/conf/application.yml
|
||||
[source,yml]
|
||||
----
|
||||
spring:
|
||||
redis:
|
||||
host: localhost
|
||||
password: secret
|
||||
port: 6397
|
||||
----
|
||||
|
||||
For more information, refer to https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-connecting-to-redis[Connecting to Redis] portion of the Spring Boot documentation.
|
||||
|
||||
[[grails3-sample]]
|
||||
== Grails 3 Sample Application
|
||||
|
||||
The Grails 3 Sample Application demonstrates how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using Grails.
|
||||
|
||||
[[grails3-running]]
|
||||
=== Running the Grails 3 Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `RedisConnectionFactory` to point to a Redis server.
|
||||
Another option is to use https://www.docker.com/[Docker] to run Redis on localhost. See https://hub.docker.com/_/redis/[Docker Redis repository] for detailed instructions.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :spring-session-sample-misc-grails3:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/test/index
|
||||
|
||||
[[grails3-explore]]
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
The user's information is stored in Redis rather than Tomcat's `HttpSession` implementation.
|
||||
|
||||
[[grails3-how]]
|
||||
=== How does it work?
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||
Spring Session replaces the `HttpSession` with an implementation that is backed by Redis.
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Redis.
|
||||
|
||||
When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developers.google.com/web/tools/chrome-devtools/manage-data/cookies[Chrome] or https://developer.mozilla.org/en-US/docs/Tools/Storage_Inspector[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using redis-cli. For example, on a Linux based system you can type:
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for https://redis.io/topics/quickstart[installing redis-cli].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
Now visit the application at http://localhost:8080/test/index and observe that we are no longer authenticated.
|
||||
|
||||
NOTE: Spring Session will not work with grails flash scope without additional work. +
|
||||
See this answer for an explanation: https://stackoverflow.com/a/43311427
|
||||
@@ -1,102 +0,0 @@
|
||||
= Spring Session - Custom Cookie
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to configure Spring Session to use custom cookies with Java Configuration.
|
||||
The guide assumes you have already link:./httpsession.html[setup Spring Session in your project].
|
||||
|
||||
NOTE: The completed guide can be found in the <<custom-cookie-sample, Custom Cookie sample application>>.
|
||||
|
||||
[[custom-cookie-spring-configuration]]
|
||||
== Spring Java Configuration
|
||||
|
||||
Once you have setup Spring Session you can easily customize how the session cookie is written by exposing a `CookieSerializer` as a Spring Bean.
|
||||
Out of the box, Spring Session comes with `DefaultCookieSerializer`.
|
||||
Simply exposing the `DefaultCookieSerializer` as a Spring Bean will augment the existing configuration when using configurations like `@EnableRedisHttpSession`.
|
||||
You can find an example of customizing Spring Session's cookie below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}javaconfig/custom-cookie/src/main/java/sample/Config.java[tags=cookie-serializer]
|
||||
----
|
||||
|
||||
<1> We customize the name of the cookie to be JSESSIONID
|
||||
<2> We customize the path of the cookie to be "/" (rather than the default of the context root)
|
||||
<3> We customize the domain name pattern (a regular expression) to be `^.+?\\.(\\w+\\.[a-z]+)$`
|
||||
This allows sharing a session across domains and applications.
|
||||
If the regular expression does not match, no domain is set and the existing domain will be used.
|
||||
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] will be used as the domain.
|
||||
This means that a request to https://child.example.com will set the domain to example.com.
|
||||
However, a request to http://localhost:8080/ or http://192.168.1.100:8080/ will leave the cookie unset and thus still work in development without any changes necessary for production.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
It is important to note that users should only match on valid domain characters since the domain name is reflected in the response.
|
||||
This is prevent a malicious user from performing attacks like https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
|
||||
====
|
||||
|
||||
[[custom-cookie-options]]
|
||||
== Configuration Options
|
||||
|
||||
The configuration options available are:
|
||||
|
||||
* `cookieName` - the name of the cookie to use
|
||||
Default "SESSION"
|
||||
* `useSecureCookie` - specify if a secure cookie be used
|
||||
Default use value of `HttpServletRequest.isSecure()` at the time of creation.
|
||||
* `cookiePath` - the path of the cookie
|
||||
Default is context root
|
||||
* `cookieMaxAge` - specifies the max age of the cookie to be set at the time the session is created.
|
||||
Default is -1 which indicates the cookie will be removed when the browser is closed.
|
||||
* `jvmRoute` - specifies a suffix to be appended to the session id and included in the cookie.
|
||||
Used to identify which JVM to route to for session affinity.
|
||||
With some implementations (i.e. Redis) this provides no performance benefit.
|
||||
However, this can help with tracing logs of a particular user.
|
||||
* `domainName` - allows specifying a specific domain name to be used for the cookie.
|
||||
This option is simple to understand, but will likely require a different configuration between development and production environments.
|
||||
See domainNamePattern as an alternative.
|
||||
* `domainNamePattern` - a case insensitive pattern used to extract the domain name from the `HttpServletRequest#getServerName()`.
|
||||
The pattern should provide a single grouping used to extract the value of the cookie domain.
|
||||
If the regular expression does not match, no domain is set and the existing domain will be used.
|
||||
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] will be used as the domain.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
It is important to note that users should only match on valid domain characters since the domain name is reflected in the response.
|
||||
This is prevent a malicious user from performing attacks like https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
|
||||
====
|
||||
|
||||
|
||||
[[custom-cookie-sample]]
|
||||
== custom-cookie Sample Application
|
||||
|
||||
|
||||
|
||||
=== Running the custom-cookie Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `RedisConnectionFactory` to point to a Redis server.
|
||||
Another option is to use https://www.docker.com/[Docker] to run Redis on localhost. See https://hub.docker.com/_/redis/[Docker Redis repository] for detailed instructions.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :spring-session-sample-javaconfig-custom-cookie:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the custom-cookie Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _rob_
|
||||
|
||||
Now click the **Set Attribute** button.
|
||||
You should now see the values displayed in the table.
|
||||
|
||||
If you look at the cookies for the application, you can see the cookie is saved to the custom name of JSESSIONID
|
||||
@@ -1,195 +0,0 @@
|
||||
= Spring Session and Spring Security with Hazelcast
|
||||
Tommy Ludwig; Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session along with Spring Security using Hazelcast as your data store.
|
||||
It assumes you have already applied Spring Security to your application.
|
||||
|
||||
NOTE: The completed guide can be found in the <<hazelcast-spring-security-sample, Hazelcast Spring Security sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.hazelcast</groupId>
|
||||
<artifactId>hazelcast</artifactId>
|
||||
<version>{hazelcast-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[security-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{docs-test-dir}docs/http/HazelcastHttpSessionConfig.java[tags=config]
|
||||
----
|
||||
|
||||
<1> The `@EnableHazelcastHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Hazelcast.
|
||||
<2> In order to support retrieval of sessions by principal name index, appropriate `ValueExtractor` needs to be registered.
|
||||
Spring Session provides `PrincipalNameExtractor` for this purpose.
|
||||
<3> We create a `HazelcastInstance` that connects Spring Session to Hazelcast.
|
||||
By default, an embedded instance of Hazelcast is started and connected to by the application.
|
||||
For more information on configuring Hazelcast, refer to the http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#hazelcast-configuration[reference documentation].
|
||||
|
||||
== Servlet Container Initialization
|
||||
|
||||
Our <<security-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `SessionConfig` class.
|
||||
Since our application is already loading Spring configuration using our `SecurityInitializer` class, we can simply add our `SessionConfig` class to it.
|
||||
|
||||
.src/main/java/sample/SecurityInitializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}javaconfig/hazelcast/src/main/java/sample/SecurityInitializer.java[tags=class]
|
||||
----
|
||||
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
It is extremely important that Spring Session's `springSessionRepositoryFilter` is invoked before Spring Security's `springSecurityFilterChain`.
|
||||
This ensures that the `HttpSession` that Spring Security uses is backed by Spring Session.
|
||||
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` that makes this extremely easy.
|
||||
You can find an example below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}javaconfig/hazelcast/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
By extending `AbstractHttpSessionApplicationInitializer` we ensure that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request before Spring Security's `springSecurityFilterChain`.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[hazelcast-spring-security-sample]]
|
||||
== Hazelcast Spring Security Sample Application
|
||||
|
||||
=== Running the Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Hazelcast will run in embedded mode with your application by default, but if you want to connect
|
||||
to a stand alone instance instead, you can configure it by following the instructions in the
|
||||
http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#hazelcast-configuration[reference documentation].
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :spring-session-sample-javaconfig-hazelcast:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the security Sample Application
|
||||
|
||||
Try using the application. Enter the following to log in:
|
||||
|
||||
* **Username** _user_
|
||||
* **Password** _password_
|
||||
|
||||
Now click the **Login** button.
|
||||
You should now see a message indicating your are logged in with the user entered previously.
|
||||
The user's information is stored in Hazelcast rather than Tomcat's `HttpSession` implementation.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Hazelcast.
|
||||
Spring Session replaces the `HttpSession` with an implementation that is backed by a `Map` in Hazelcast.
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into Hazelcast.
|
||||
|
||||
When a new `HttpSession` is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developers.google.com/web/tools/chrome-devtools/manage-data/cookies[Chrome] or https://developer.mozilla.org/en-US/docs/Tools/Storage_Inspector[Firefox]).
|
||||
|
||||
=== Interact with the data store
|
||||
|
||||
If you like, you can remove the session using http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#hazelcast-java-client[a Java client],
|
||||
http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#other-client-implementations[one of the other clients], or the
|
||||
http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#management-center[management center].
|
||||
|
||||
==== Using the console
|
||||
|
||||
For example, using the management center console after connecting to your Hazelcast node:
|
||||
|
||||
default> ns spring:session:sessions
|
||||
spring:session:sessions> m.clear
|
||||
|
||||
TIP: The Hazelcast documentation has instructions for http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#executing-console-commands[the console].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into the console ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
spring:session:sessions> m.remove 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
|
||||
|
||||
==== Using the REST API
|
||||
|
||||
As described in the other clients section of the documentation, there is a
|
||||
http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#rest-client[REST API]
|
||||
provided by the Hazelcast node(s).
|
||||
|
||||
For example, you could delete an individual key as follows (replacing `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie):
|
||||
|
||||
$ curl -v -X DELETE http://localhost:xxxxx/hazelcast/rest/maps/spring:session:sessions/7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
TIP: The port number of the Hazelcast node will be printed to the console on startup. Replace `xxxxx` above with the port number.
|
||||
|
||||
Now observe that you are no longer authenticated with this session.
|
||||
@@ -1,152 +0,0 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` with Java Configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-jdbc-sample, httpsession-jdbc sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-jdbc</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[httpsession-jdbc-spring-configuration]]
|
||||
== Spring Java Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}javaconfig/jdbc/src/main/java/sample/Config.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableJdbcHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by a relational database.
|
||||
<2> We create a `dataSource` that connects Spring Session to an embedded instance of H2 database.
|
||||
We configure the H2 database to create database tables using the SQL script which is included in Spring Session.
|
||||
<3> We create a `transactionManager` that manages transactions for previously configured `dataSource`.
|
||||
|
||||
For additional information on how to configure data access related concerns, please refer to the https://docs.spring.io/spring/docs/{spring-framework-version}/spring-framework-reference/data-access.html[Spring Framework Reference Documentation].
|
||||
|
||||
== Java Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` both of these steps extremely easy.
|
||||
You can find an example below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}javaconfig/jdbc/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
<1> The first step is to extend `AbstractHttpSessionApplicationInitializer`.
|
||||
This ensures that the Spring Bean by the name `springSessionRepositoryFilter` is registered with our Servlet Container for every request.
|
||||
<2> `AbstractHttpSessionApplicationInitializer` also provides a mechanism to easily ensure Spring loads our `Config`.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-jdbc-sample]]
|
||||
== httpsession-jdbc Sample Application
|
||||
|
||||
=== Running the httpsession-jdbc Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :spring-session-sample-javaconfig-jdbc:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the httpsession-jdbc Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _rob_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}javaconfig/jdbc/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developers.google.com/web/tools/chrome-devtools/manage-data/cookies[Chrome] or https://developer.mozilla.org/en-US/docs/Tools/Storage_Inspector[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using H2 web console available at: http://localhost:8080/h2-console/ (use `jdbc:h2:mem:testdb` for JDBC URL)
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
|
||||
@@ -1,220 +0,0 @@
|
||||
= Spring Session - REST
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using REST endpoints.
|
||||
|
||||
NOTE: The completed guide can be found in the <<rest-sample, rest sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
<version>{lettuce-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[rest-spring-configuration]]
|
||||
== Spring Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}javaconfig/rest/src/main/java/sample/HttpSessionConfig.java[tags=class]
|
||||
----
|
||||
|
||||
<1> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by Redis.
|
||||
<2> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server.
|
||||
We configure the connection to connect to localhost on the default port (6379)
|
||||
For more information on configuring Spring Data Redis, refer to the https://docs.spring.io/spring-data/data-redis/docs/{spring-data-redis-version}/reference/html/[reference documentation].
|
||||
<3> We customize Spring Session's HttpSession integration to use HTTP headers to convey the current session information instead of cookies.
|
||||
|
||||
== Servlet Container Initialization
|
||||
|
||||
Our <<rest-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class. We provide the configuration in our Spring `MvcInitializer` as shown below:
|
||||
|
||||
.src/main/java/sample/mvc/MvcInitializer.java
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}javaconfig/rest/src/main/java/sample/mvc/MvcInitializer.java[tags=config]
|
||||
----
|
||||
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` that makes this extremely easy. Simply extend the class with the default constructor as shown below:
|
||||
|
||||
.src/main/java/sample/Initializer.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}javaconfig/rest/src/main/java/sample/Initializer.java[tags=class]
|
||||
----
|
||||
|
||||
NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[rest-sample]]
|
||||
== rest Sample Application
|
||||
|
||||
=== Running the rest Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must https://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `RedisConnectionFactory` to point to a Redis server.
|
||||
Another option is to use https://www.docker.com/[Docker] to run Redis on localhost. See https://hub.docker.com/_/redis/[Docker Redis repository] for detailed instructions.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :spring-session-sample-javaconfig-rest:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the rest Sample Application
|
||||
|
||||
Try using the application. Use your favorite REST client to request http://localhost:8080/
|
||||
|
||||
$ curl -v http://localhost:8080/
|
||||
|
||||
Observe that we are prompted for basic authentication. Provide the following information for the username and password:
|
||||
|
||||
* **Username** *user*
|
||||
* **Password** *password*
|
||||
|
||||
$ curl -v http://localhost:8080/ -u user:password
|
||||
|
||||
In the output you will notice the following:
|
||||
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
...
|
||||
X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3
|
||||
|
||||
{"username":"user"}
|
||||
----
|
||||
|
||||
Specifically, we notice the following things about our response:
|
||||
|
||||
* The HTTP Status is now a 200
|
||||
* We have a header with the name of *X-Auth-Token* which contains a new session id
|
||||
* The current username is displayed
|
||||
|
||||
We can now use the *X-Auth-Token* to make another request without providing the username and password again. For example, the following outputs the username just as before:
|
||||
|
||||
$ curl -v http://localhost:8080/ -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
|
||||
|
||||
The only difference is that the session id is not provided in the response headers because we are reusing an existing session.
|
||||
|
||||
If we invalidate the session, then the X-Auth-Token is displayed in the response with an empty value. For example, the following will invalidate our session:
|
||||
|
||||
$ curl -v http://localhost:8080/logout -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
|
||||
|
||||
You will see in the output that the X-Auth-Token provides an empty String indicating that the previous session was invalidated.
|
||||
|
||||
----
|
||||
HTTP/1.1 204 No Content
|
||||
...
|
||||
X-Auth-Token:
|
||||
----
|
||||
|
||||
=== How does it work?
|
||||
|
||||
Spring Security interacts with the standard `HttpSession` in `SecurityContextPersistenceFilter`.
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, Spring Security is now persisting the values in Redis.
|
||||
Spring Session creates a header named X-Auth-Token in your browser that contains the id of your session.
|
||||
|
||||
If you like, you can easily see that the session is created in Redis. First create a session using the following:
|
||||
|
||||
$ curl -v http://localhost:8080/ -u user:password
|
||||
|
||||
In the output you will notice the following:
|
||||
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
...
|
||||
X-Auth-Token: 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
{"username":"user"}
|
||||
----
|
||||
|
||||
Now remove the session using redis-cli. For example, on a Linux based system you can type:
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for https://redis.io/topics/quickstart[installing redis-cli].
|
||||
|
||||
Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie:
|
||||
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
We can now use the *X-Auth-Token* to make another request with the session we deleted and observe we are prompted for a authentication. For example, the following returns an HTTP 401:
|
||||
|
||||
$ curl -v http://localhost:8080/ -H "X-Auth-Token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"
|
||||
@@ -1,162 +0,0 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage a relational to back a web application's `HttpSession` with XML based configuration.
|
||||
|
||||
NOTE: The completed guide can be found in the <<httpsession-jdbc-xml-sample, httpsession-jdbc-xml sample application>>.
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must ensure to update your dependencies.
|
||||
If you are using Maven, ensure to add the following dependencies:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<!-- ... -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-jdbc</artifactId>
|
||||
<version>{spring-session-version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
ifeval::["{version-snapshot}" == "true"]
|
||||
Since we are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repositories>
|
||||
|
||||
<!-- ... -->
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
ifeval::["{version-milestone}" == "true"]
|
||||
Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository.
|
||||
Ensure you have the following in your pom.xml:
|
||||
|
||||
.pom.xml
|
||||
[source,xml]
|
||||
----
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
[[httpsession-jdbc-xml-spring-configuration]]
|
||||
== Spring XML Configuration
|
||||
|
||||
After adding the required dependencies, we can create our Spring configuration.
|
||||
The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
.src/main/webapp/WEB-INF/spring/session.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/spring/session.xml[tags=beans]
|
||||
----
|
||||
|
||||
<1> We use the combination of `<context:annotation-config/>` and `JdbcHttpSessionConfiguration` because Spring Session does not yet provide XML Namespace support (see https://github.com/spring-projects/spring-session/issues/104[gh-104]).
|
||||
This creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter.
|
||||
The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session.
|
||||
In this instance Spring Session is backed by a relational database.
|
||||
<2> We create a `dataSource` that connects Spring Session to an embedded instance of H2 database.
|
||||
We configure the H2 database to create database tables using the SQL script which is included in Spring Session.
|
||||
<3> We create a `transactionManager` that manages transactions for previously configured `dataSource`.
|
||||
|
||||
For additional information on how to configure data access related concerns, please refer to the https://docs.spring.io/spring/docs/{spring-framework-version}/spring-framework-reference/data-access.html[Spring Framework Reference Documentation].
|
||||
|
||||
== XML Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-xml-spring-configuration,Spring Configuration>> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, we need to instruct Spring to load our `session.xml` configuration.
|
||||
We do this with the following configuration:
|
||||
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=context-param]
|
||||
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=listeners]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring/docs/{spring-framework-version}/spring-framework-reference/core.html#context-create[ContextLoaderListener] reads the contextConfigLocation and picks up our session.xml configuration.
|
||||
|
||||
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request.
|
||||
The following snippet performs this last step for us:
|
||||
|
||||
.src/main/webapp/WEB-INF/web.xml
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
|
||||
----
|
||||
|
||||
The https://docs.spring.io/spring-framework/docs/{spring-framework-version}/javadoc-api/org/springframework/web/filter/DelegatingFilterProxy.html[DelegatingFilterProxy] will look up a Bean by the name of `springSessionRepositoryFilter` and cast it to a `Filter`.
|
||||
For every request that `DelegatingFilterProxy` is invoked, the `springSessionRepositoryFilter` will be invoked.
|
||||
|
||||
// end::config[]
|
||||
|
||||
[[httpsession-jdbc-xml-sample]]
|
||||
== httpsession-jdbc-xml Sample Application
|
||||
|
||||
=== Running the httpsession-jdbc-xml Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :spring-session-sample-xml-jdbc:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the httpsession-jdbc-xml Sample Application
|
||||
|
||||
Try using the application. Fill out the form with the following information:
|
||||
|
||||
* **Attribute Name:** _username_
|
||||
* **Attribute Value:** _rob_
|
||||
|
||||
Now click the **Set Attribute** button. You should now see the values displayed in the table.
|
||||
|
||||
=== How does it work?
|
||||
|
||||
We interact with the standard `HttpSession` in the `SessionServlet` shown below:
|
||||
|
||||
.src/main/java/sample/SessionServlet.java
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}xml/jdbc/src/main/java/sample/SessionServlet.java[tags=class]
|
||||
----
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in H2 database.
|
||||
Spring Session creates a cookie named SESSION in your browser that contains the id of your session.
|
||||
Go ahead and view the cookies (click for help with https://developers.google.com/web/tools/chrome-devtools/manage-data/cookies[Chrome] or https://developer.mozilla.org/en-US/docs/Tools/Storage_Inspector[Firefox]).
|
||||
|
||||
If you like, you can easily remove the session using H2 web console available at: http://localhost:8080/h2-console/ (use `jdbc:h2:mem:testdb` for JDBC URL)
|
||||
|
||||
Now visit the application at http://localhost:8080/ and observe that the attribute we added is no longer displayed.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,37 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<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 -->
|
||||
<module name="io.spring.javaformat.checkstyle.SpringChecks"/>
|
||||
<module name="com.puppycrawl.tools.checkstyle.TreeWalker">
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck">
|
||||
<property name="regexp" value="true"/>
|
||||
<property name="illegalPkgs"
|
||||
value="^sun.*, ^org\.apache\.commons\.(?!compress|dbcp2|lang|lang3|logging|pool2).*, ^com\.google\.common.*, ^org\.flywaydb\.core\.internal.*, ^org\.testcontainers\.shaded.*"/>
|
||||
<property name="illegalClasses"
|
||||
value="^reactor\.core\.support\.Assert, ^org\.junit\.rules\.ExpectedException, ^org\.slf4j\.LoggerFactory"/>
|
||||
</module>
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format" value="org\.junit\.Assert\.assert"/>
|
||||
<property name="message" value="Please use AssertJ imports."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format"
|
||||
value="assertThatExceptionOfType\((NullPointerException|IllegalArgumentException|IOException|IllegalStateException)\.class\)"/>
|
||||
<property name="message" value="Please use specialized AssertJ assertThat*Exception method."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="format" value="org\.mockito\.Mockito\.(when|doThrow|doAnswer)"/>
|
||||
<property name="message" value="Please use BDDMockito imports."/>
|
||||
<property name="ignoreComments" value="true"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
|
||||
@@ -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$
|
||||
^.*$
|
||||
@@ -1,16 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<!DOCTYPE suppressions PUBLIC "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
|
||||
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
|
||||
<suppressions>
|
||||
<!-- global -->
|
||||
<suppress files="[\\/]src[\\/]integration-test[\\/]java[\\/]" checks="Javadoc*"/>
|
||||
|
||||
<!-- docs -->
|
||||
<suppress files="[\\/]docs[\\/]" checks="Javadoc*"/>
|
||||
<suppress files="[\\/]docs[\\/]" checks="AvoidStaticImport"/>
|
||||
<suppress files="[\\/]docs[\\/]" checks="InnerTypeLast"/>
|
||||
|
||||
<!-- samples -->
|
||||
<suppress files="[\\/]samples[\\/]" checks="Javadoc*"/>
|
||||
<suppress files="[\\/]samples[\\/].+Application\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files="[\\/]spring-session-docs[\\/]" checks="Javadoc*"/>
|
||||
<suppress files="[\\/]spring-session-docs[\\/]" checks="InnerTypeLast"/>
|
||||
<suppress files="[\\/]spring-session-samples[\\/]" checks="Javadoc*"/>
|
||||
<suppress files="[\\/]spring-session-samples[\\/].+Application\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files="SessionRepositoryFilterTests\.java" checks="SpringLambda"/>
|
||||
</suppressions>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,2 +1 @@
|
||||
springBootVersion=2.0.3.RELEASE
|
||||
version=2.1.0.M1
|
||||
version=2.2.0.M2
|
||||
|
||||
@@ -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:Dysprosium-M2'
|
||||
mavenBom 'org.junit:junit-bom:5.4.2'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.2.0.M3'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Moore-RC1'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.2.0.M3'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.11.3'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.10.3') {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.12.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.h2database:h2:1.4.199'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:7.2.2.jre8'
|
||||
dependency 'com.zaxxer:HikariCP:3.3.1'
|
||||
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 'junit:junit:4.12'
|
||||
dependency 'mysql:mysql-connector-java:8.0.11'
|
||||
dependency 'io.lettuce:lettuce-core:5.1.7.RELEASE'
|
||||
dependency 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
dependency 'javax.servlet:javax.servlet-api:4.0.1'
|
||||
dependency 'mysql:mysql-connector-java:8.0.16'
|
||||
dependency 'org.apache.derby:derby:10.14.2.0'
|
||||
dependency 'org.assertj:assertj-core:3.10.0'
|
||||
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.assertj:assertj-core:3.12.2'
|
||||
dependency 'org.hsqldb:hsqldb:2.5.0'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.4.1'
|
||||
dependency 'org.mockito:mockito-core:2.28.2'
|
||||
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-5.4.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
18
gradlew
vendored
18
gradlew
vendored
@@ -1,5 +1,21 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
18
gradlew.bat
vendored
18
gradlew.bat
vendored
@@ -1,3 +1,19 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -1,174 +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 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;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import sample.pages.HomePage;
|
||||
import sample.pages.LoginPage;
|
||||
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* @author Eddú Meléndez
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@AutoConfigureMockMvc
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
|
||||
public class FindByUsernameTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
this.driver.quit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void home() {
|
||||
LoginPage login = HomePage.go(this.driver);
|
||||
login.assertAt();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void login() {
|
||||
LoginPage login = HomePage.go(this.driver);
|
||||
HomePage home = login.form().login(HomePage.class);
|
||||
home.assertAt();
|
||||
home.containCookie("SESSION");
|
||||
home.doesNotContainCookie("JSESSIONID");
|
||||
home.terminateButtonDisabled();
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public GenericContainer redisContainer() {
|
||||
GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
.withExposedPorts(6379);
|
||||
redisContainer.start();
|
||||
return redisContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LettuceConnectionFactory redisConnectionFactory() {
|
||||
return new LettuceConnectionFactory(redisContainer().getContainerIpAddress(),
|
||||
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,163 +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 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;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import sample.pages.HomePage;
|
||||
import sample.pages.LoginPage;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Eddú Meléndez
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@AutoConfigureMockMvc
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
|
||||
public class BootTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
this.driver.quit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void home() {
|
||||
LoginPage login = HomePage.go(this.driver);
|
||||
login.assertAt();
|
||||
HomePage home = login.form().login(HomePage.class);
|
||||
home.assertAt();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void login() {
|
||||
LoginPage login = HomePage.go(this.driver);
|
||||
HomePage home = login.form().login(HomePage.class);
|
||||
home.containCookie("SESSION");
|
||||
home.doesNotContainCookie("JSESSIONID");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logout() {
|
||||
LoginPage login = HomePage.go(this.driver);
|
||||
HomePage home = login.form().login(HomePage.class);
|
||||
login = home.logout();
|
||||
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,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.pages;
|
||||
|
||||
import org.openqa.selenium.SearchContext;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
import org.openqa.selenium.support.PageFactory;
|
||||
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Eddú Meléndez
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class LoginPage extends BasePage {
|
||||
|
||||
public LoginPage(WebDriver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Login Page");
|
||||
}
|
||||
|
||||
public Form form() {
|
||||
return new Form(getDriver());
|
||||
}
|
||||
|
||||
public class Form {
|
||||
|
||||
@FindBy(name = "username")
|
||||
private WebElement username;
|
||||
|
||||
@FindBy(name = "password")
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(name = "submit")
|
||||
private WebElement button;
|
||||
|
||||
public Form(SearchContext context) {
|
||||
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
|
||||
}
|
||||
|
||||
public <T> T login(Class<T> page) {
|
||||
this.username.sendKeys("user");
|
||||
this.password.sendKeys("password");
|
||||
this.button.click();
|
||||
return PageFactory.initElements(getDriver(), page);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
grailsVersion=3.1.4
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
|
||||
// Added by the Spring Security Core plugin:
|
||||
grails.plugin.springsecurity.userLookup.userDomainClassName = 'grails3.redis.session.User'
|
||||
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'grails3.redis.session.UserRole'
|
||||
grails.plugin.springsecurity.authority.className = 'grails3.redis.session.Role'
|
||||
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
|
||||
[pattern: '/', access: ['permitAll']],
|
||||
[pattern: '/error', access: ['permitAll']],
|
||||
[pattern: '/index', access: ['permitAll']],
|
||||
[pattern: '/index.gsp', access: ['permitAll']],
|
||||
[pattern: '/shutdown', access: ['permitAll']],
|
||||
[pattern: '/assets/**', access: ['permitAll']],
|
||||
[pattern: '/**/js/**', access: ['permitAll']],
|
||||
[pattern: '/**/css/**', access: ['permitAll']],
|
||||
[pattern: '/**/images/**', access: ['permitAll']],
|
||||
[pattern: '/**/favicon.ico', access: ['permitAll']]
|
||||
]
|
||||
|
||||
grails.plugin.springsecurity.filterChain.chainMap = [
|
||||
[pattern: '/assets/**', filters: 'none'],
|
||||
[pattern: '/**/js/**', filters: 'none'],
|
||||
[pattern: '/**/css/**', filters: 'none'],
|
||||
[pattern: '/**/images/**', filters: 'none'],
|
||||
[pattern: '/**/favicon.ico', filters: 'none'],
|
||||
[pattern: '/**', filters: 'JOINED_FILTERS']
|
||||
]
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
---
|
||||
hibernate:
|
||||
cache:
|
||||
queries: false
|
||||
use_second_level_cache: true
|
||||
use_query_cache: false
|
||||
region.factory_class: 'org.hibernate.cache.ehcache.EhCacheRegionFactory'
|
||||
|
||||
dataSource:
|
||||
pooled: true
|
||||
jmxExport: true
|
||||
driverClassName: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
|
||||
environments:
|
||||
development:
|
||||
dataSource:
|
||||
dbCreate: create-drop
|
||||
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
|
||||
test:
|
||||
dataSource:
|
||||
dbCreate: update
|
||||
url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
|
||||
production:
|
||||
dataSource:
|
||||
dbCreate: update
|
||||
url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
|
||||
properties:
|
||||
jmxEnabled: true
|
||||
initialSize: 5
|
||||
maxActive: 50
|
||||
minIdle: 5
|
||||
maxIdle: 25
|
||||
maxWait: 10000
|
||||
maxAge: 600000
|
||||
timeBetweenEvictionRunsMillis: 5000
|
||||
minEvictableIdleTimeMillis: 60000
|
||||
validationQuery: SELECT 1
|
||||
validationQueryTimeout: 3
|
||||
validationInterval: 15000
|
||||
testOnBorrow: true
|
||||
testWhileIdle: true
|
||||
testOnReturn: false
|
||||
jdbcInterceptors: ConnectionState
|
||||
defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
|
||||
|
||||
---
|
||||
---
|
||||
grails:
|
||||
profile: web
|
||||
codegen:
|
||||
defaultPackage: grails3.redis.session
|
||||
spring:
|
||||
transactionManagement:
|
||||
proxies: false
|
||||
info:
|
||||
app:
|
||||
name: '@info.app.name@'
|
||||
version: '@info.app.version@'
|
||||
grailsVersion: '@info.app.grailsVersion@'
|
||||
spring:
|
||||
|
||||
groovy:
|
||||
template:
|
||||
check-template-location: false
|
||||
|
||||
---
|
||||
grails:
|
||||
mime:
|
||||
disable:
|
||||
accept:
|
||||
header:
|
||||
userAgents:
|
||||
- Gecko
|
||||
- WebKit
|
||||
- Presto
|
||||
- Trident
|
||||
types:
|
||||
all: '*/*'
|
||||
atom: application/atom+xml
|
||||
css: text/css
|
||||
csv: text/csv
|
||||
form: application/x-www-form-urlencoded
|
||||
html:
|
||||
- text/html
|
||||
- application/xhtml+xml
|
||||
js: text/javascript
|
||||
json:
|
||||
- application/json
|
||||
- text/json
|
||||
multipartForm: multipart/form-data
|
||||
pdf: application/pdf
|
||||
rss: application/rss+xml
|
||||
text: text/plain
|
||||
hal:
|
||||
- application/hal+json
|
||||
- application/hal+xml
|
||||
xml:
|
||||
- text/xml
|
||||
- application/xml
|
||||
urlmapping:
|
||||
cache:
|
||||
maxsize: 1000
|
||||
controllers:
|
||||
defaultScope: singleton
|
||||
converters:
|
||||
encoding: UTF-8
|
||||
views:
|
||||
default:
|
||||
codec: html
|
||||
gsp:
|
||||
encoding: UTF-8
|
||||
htmlcodec: xml
|
||||
codecs:
|
||||
expression: html
|
||||
scriptlets: html
|
||||
taglib: none
|
||||
staticparts: none
|
||||
endpoints:
|
||||
jmx:
|
||||
unique-names: true
|
||||
@@ -1,23 +0,0 @@
|
||||
import grails.util.BuildSettings
|
||||
import grails.util.Environment
|
||||
|
||||
// See http://logback.qos.ch/manual/groovy.html for details on configuration
|
||||
appender('STDOUT', ConsoleAppender) {
|
||||
encoder(PatternLayoutEncoder) {
|
||||
pattern = "%level %logger - %msg%n"
|
||||
}
|
||||
}
|
||||
|
||||
root(ERROR, ['STDOUT'])
|
||||
|
||||
def targetDir = BuildSettings.TARGET_DIR
|
||||
if (Environment.isDevelopmentMode() && targetDir) {
|
||||
appender("FULL_STACKTRACE", FileAppender) {
|
||||
file = "${targetDir}/stacktrace.log"
|
||||
append = true
|
||||
encoder(PatternLayoutEncoder) {
|
||||
pattern = "%level %logger - %msg%n"
|
||||
}
|
||||
}
|
||||
logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
// Place your Spring DSL code here
|
||||
beans = {
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package grails3.redis.session
|
||||
|
||||
import grails.plugin.springsecurity.annotation.Secured
|
||||
|
||||
class TestController {
|
||||
@Secured('ROLE_ADMIN')
|
||||
def index() { } // Renders `test/index.gsp`
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package grails3.redis.session
|
||||
|
||||
class UrlMappings {
|
||||
|
||||
static mappings = {
|
||||
"/$controller/$action?/$id?(.$format)?"{
|
||||
constraints {
|
||||
// apply constraints here
|
||||
}
|
||||
}
|
||||
|
||||
"/"(view:"/index")
|
||||
"500"(view:'/error')
|
||||
"404"(view:'/notFound')
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package grails3.redis.session
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
|
||||
@EqualsAndHashCode(includes='authority')
|
||||
@ToString(includes='authority', includeNames=true, includePackage=false)
|
||||
class Role implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1
|
||||
|
||||
String authority
|
||||
|
||||
Role(String authority) {
|
||||
this()
|
||||
this.authority = authority
|
||||
}
|
||||
|
||||
static constraints = {
|
||||
authority blank: false, unique: true
|
||||
}
|
||||
|
||||
static mapping = {
|
||||
cache true
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package grails3.redis.session
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
|
||||
@EqualsAndHashCode(includes='username')
|
||||
@ToString(includes='username', includeNames=true, includePackage=false)
|
||||
class User implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1
|
||||
|
||||
transient springSecurityService
|
||||
|
||||
String username
|
||||
String password
|
||||
boolean enabled = true
|
||||
boolean accountExpired
|
||||
boolean accountLocked
|
||||
boolean passwordExpired
|
||||
|
||||
User(String username, String password) {
|
||||
this()
|
||||
this.username = username
|
||||
this.password = password
|
||||
}
|
||||
|
||||
Set<Role> getAuthorities() {
|
||||
UserRole.findAllByUser(this)*.role
|
||||
}
|
||||
|
||||
def beforeInsert() {
|
||||
encodePassword()
|
||||
}
|
||||
|
||||
def beforeUpdate() {
|
||||
if (isDirty('password')) {
|
||||
encodePassword()
|
||||
}
|
||||
}
|
||||
|
||||
protected void encodePassword() {
|
||||
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
|
||||
}
|
||||
|
||||
static transients = ['springSecurityService']
|
||||
|
||||
static constraints = {
|
||||
password blank: false, password: true
|
||||
username blank: false, unique: true
|
||||
}
|
||||
|
||||
static mapping = {
|
||||
password column: '`password`'
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package grails3.redis.session
|
||||
|
||||
import grails.gorm.DetachedCriteria
|
||||
import groovy.transform.ToString
|
||||
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder
|
||||
|
||||
@ToString(cache=true, includeNames=true, includePackage=false)
|
||||
class UserRole implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1
|
||||
|
||||
User user
|
||||
Role role
|
||||
|
||||
UserRole(User u, Role r) {
|
||||
this()
|
||||
user = u
|
||||
role = r
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean equals(other) {
|
||||
if (!(other instanceof UserRole)) {
|
||||
return false
|
||||
}
|
||||
|
||||
other.user?.id == user?.id && other.role?.id == role?.id
|
||||
}
|
||||
|
||||
@Override
|
||||
int hashCode() {
|
||||
def builder = new HashCodeBuilder()
|
||||
if (user) builder.append(user.id)
|
||||
if (role) builder.append(role.id)
|
||||
builder.toHashCode()
|
||||
}
|
||||
|
||||
static UserRole get(long userId, long roleId) {
|
||||
criteriaFor(userId, roleId).get()
|
||||
}
|
||||
|
||||
static boolean exists(long userId, long roleId) {
|
||||
criteriaFor(userId, roleId).count()
|
||||
}
|
||||
|
||||
private static DetachedCriteria criteriaFor(long userId, long roleId) {
|
||||
UserRole.where {
|
||||
user == User.load(userId) &&
|
||||
role == Role.load(roleId)
|
||||
}
|
||||
}
|
||||
|
||||
static UserRole create(User user, Role role, boolean flush = false) {
|
||||
def instance = new UserRole(user: user, role: role)
|
||||
instance.save(flush: flush, insert: true)
|
||||
instance
|
||||
}
|
||||
|
||||
static boolean remove(User u, Role r, boolean flush = false) {
|
||||
if (u == null || r == null) return false
|
||||
|
||||
int rowCount = UserRole.where { user == u && role == r }.deleteAll()
|
||||
|
||||
if (flush) { UserRole.withSession { it.flush() } }
|
||||
|
||||
rowCount
|
||||
}
|
||||
|
||||
static void removeAll(User u, boolean flush = false) {
|
||||
if (u == null) return
|
||||
|
||||
UserRole.where { user == u }.deleteAll()
|
||||
|
||||
if (flush) { UserRole.withSession { it.flush() } }
|
||||
}
|
||||
|
||||
static void removeAll(Role r, boolean flush = false) {
|
||||
if (r == null) return
|
||||
|
||||
UserRole.where { role == r }.deleteAll()
|
||||
|
||||
if (flush) { UserRole.withSession { it.flush() } }
|
||||
}
|
||||
|
||||
static constraints = {
|
||||
role validator: { Role r, UserRole ur ->
|
||||
if (ur.user == null || ur.user.id == null) return
|
||||
boolean existing = false
|
||||
UserRole.withNewSession {
|
||||
existing = UserRole.exists(ur.user.id, r.id)
|
||||
}
|
||||
if (existing) {
|
||||
return 'userRole.exists'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mapping = {
|
||||
id composite: ['user', 'role']
|
||||
version false
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
default.doesnt.match.message=Property [{0}] of class [{1}] with value [{2}] does not match the required pattern [{3}]
|
||||
default.invalid.url.message=Property [{0}] of class [{1}] with value [{2}] is not a valid URL
|
||||
default.invalid.creditCard.message=Property [{0}] of class [{1}] with value [{2}] is not a valid credit card number
|
||||
default.invalid.email.message=Property [{0}] of class [{1}] with value [{2}] is not a valid e-mail address
|
||||
default.invalid.range.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid range from [{3}] to [{4}]
|
||||
default.invalid.size.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid size range from [{3}] to [{4}]
|
||||
default.invalid.max.message=Property [{0}] of class [{1}] with value [{2}] exceeds maximum value [{3}]
|
||||
default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
|
||||
default.invalid.max.size.message=Property [{0}] of class [{1}] with value [{2}] exceeds the maximum size of [{3}]
|
||||
default.invalid.min.size.message=Property [{0}] of class [{1}] with value [{2}] is less than the minimum size of [{3}]
|
||||
default.invalid.validator.message=Property [{0}] of class [{1}] with value [{2}] does not pass custom validation
|
||||
default.not.inlist.message=Property [{0}] of class [{1}] with value [{2}] is not contained within the list [{3}]
|
||||
default.blank.message=Property [{0}] of class [{1}] cannot be blank
|
||||
default.not.equal.message=Property [{0}] of class [{1}] with value [{2}] cannot equal [{3}]
|
||||
default.null.message=Property [{0}] of class [{1}] cannot be null
|
||||
default.not.unique.message=Property [{0}] of class [{1}] with value [{2}] must be unique
|
||||
|
||||
default.paginate.prev=Previous
|
||||
default.paginate.next=Next
|
||||
default.boolean.true=True
|
||||
default.boolean.false=False
|
||||
default.date.format=yyyy-MM-dd HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} created
|
||||
default.updated.message={0} {1} updated
|
||||
default.deleted.message={0} {1} deleted
|
||||
default.not.deleted.message={0} {1} could not be deleted
|
||||
default.not.found.message={0} not found with id {1}
|
||||
default.optimistic.locking.failure=Another user has updated this {0} while you were editing
|
||||
|
||||
default.home.label=Home
|
||||
default.list.label={0} List
|
||||
default.add.label=Add {0}
|
||||
default.new.label=New {0}
|
||||
default.create.label=Create {0}
|
||||
default.show.label=Show {0}
|
||||
default.edit.label=Edit {0}
|
||||
|
||||
default.button.create.label=Create
|
||||
default.button.edit.label=Edit
|
||||
default.button.update.label=Update
|
||||
default.button.delete.label=Delete
|
||||
default.button.delete.confirm.message=Are you sure?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=Property {0} must be a valid URL
|
||||
typeMismatch.java.net.URI=Property {0} must be a valid URI
|
||||
typeMismatch.java.util.Date=Property {0} must be a valid Date
|
||||
typeMismatch.java.lang.Double=Property {0} must be a valid number
|
||||
typeMismatch.java.lang.Integer=Property {0} must be a valid number
|
||||
typeMismatch.java.lang.Long=Property {0} must be a valid number
|
||||
typeMismatch.java.lang.Short=Property {0} must be a valid number
|
||||
typeMismatch.java.math.BigDecimal=Property {0} must be a valid number
|
||||
typeMismatch.java.math.BigInteger=Property {0} must be a valid number
|
||||
typeMismatch=Property {0} is type-mismatched
|
||||
@@ -1,24 +0,0 @@
|
||||
import grails3.redis.session.*
|
||||
|
||||
class BootStrap {
|
||||
|
||||
def init = { servletContext ->
|
||||
def adminRole = new Role('ROLE_ADMIN').save()
|
||||
def userRole = new Role('ROLE_USER').save()
|
||||
|
||||
def testUser = new User('user', 'password').save()
|
||||
|
||||
UserRole.create testUser, adminRole
|
||||
|
||||
UserRole.withSession {
|
||||
it.flush()
|
||||
it.clear()
|
||||
}
|
||||
|
||||
assert User.count() == 1
|
||||
assert Role.count() == 2
|
||||
assert UserRole.count() == 1
|
||||
}
|
||||
def destroy = {
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package grails3.redis.session
|
||||
|
||||
import grails.boot.GrailsApp
|
||||
import grails.boot.config.GrailsAutoConfiguration
|
||||
|
||||
class Application extends GrailsAutoConfiguration {
|
||||
static void main(String[] args) {
|
||||
GrailsApp.run(Application, args)
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title><g:if env="development">Grails Runtime Exception</g:if><g:else>Error</g:else></title>
|
||||
<meta name="layout" content="main">
|
||||
<g:if env="development"><asset:stylesheet src="errors.css"/></g:if>
|
||||
</head>
|
||||
<body>
|
||||
<g:if env="development">
|
||||
<g:if test="${Throwable.isInstance(exception)}">
|
||||
<g:renderException exception="${exception}" />
|
||||
</g:if>
|
||||
<g:elseif test="${request.getAttribute('javax.servlet.error.exception')}">
|
||||
<g:renderException exception="${request.getAttribute('javax.servlet.error.exception')}" />
|
||||
</g:elseif>
|
||||
<g:else>
|
||||
<ul class="errors">
|
||||
<li>An error has occurred</li>
|
||||
<li>Exception: ${exception}</li>
|
||||
<li>Message: ${message}</li>
|
||||
<li>Path: ${path}</li>
|
||||
</ul>
|
||||
</g:else>
|
||||
</g:if>
|
||||
<g:else>
|
||||
<ul class="errors">
|
||||
<li>An error has occurred</li>
|
||||
</ul>
|
||||
</g:else>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,8 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Index</title>
|
||||
</head>
|
||||
<body>
|
||||
Left blank, goto <a href="/test">test</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,14 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Page Not Found</title>
|
||||
<meta name="layout" content="main">
|
||||
<g:if env="development"><asset:stylesheet src="errors.css"/></g:if>
|
||||
</head>
|
||||
<body>
|
||||
<ul class="errors">
|
||||
<li>Error: Page Not Found (404)</li>
|
||||
<li>Path: ${request.forwardURI}</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Home Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="un">
|
||||
<sec:loggedInUserInfo field='username'/>
|
||||
</div>
|
||||
<div id="session">
|
||||
${session.id}
|
||||
</div>
|
||||
<form action="/logout" method="post">
|
||||
<input type="submit" value="Log Out"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,71 +0,0 @@
|
||||
buildscript {
|
||||
ext {
|
||||
grailsVersion = project.grailsVersion
|
||||
}
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { url "https://repo.grails.org/grails/core" }
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
|
||||
classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.5.0"
|
||||
classpath "org.grails.plugins:hibernate4:5.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "eclipse"
|
||||
apply plugin: "idea"
|
||||
apply plugin: "war"
|
||||
apply plugin: "org.grails.grails-web"
|
||||
apply plugin: "org.grails.grails-gsp"
|
||||
apply plugin: "asset-pipeline"
|
||||
apply from: SAMPLE_GRADLE
|
||||
|
||||
ext {
|
||||
grailsVersion = project.grailsVersion
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { url "https://repo.grails.org/grails/core" }
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "org.grails:grails-bom:$grailsVersion"
|
||||
}
|
||||
applyMavenExclusions false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework.boot:spring-boot-starter-logging"
|
||||
compile "org.springframework.boot:spring-boot-autoconfigure"
|
||||
compile "org.grails:grails-core"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
compile "org.springframework.boot:spring-boot-starter-tomcat"
|
||||
compile "org.grails:grails-dependencies"
|
||||
compile "org.grails:grails-web-boot"
|
||||
compile "org.grails.plugins:cache"
|
||||
compile "org.grails.plugins:scaffolding"
|
||||
compile "org.grails.plugins:hibernate4"
|
||||
compile "org.hibernate:hibernate-ehcache"
|
||||
console "org.grails:grails-console"
|
||||
profile "org.grails.profiles:web:3.1.4"
|
||||
runtime "org.grails.plugins:asset-pipeline"
|
||||
runtime "com.h2database:h2"
|
||||
testCompile "org.grails:grails-plugin-testing"
|
||||
testCompile "org.grails.plugins:geb"
|
||||
testCompile "org.assertj:assertj-core"
|
||||
testCompile "org.seleniumhq.selenium:selenium-htmlunit-driver"
|
||||
testCompile "net.sourceforge.htmlunit:htmlunit"
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-redis"
|
||||
compile 'org.springframework.session:spring-session:1.1.1.RELEASE'
|
||||
|
||||
compile 'org.grails.plugins:spring-security-core:3.0.4'
|
||||
}
|
||||
|
||||
assets {
|
||||
minifyJs = true
|
||||
minifyCss = true
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample
|
||||
|
||||
import grails.test.mixin.integration.Integration
|
||||
import grails.transaction.Transactional
|
||||
import org.springframework.boot.test.IntegrationTest
|
||||
|
||||
import spock.lang.*
|
||||
import geb.spock.*
|
||||
|
||||
import sample.pages.HomePage
|
||||
import sample.pages.LoginPage
|
||||
import sample.pages.IndexPage
|
||||
import spock.lang.Stepwise
|
||||
import pages.*
|
||||
|
||||
/**
|
||||
* Functional tests for grails 3 and spring-session
|
||||
*
|
||||
* @author Eric Helgeson
|
||||
*/
|
||||
|
||||
@Stepwise
|
||||
@IntegrationTest("server.port:0")
|
||||
@Integration(applicationClass=grails3.redis.session.Application)
|
||||
class HomeSpec extends GebSpec {
|
||||
|
||||
def setup() {
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
}
|
||||
|
||||
void 'Anonymous page not redirected to login'() {
|
||||
when: 'The index page is visited'
|
||||
go '/'
|
||||
|
||||
then: 'Not redirected'
|
||||
at IndexPage
|
||||
}
|
||||
|
||||
void 'Unauthenticated user sent to log in page'() {
|
||||
when: 'The test page is visited'
|
||||
go '/test/index'
|
||||
if(title != 'Login') {
|
||||
println driver.pageSource
|
||||
}
|
||||
|
||||
then: 'The password form is correct'
|
||||
title == 'Login'
|
||||
$('#password')
|
||||
$('#username')
|
||||
}
|
||||
|
||||
void 'Log in views home page'() {
|
||||
when: 'log in successfully'
|
||||
to LoginPage
|
||||
login()
|
||||
|
||||
then: 'sent to original page'
|
||||
at HomePage
|
||||
|
||||
and: 'the username is displayed'
|
||||
username == 'user'
|
||||
|
||||
and: 'session id is not blank'
|
||||
session != ''
|
||||
|
||||
and: 'Spring Session Management is being used'
|
||||
driver.manage().cookies.find { it.name == 'SESSION' }
|
||||
|
||||
and: 'Standard Session is NOT being used'
|
||||
!driver.manage().cookies.find { it.name == 'JSESSIONID' }
|
||||
}
|
||||
|
||||
def 'Log out success'() {
|
||||
when:
|
||||
logout()
|
||||
|
||||
then:
|
||||
at IndexPage
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The home page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class HomePage extends Page {
|
||||
static url = '/test'
|
||||
static at = { assert driver.title == 'Home Page'; true}
|
||||
static content = {
|
||||
username { $('#un').text() }
|
||||
session { $('#session').text() }
|
||||
logout(to:LoginPage) { $('input[type=submit]').click() }
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The Index page
|
||||
*
|
||||
* @author Eric Helgeson
|
||||
*/
|
||||
class IndexPage extends Page {
|
||||
static url = '/'
|
||||
static at = { assert driver.title == 'Index'; true}
|
||||
static content = { }
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The Login Page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class LoginPage extends Page {
|
||||
static url = '/login'
|
||||
static at = { assert driver.title == 'Login'; true}
|
||||
static content = {
|
||||
form { $('form') }
|
||||
submit { $('input[type=submit]') }
|
||||
login(required:false) { user='user', pass='password' ->
|
||||
form.username = user
|
||||
form.password = pass
|
||||
submit.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.pages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.openqa.selenium.SearchContext;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
import org.openqa.selenium.support.PageFactory;
|
||||
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Eddú Meléndez
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class HomePage {
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
@FindBy(css = "form")
|
||||
WebElement form;
|
||||
|
||||
@FindBy(css = "table tbody tr")
|
||||
List<WebElement> trs;
|
||||
|
||||
List<Attribute> attributes;
|
||||
|
||||
public HomePage(WebDriver driver) {
|
||||
this.driver = driver;
|
||||
this.attributes = new ArrayList<>();
|
||||
}
|
||||
|
||||
private static void get(WebDriver driver, String get) {
|
||||
String baseUrl = "http://localhost:" + System.getProperty("app.port", "8080");
|
||||
driver.get(baseUrl + get);
|
||||
}
|
||||
|
||||
public static HomePage go(WebDriver driver) {
|
||||
get(driver, "/");
|
||||
return PageFactory.initElements(driver, HomePage.class);
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(this.driver.getTitle()).isEqualTo("Session Attributes");
|
||||
}
|
||||
|
||||
public List<Attribute> attributes() {
|
||||
List<Attribute> rows = new ArrayList<>();
|
||||
for (WebElement tr : this.trs) {
|
||||
rows.add(new Attribute(tr));
|
||||
}
|
||||
this.attributes.addAll(rows);
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
public Form form() {
|
||||
return new Form(this.form);
|
||||
}
|
||||
|
||||
public class Form {
|
||||
@FindBy(name = "attributeName")
|
||||
WebElement attributeName;
|
||||
|
||||
@FindBy(name = "attributeValue")
|
||||
WebElement attributeValue;
|
||||
|
||||
@FindBy(css = "input[type=\"submit\"]")
|
||||
WebElement submit;
|
||||
|
||||
public Form(SearchContext context) {
|
||||
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
|
||||
}
|
||||
|
||||
public Form attributeName(String text) {
|
||||
this.attributeName.sendKeys(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Form attributeValue(String text) {
|
||||
this.attributeValue.sendKeys(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <T> T submit(Class<T> page) {
|
||||
this.submit.click();
|
||||
return PageFactory.initElements(HomePage.this.driver, page);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Attribute {
|
||||
@FindBy(xpath = ".//td[1]")
|
||||
WebElement attributeName;
|
||||
|
||||
@FindBy(xpath = ".//td[2]")
|
||||
WebElement attributeValue;
|
||||
|
||||
public Attribute(SearchContext context) {
|
||||
PageFactory.initElements(new DefaultElementLocatorFactory(context), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the attributeName
|
||||
*/
|
||||
public String getAttributeName() {
|
||||
return this.attributeName.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the attributeValue
|
||||
*/
|
||||
public String getAttributeValue() {
|
||||
return this.attributeValue.getText();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +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 sample;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
|
||||
@Configuration
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.10";
|
||||
|
||||
@Bean
|
||||
public GenericContainer redisContainer() {
|
||||
GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
.withExposedPorts(6379);
|
||||
redisContainer.start();
|
||||
return redisContainer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public LettuceConnectionFactory redisConnectionFactory() {
|
||||
return new LettuceConnectionFactory(redisContainer().getContainerIpAddress(),
|
||||
redisContainer().getFirstMappedPort());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- <logger name="org.springframework.security" level="DEBUG"/> -->
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -1,25 +1,16 @@
|
||||
rootProject.name = 'spring-session-build'
|
||||
|
||||
FileTree buildFiles = fileTree(rootDir) {
|
||||
include '**/*.gradle'
|
||||
exclude '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*'
|
||||
exclude '**/grails3'
|
||||
include 'spring-session-core'
|
||||
include 'spring-session-data-redis'
|
||||
include 'spring-session-docs'
|
||||
include 'spring-session-hazelcast'
|
||||
include 'spring-session-jdbc'
|
||||
|
||||
file('spring-session-samples').eachDirMatch(~/spring-session-sample-.*/) { dir ->
|
||||
include dir.name
|
||||
project(":$dir.name").projectDir = dir
|
||||
}
|
||||
|
||||
String rootDirPath = rootDir.absolutePath + File.separator
|
||||
buildFiles.each { File buildFile ->
|
||||
if (buildFile.name == 'build.gradle') {
|
||||
String buildFilePath = buildFile.parentFile.absolutePath
|
||||
String projectPath = buildFilePath.replace(rootDirPath, '').replaceAll(File.separator, ':')
|
||||
include projectPath
|
||||
}
|
||||
else {
|
||||
String projectName = buildFile.name.replace('.gradle', '')
|
||||
String projectPath = ':' + projectName
|
||||
include projectPath
|
||||
def project = findProject("${projectPath}")
|
||||
project.name = projectName
|
||||
project.projectDir = buildFile.parentFile
|
||||
project.buildFileName = buildFile.name
|
||||
}
|
||||
rootProject.children.each { project ->
|
||||
project.buildFileName = "${project.name}.gradle"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -17,10 +18,12 @@ dependencies {
|
||||
optional "org.springframework.security:spring-security-web"
|
||||
|
||||
testCompile "io.projectreactor:reactor-test"
|
||||
testCompile "junit:junit"
|
||||
testCompile "org.mockito:mockito-core"
|
||||
testCompile "edu.umd.cs.mtc:multithreadedtc"
|
||||
testCompile "org.springframework:spring-test"
|
||||
testCompile "org.assertj:assertj-core"
|
||||
testCompile "org.springframework.security:spring-security-core"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-api"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-params"
|
||||
testRuntime "org.junit.jupiter:junit-jupiter-engine"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -91,7 +91,7 @@ import org.springframework.util.ObjectUtils;
|
||||
*
|
||||
* @see EnableSpringHttpSession
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -125,7 +126,6 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
SessionRepository<S> sessionRepository) {
|
||||
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(
|
||||
sessionRepository);
|
||||
sessionRepositoryFilter.setServletContext(this.servletContext);
|
||||
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
|
||||
return sessionRepositoryFilter;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -35,7 +35,7 @@ import org.springframework.web.server.session.WebSessionManager;
|
||||
*
|
||||
* @see EnableSpringWebSession
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class SpringWebSessionConfiguration {
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,6 +18,7 @@ package org.springframework.session.web.http;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
@@ -66,8 +67,11 @@ abstract class OncePerRequestFilter implements Filter {
|
||||
}
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
String alreadyFilteredAttributeName = this.alreadyFilteredAttributeName;
|
||||
alreadyFilteredAttributeName = updateForErrorDispatch(
|
||||
alreadyFilteredAttributeName, request);
|
||||
boolean hasAlreadyFilteredAttribute = request
|
||||
.getAttribute(this.alreadyFilteredAttributeName) != null;
|
||||
.getAttribute(alreadyFilteredAttributeName) != null;
|
||||
|
||||
if (hasAlreadyFilteredAttribute) {
|
||||
|
||||
@@ -76,17 +80,28 @@ abstract class OncePerRequestFilter implements Filter {
|
||||
}
|
||||
else {
|
||||
// Do invoke this filter...
|
||||
request.setAttribute(this.alreadyFilteredAttributeName, Boolean.TRUE);
|
||||
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
|
||||
try {
|
||||
doFilterInternal(httpRequest, httpResponse, filterChain);
|
||||
}
|
||||
finally {
|
||||
// Remove the "already filtered" request attribute for this request.
|
||||
request.removeAttribute(this.alreadyFilteredAttributeName);
|
||||
request.removeAttribute(alreadyFilteredAttributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String updateForErrorDispatch(String alreadyFilteredAttributeName,
|
||||
ServletRequest request) {
|
||||
// Jetty does ERROR dispatch within sendError, so request attribute is still present
|
||||
// Use a separate attribute for ERROR dispatches
|
||||
if (DispatcherType.ERROR.equals(request.getDispatcherType())
|
||||
&& request.getAttribute(alreadyFilteredAttributeName) != null) {
|
||||
return alreadyFilteredAttributeName + ".ERROR";
|
||||
}
|
||||
return alreadyFilteredAttributeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same contract as for {@code doFilter}, but guaranteed to be just invoked once per
|
||||
* request within a single request thread.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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 {
|
||||
@@ -102,8 +106,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
private final SessionRepository<S> sessionRepository;
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
private HttpSessionIdResolver httpSessionIdResolver = new CookieHttpSessionIdResolver();
|
||||
|
||||
/**
|
||||
@@ -139,7 +141,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
|
||||
|
||||
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
|
||||
request, response, this.servletContext);
|
||||
request, response);
|
||||
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
|
||||
wrappedRequest, response);
|
||||
|
||||
@@ -151,10 +153,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
}
|
||||
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows ensuring that the session is saved if the response is committed.
|
||||
*
|
||||
@@ -199,21 +197,20 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private S requestedSession;
|
||||
|
||||
private boolean requestedSessionCached;
|
||||
|
||||
private String requestedSessionId;
|
||||
|
||||
private Boolean requestedSessionIdValid;
|
||||
|
||||
private boolean requestedSessionInvalidated;
|
||||
|
||||
private SessionRepositoryRequestWrapper(HttpServletRequest request,
|
||||
HttpServletResponse response, ServletContext servletContext) {
|
||||
HttpServletResponse response) {
|
||||
super(request);
|
||||
this.response = response;
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,7 +274,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
}
|
||||
return isRequestedSessionIdValid(requestedSession);
|
||||
}
|
||||
|
||||
return this.requestedSessionIdValid;
|
||||
}
|
||||
|
||||
@@ -335,15 +331,6 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
return currentSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
if (this.servletContext != null) {
|
||||
return this.servletContext;
|
||||
}
|
||||
// Servlet 3.0+
|
||||
return super.getServletContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSessionWrapper getSession() {
|
||||
return getSession(true);
|
||||
@@ -351,8 +338,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 +355,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 +374,7 @@ public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFi
|
||||
private void clearRequestedSessionCache() {
|
||||
this.requestedSessionCached = false;
|
||||
this.requestedSession = null;
|
||||
this.requestedSessionId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,6 +399,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -21,8 +21,8 @@ import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -35,7 +35,7 @@ public class MapSessionRepositoryTests {
|
||||
|
||||
private MapSession session;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.repository = new MapSessionRepository(new ConcurrentHashMap<>());
|
||||
this.session = new MapSession();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -20,17 +20,17 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
public class MapSessionTests {
|
||||
|
||||
private MapSession session;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.session = new MapSession();
|
||||
this.session.setLastAccessedTime(Instant.ofEpochMilli(1413258262962L));
|
||||
@@ -38,9 +38,9 @@ public class MapSessionTests {
|
||||
|
||||
@Test
|
||||
public void constructorNullSession() {
|
||||
assertThatThrownBy(() -> new MapSession((Session) null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("session cannot be null");
|
||||
assertThatIllegalArgumentException()
|
||||
.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.");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.session.getRequiredAttribute("attrName"))
|
||||
.withMessage("Required attribute 'attrName' is missing.");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -23,11 +23,11 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveMapSessionRepository}.
|
||||
@@ -41,7 +41,7 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
|
||||
private MapSession session;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.repository = new ReactiveMapSessionRepository(new HashMap<>());
|
||||
this.session = new MapSession("session-id");
|
||||
@@ -60,9 +60,9 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
|
||||
@Test
|
||||
public void constructorMapWhenNullThenThrowsIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new ReactiveMapSessionRepository(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("sessions cannot be null");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ReactiveMapSessionRepository(null))
|
||||
.withMessage("sessions cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -107,13 +107,12 @@ public class ReactiveMapSessionRepositoryTests {
|
||||
public void createSessionWhenCustomMaxInactiveIntervalThenCustomMaxInactiveInterval() {
|
||||
final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval()
|
||||
.plusSeconds(10);
|
||||
this.repository.setDefaultMaxInactiveInterval(
|
||||
(int) expectedMaxInterval.getSeconds());
|
||||
this.repository
|
||||
.setDefaultMaxInactiveInterval((int) expectedMaxInterval.getSeconds());
|
||||
|
||||
Session session = this.repository.createSession().block();
|
||||
|
||||
assertThat(session.getMaxInactiveInterval())
|
||||
.isEqualTo(expectedMaxInterval);
|
||||
assertThat(session.getMaxInactiveInterval()).isEqualTo(expectedMaxInterval);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -24,9 +24,9 @@ import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -41,7 +41,7 @@ import org.springframework.session.web.http.CookieSerializer;
|
||||
import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -58,7 +58,7 @@ import static org.mockito.Mockito.verify;
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
@@ -80,7 +80,7 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
@Autowired
|
||||
private CookieSerializer cookieSerializer;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.chain = new MockFilterChain();
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -20,8 +20,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
@@ -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}.
|
||||
@@ -49,7 +49,7 @@ public class SpringHttpSessionConfigurationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void closeContext() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
@@ -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,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,8 +18,8 @@ package org.springframework.session.config.annotation.web.server;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
@@ -44,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
public class SpringWebSessionConfigurationTests {
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,17 +17,18 @@
|
||||
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;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
@@ -45,7 +46,6 @@ import static org.mockito.BDDMockito.when;
|
||||
/**
|
||||
* Tests for {@link SpringSessionBackedSessionRegistry}.
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SpringSessionBackedSessionRegistryTest {
|
||||
|
||||
private static final String SESSION_ID = "sessionId";
|
||||
@@ -65,6 +65,11 @@ public class SpringSessionBackedSessionRegistryTest {
|
||||
@InjectMocks
|
||||
private SpringSessionBackedSessionRegistry<Session> sessionRegistry;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionInformationForExistingSession() {
|
||||
Session session = createSession(SESSION_ID, USER_NAME, NOW);
|
||||
@@ -74,7 +79,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 +97,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 +171,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -20,14 +20,14 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
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.assertThatIllegalArgumentException;
|
||||
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");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(
|
||||
() -> this.rememberMeServices.setRememberMeParameterName(null))
|
||||
.withMessage("rememberMeParameterName cannot be empty or null");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -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.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.MapSession;
|
||||
@@ -45,7 +43,7 @@ public class CookieHttpSessionIdResolverTests {
|
||||
private String cookieName;
|
||||
private Session session;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
this.cookieName = "SESSION";
|
||||
this.session = new 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) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user