Compare commits
56 Commits
2.0.0.RC1
...
2.0.1.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e520ea237d | ||
|
|
175e05dcda | ||
|
|
bb427ff1af | ||
|
|
0a65b82373 | ||
|
|
e25c64efae | ||
|
|
43fcba65c4 | ||
|
|
1cc2c83f36 | ||
|
|
0941358807 | ||
|
|
7d3698515e | ||
|
|
d382603445 | ||
|
|
22e3b5ce38 | ||
|
|
ebd4b349d2 | ||
|
|
ffa1bca898 | ||
|
|
d0ee9fd16a | ||
|
|
7a631fe414 | ||
|
|
d217077dec | ||
|
|
a9b3ce034b | ||
|
|
1ba434a357 | ||
|
|
45807998f6 | ||
|
|
2f49a8ac25 | ||
|
|
e364511c7e | ||
|
|
79ccbe7066 | ||
|
|
1edce117aa | ||
|
|
c0f4c7f381 | ||
|
|
7fa07b2973 | ||
|
|
3252b38c87 | ||
|
|
c4daeff3d8 | ||
|
|
2fccca1158 | ||
|
|
81798c36f6 | ||
|
|
27283e29d5 | ||
|
|
77bb9dfdb1 | ||
|
|
c874592323 | ||
|
|
676f0e474e | ||
|
|
e5ec612771 | ||
|
|
280d5c5a77 | ||
|
|
6a370b1ef8 | ||
|
|
41de1b087a | ||
|
|
6188fe68b7 | ||
|
|
ed328ff4b1 | ||
|
|
97ad0311e2 | ||
|
|
702bc37a99 | ||
|
|
17e56dda18 | ||
|
|
f5912da089 | ||
|
|
bff8ce3c03 | ||
|
|
a3803e9e1f | ||
|
|
3fcdc9ebce | ||
|
|
36d157a658 | ||
|
|
f28ab07b9a | ||
|
|
42a6001aae | ||
|
|
fc4d2238bc | ||
|
|
36d349f328 | ||
|
|
5f23a41674 | ||
|
|
4c9fbd5b6b | ||
|
|
f2ba773ec2 | ||
|
|
647dd7c7bb | ||
|
|
555223755d |
14
Jenkinsfile
vendored
14
Jenkinsfile
vendored
@@ -41,12 +41,18 @@ try {
|
||||
}
|
||||
|
||||
if(currentBuild.result == 'SUCCESS') {
|
||||
parallel artifactory: {
|
||||
stage('Artifactory Deploy') {
|
||||
parallel artifacts: {
|
||||
stage('Deploy Artifacts') {
|
||||
node {
|
||||
checkout scm
|
||||
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
|
||||
sh "./gradlew artifactoryPublish -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --no-daemon --stacktrace"
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
202
LICENSE.txt
Normal file
202
LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
32
README.adoc
32
README.adoc
@@ -1,26 +1,32 @@
|
||||
image:https://badges.gitter.im/spring-projects/spring-session.svg[link="https://gitter.im/spring-projects/spring-session?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
|
||||
|
||||
image:https://travis-ci.org/spring-projects/spring-session.svg?branch=master["Build Status", link="https://travis-ci.org/spring-projects/spring-session"]
|
||||
|
||||
= Spring Session
|
||||
Rob Winch
|
||||
|
||||
Spring Session aims to provide a common infrastructure for managing sessions. This provides many benefits including:
|
||||
image:https://travis-ci.org/spring-projects/spring-session.svg?branch=master["Build Status", link="https://travis-ci.org/spring-projects/spring-session"] image:https://badges.gitter.im/spring-projects/spring-session.svg[link="https://gitter.im/spring-projects/spring-session?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
|
||||
|
||||
* Accessing a session from any environment (i.e. web, messaging infrastructure, etc)
|
||||
* In a web environment
|
||||
** Support for clustering in a vendor neutral way
|
||||
** Pluggable strategy for determining the session id
|
||||
** Easily keep the HttpSession alive when a WebSocket is active
|
||||
Spring Session provides an API and implementations for managing a user's session information, while also making it trivial to support clustered sessions without being tied to an application container specific solution.
|
||||
It also provides transparent integration with:
|
||||
|
||||
* `HttpSession` - allows replacing the `HttpSession` in an application container (i.e. Tomcat) neutral way, with support for providing session IDs in headers to work with RESTful APIs.
|
||||
* `WebSocket` - provides the ability to keep the `HttpSession` alive when receiving WebSocket messages
|
||||
* `WebSession` - allows replacing the Spring WebFlux's `WebSession` in an application container neutral way.
|
||||
|
||||
== Modules
|
||||
|
||||
Spring Session consists of the following modules:
|
||||
|
||||
* Spring Session Core - provides core Spring Session functionalities and APIs
|
||||
* Spring Session Data Redis - provides `SessionRepository` and `ReactiveSessionRepository` implementation backed by Redis and configuration support
|
||||
* Spring Session JDBC - provides `SessionRepository` implementation backed by a relational database and configuration support
|
||||
* Spring Session Hazelcast - provides `SessionRepository` implementation backed by Hazelcast and configuration support
|
||||
|
||||
== Code of Conduct
|
||||
|
||||
This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.adoc[code of conduct].
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
= Spring Session Project Site
|
||||
== 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/
|
||||
|
||||
= License
|
||||
== License
|
||||
|
||||
Spring Session is Open Source software released under the http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.5.RELEASE'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.9.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
}
|
||||
repositories {
|
||||
|
||||
@@ -6,20 +6,20 @@ dependencies {
|
||||
testCompile project(':spring-session-data-redis')
|
||||
testCompile project(':spring-session-hazelcast')
|
||||
testCompile project(':spring-session-jdbc')
|
||||
testCompile "org.springframework:spring-jdbc"
|
||||
testCompile "org.springframework:spring-messaging"
|
||||
testCompile "org.springframework:spring-webmvc"
|
||||
testCompile "org.springframework:spring-websocket"
|
||||
testCompile "org.springframework.security:spring-security-config"
|
||||
testCompile "org.springframework.security:spring-security-web"
|
||||
testCompile "org.springframework.security:spring-security-test"
|
||||
testCompile "junit:junit"
|
||||
testCompile "org.mockito:mockito-core"
|
||||
testCompile "org.springframework:spring-test"
|
||||
testCompile "org.assertj:assertj-core"
|
||||
testCompile "com.hazelcast:hazelcast"
|
||||
testCompile "io.lettuce:lettuce-core"
|
||||
testCompile "javax.servlet:javax.servlet-api"
|
||||
testCompile 'org.springframework:spring-jdbc'
|
||||
testCompile 'org.springframework:spring-messaging'
|
||||
testCompile 'org.springframework:spring-webmvc'
|
||||
testCompile 'org.springframework:spring-websocket'
|
||||
testCompile 'org.springframework.security:spring-security-config'
|
||||
testCompile 'org.springframework.security:spring-security-web'
|
||||
testCompile 'org.springframework.security:spring-security-test'
|
||||
testCompile 'junit:junit'
|
||||
testCompile 'org.mockito:mockito-core'
|
||||
testCompile 'org.springframework:spring-test'
|
||||
testCompile 'org.assertj:assertj-core'
|
||||
testCompile 'com.hazelcast:hazelcast'
|
||||
testCompile 'io.lettuce:lettuce-core'
|
||||
testCompile 'javax.servlet:javax.servlet-api'
|
||||
}
|
||||
|
||||
def versions = dependencyManagement.managedVersions
|
||||
@@ -27,19 +27,22 @@ def versions = dependencyManagement.managedVersions
|
||||
asciidoctor {
|
||||
def ghTag = snapshotBuild ? 'master' : project.version
|
||||
def ghUrl = "https://github.com/spring-projects/spring-session/tree/$ghTag"
|
||||
attributes 'version-snapshot': snapshotBuild,
|
||||
|
||||
attributes 'docs-itest-dir': "$rootProject.projectDir.path/docs/src/integration-test/java/",
|
||||
'docs-test-dir': "$rootProject.projectDir.path/docs/src/test/java/",
|
||||
'docs-test-resources-dir': "$rootProject.projectDir.path/docs/src/test/resources/",
|
||||
'download-url': "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip",
|
||||
'gh-samples-url': "$ghUrl/samples/",
|
||||
'gh-url': ghUrl,
|
||||
'hazelcast-version': versions['com.hazelcast:hazelcast'],
|
||||
'lettuce-version': versions['io.lettuce:lettuce-core'],
|
||||
'samples-dir': "$rootProject.projectDir.path/samples/",
|
||||
'session-jdbc-main-resources-dir': "${project(':spring-session-jdbc').projectDir.path}/src/main/resources/",
|
||||
'spring-data-redis-version': versions['org.springframework.data:spring-data-redis'],
|
||||
'spring-framework-version': versions['org.springframework:spring-core'],
|
||||
'spring-security-version': versions['org.springframework.security:spring-security-core'],
|
||||
'spring-session-version': project.version,
|
||||
'version-milestone': milestoneBuild,
|
||||
'version-release': releaseBuild,
|
||||
'gh-url': ghUrl,
|
||||
'gh-samples-url': "$ghUrl/samples/",
|
||||
'download-url' : "https://github.com/spring-projects/spring-session/archive/${ghTag}.zip",
|
||||
'spring-session-version' : version,
|
||||
'spring-version' : versions['org.springframework:spring-core'],
|
||||
'lettuce-version' : versions['io.lettuce:lettuce-core'],
|
||||
'hazelcast-version' : versions['com.hazelcast:hazelcast'],
|
||||
'docs-itest-dir' : rootProject.projectDir.path + '/docs/src/integration-test/java/',
|
||||
'docs-test-dir' : rootProject.projectDir.path + '/docs/src/test/java/',
|
||||
'docs-test-resources-dir' : rootProject.projectDir.path + '/docs/src/test/resources/',
|
||||
'samples-dir' : rootProject.projectDir.path + '/samples/',
|
||||
'session-jdbc-main-resources-dir' : project(':spring-session-jdbc').projectDir.path + '/src/main/resources/'
|
||||
'version-snapshot': snapshotBuild
|
||||
}
|
||||
|
||||
@@ -108,13 +108,13 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:findbyusername:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-boot-findbyusername:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
@@ -54,7 +54,7 @@ spring.session.jdbc.schema=classpath:org/springframework/session/jdbc/schema-@@p
|
||||
spring.session.jdbc.table-name=SPRING_SESSION # Name of database table used to store sessions.
|
||||
----
|
||||
|
||||
For more information, refer to http://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-session[Spring Session] portion of the Spring Boot documentation.
|
||||
For more information, refer to https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-session[Spring Session] portion of the Spring Boot documentation.
|
||||
|
||||
[[httpsession-jdbc-boot-configuration]]
|
||||
== Configuring the DataSource
|
||||
@@ -70,7 +70,7 @@ spring.datasource.username=myapp
|
||||
spring.datasource.password=secret
|
||||
----
|
||||
|
||||
For more information, refer to http://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-configure-datasource[Configure a DataSource] portion of the Spring Boot documentation.
|
||||
For more information, refer to https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-configure-datasource[Configure a DataSource] portion of the Spring Boot documentation.
|
||||
|
||||
[[httpsession-jdbc-boot-servlet-configuration]]
|
||||
== Servlet Container Initialization
|
||||
@@ -95,7 +95,7 @@ The httpsession-jdbc-boot Sample Application demonstrates how to use Spring Sess
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-jdbc-boot:bootRun
|
||||
$ ./gradlew :spring-session-sample-boot-jdbc:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -120,7 +120,7 @@ Spring Session replaces the `HttpSession` with an implementation that is backed
|
||||
When Spring Security's `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession` it is then persisted into H2 database.
|
||||
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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)
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ spring.session.redis.flush-mode= # Sessions flush mode.
|
||||
spring.session.redis.namespace= # Namespace for keys used to store sessions.
|
||||
----
|
||||
|
||||
For more information, refer to http://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-session[Spring Session] portion of the Spring Boot documentation.
|
||||
For more information, refer to https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle/#boot-features-session[Spring Session] portion of the Spring Boot documentation.
|
||||
|
||||
[[boot-redis-configuration]]
|
||||
== Configuring the Redis Connection
|
||||
@@ -67,7 +67,7 @@ spring.redis.password=secret
|
||||
spring.redis.port=6379
|
||||
----
|
||||
|
||||
For more information, refer to http://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.
|
||||
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.
|
||||
|
||||
[[boot-servlet-configuration]]
|
||||
== Servlet Container Initialization
|
||||
@@ -91,13 +91,13 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:boot:bootRun
|
||||
$ ./gradlew :spring-session-sample-boot-redis:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -122,13 +122,13 @@ Spring Session replaces the `HttpSession` with an implementation that is backed
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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 http://redis.io/topics/quickstart[installing redis-cli].
|
||||
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:
|
||||
|
||||
|
||||
@@ -83,13 +83,13 @@ server.session.timeout=60
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:websocket:bootRun
|
||||
$ ./gradlew :spring-session-sample-boot-websocket:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
@@ -70,7 +70,7 @@ spring:
|
||||
port: 6397
|
||||
----
|
||||
|
||||
For more information, refer to http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-connecting-to-redis[Connecting to Redis] portion of the Spring Boot documentation.
|
||||
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
|
||||
@@ -84,13 +84,13 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:grails3:bootRun
|
||||
$ ./gradlew :spring-session-sample-misc-grails3:bootRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/test/index
|
||||
@@ -115,13 +115,13 @@ Spring Session replaces the `HttpSession` with an implementation that is backed
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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 http://redis.io/topics/quickstart[installing redis-cli].
|
||||
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:
|
||||
|
||||
|
||||
@@ -78,13 +78,13 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:custom-cookie:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-javaconfig-custom-cookie:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
@@ -26,7 +26,7 @@ If you are using Maven, ensure to add the following dependencies:
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
@@ -85,7 +85,7 @@ In this instance Spring Session is backed by Hazelcast.
|
||||
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/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
|
||||
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
|
||||
|
||||
@@ -130,11 +130,11 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
====
|
||||
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/latest/manual/html-single/index.html#hazelcast-configuration[reference documentation].
|
||||
http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#hazelcast-configuration[reference documentation].
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:hazelcast-spring:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-javaconfig-hazelcast:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -157,13 +157,13 @@ Spring Session replaces the `HttpSession` with an implementation that is backed
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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/latest/manual/html-single/index.html#hazelcast-java-client[a Java client],
|
||||
http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#other-client-implementations[one of the other clients], or the
|
||||
http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#management-center[management center].
|
||||
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
|
||||
|
||||
@@ -172,7 +172,7 @@ For example, using the management center console after connecting to your Hazelc
|
||||
default> ns spring:session:sessions
|
||||
spring:session:sessions> m.clear
|
||||
|
||||
TIP: The Hazelcast documentation has instructions for http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#executing-console-commands[the console].
|
||||
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:
|
||||
|
||||
@@ -183,7 +183,7 @@ Now visit the application at http://localhost:8080/ and observe that we are no l
|
||||
==== Using the REST API
|
||||
|
||||
As described in the other clients section of the documentation, there is a
|
||||
http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#rest-client[REST API]
|
||||
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):
|
||||
|
||||
@@ -26,7 +26,7 @@ If you are using Maven, ensure to add the following dependencies:
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
@@ -85,7 +85,7 @@ In this instance Spring Session is backed by a relational 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 http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
|
||||
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
|
||||
|
||||
@@ -119,7 +119,7 @@ This ensures that the Spring Bean by the name `springSessionRepositoryFilter` is
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-jdbc:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-javaconfig-jdbc:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -145,7 +145,7 @@ include::{samples-dir}javaconfig/jdbc/src/main/java/sample/SessionServlet.java[t
|
||||
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ If you are using Maven, ensure to add the following dependencies:
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
@@ -88,7 +88,7 @@ The filter is what is in charge of replacing the `HttpSession` implementation to
|
||||
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 http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
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].
|
||||
|
||||
== Java Servlet Container Initialization
|
||||
|
||||
@@ -125,13 +125,13 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:httpsession:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-javaconfig-redis:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -157,13 +157,13 @@ include::{samples-dir}javaconfig/redis/src/main/java/sample/SessionServlet.java[
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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 http://redis.io/topics/quickstart[installing redis-cli].
|
||||
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:
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ If you are using Maven, ensure to add the following dependencies:
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
@@ -88,7 +88,7 @@ The filter is what is in charge of replacing the `HttpSession` implementation to
|
||||
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 http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
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
|
||||
@@ -126,13 +126,13 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:rest:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-javaconfig-rest:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -209,7 +209,7 @@ Now remove the session using redis-cli. For example, on a Linux based system you
|
||||
|
||||
$ redis-cli keys '*' | xargs redis-cli del
|
||||
|
||||
TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli].
|
||||
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:
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ If you are using Maven, ensure to add the following dependencies:
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
@@ -87,7 +87,7 @@ The filter is what is in charge of replacing the `HttpSession` implementation to
|
||||
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 http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
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].
|
||||
|
||||
== Servlet Container Initialization
|
||||
|
||||
@@ -130,13 +130,13 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:security:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-javaconfig-security:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -159,13 +159,13 @@ Spring Session replaces the `HttpSession` with an implementation that is backed
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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 http://redis.io/topics/quickstart[installing redis-cli].
|
||||
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:
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ If you are using Maven, ensure to add the following dependencies:
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
@@ -87,7 +87,7 @@ In this instance Spring Session is backed by a relational 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 http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
|
||||
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
|
||||
|
||||
@@ -105,7 +105,7 @@ include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=context-para
|
||||
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=listeners]
|
||||
----
|
||||
|
||||
The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the contextConfigLocation and picks up our session.xml configuration.
|
||||
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:
|
||||
@@ -116,7 +116,7 @@ The following snippet performs this last step for us:
|
||||
include::{samples-dir}xml/jdbc/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
|
||||
----
|
||||
|
||||
The http://docs.spring.io/spring-framework/docs/current/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`.
|
||||
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[]
|
||||
@@ -129,7 +129,7 @@ For every request that `DelegatingFilterProxy` is invoked, the `springSessionRep
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:httpsession-jdbc-xml:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-xml-jdbc:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -155,7 +155,7 @@ include::{samples-dir}xml/jdbc/src/main/java/sample/SessionServlet.java[tags=cla
|
||||
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ If you are using Maven, ensure to add the following dependencies:
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
<version>{spring-framework-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
@@ -90,7 +90,7 @@ The filter is what is in charge of replacing the `HttpSession` implementation to
|
||||
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 http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation].
|
||||
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].
|
||||
|
||||
== XML Servlet Container Initialization
|
||||
|
||||
@@ -108,7 +108,7 @@ include::{samples-dir}xml/redis/src/main/webapp/WEB-INF/web.xml[tags=context-par
|
||||
include::{samples-dir}xml/redis/src/main/webapp/WEB-INF/web.xml[tags=listeners]
|
||||
----
|
||||
|
||||
The http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-create[ContextLoaderListener] reads the contextConfigLocation and picks up our session.xml configuration.
|
||||
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:
|
||||
@@ -119,7 +119,7 @@ The following snippet performs this last step for us:
|
||||
include::{samples-dir}xml/redis/src/main/webapp/WEB-INF/web.xml[tags=springSessionRepositoryFilter]
|
||||
----
|
||||
|
||||
The http://docs.spring.io/spring-framework/docs/current/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`.
|
||||
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[]
|
||||
@@ -133,13 +133,13 @@ You can run the sample by obtaining the {download-url}[source code] and invoking
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
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 :samples:httpsession-xml:tomcatRun
|
||||
$ ./gradlew :spring-session-sample-xml-redis:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
@@ -165,13 +165,13 @@ include::{samples-dir}xml/redis/src/main/java/sample/SessionServlet.java[tags=cl
|
||||
|
||||
Instead of using Tomcat's `HttpSession`, we are actually persisting the values in Redis.
|
||||
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://developer.chrome.com/devtools/docs/resources#cookies[Chrome] or https://getfirebug.com/wiki/index.php/Cookies_Panel#Cookies_List[Firefox]).
|
||||
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 http://redis.io/topics/quickstart[installing redis-cli].
|
||||
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:
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
= Spring Session
|
||||
Rob Winch, Vedran Pavić, Jakub Kubrynski
|
||||
Rob Winch, Vedran Pavić
|
||||
:doctype: book
|
||||
:indexdoc-tests: {docs-test-dir}docs/IndexDocTests.java
|
||||
:websocketdoc-test-dir: {docs-test-dir}docs/websocket/
|
||||
@@ -13,29 +12,31 @@ Spring Session provides an API and implementations for managing a user's session
|
||||
[[introduction]]
|
||||
== Introduction
|
||||
|
||||
Spring Session provides an API and implementations for managing a user's session information. It also provides transparent integration with:
|
||||
|
||||
* <<httpsession,HttpSession>> - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way.
|
||||
Additional features include:
|
||||
** **Clustered Sessions** - Spring Session makes it trivial to support <<httpsession-redis,clustered sessions>> without being tied to an application container specific solution.
|
||||
** **RESTful APIs** - Spring Session allows providing session ids in headers to work with <<httpsession-rest,RESTful APIs>>
|
||||
Spring Session provides an API and implementations for managing a user's session information, while also making it trivial to support clustered sessions without being tied to an application container specific solution.
|
||||
It also provides transparent integration with:
|
||||
|
||||
* <<httpsession,HttpSession>> - allows replacing the `HttpSession` in an application container (i.e. Tomcat) neutral way, with support for providing session IDs in headers to work with RESTful APIs.
|
||||
* <<websocket,WebSocket>> - provides the ability to keep the `HttpSession` alive when receiving WebSocket messages
|
||||
* <<websession,WebSession>> - allows replacing the Spring WebFlux's `WebSession` in an application container neutral way.
|
||||
|
||||
== What's New in 1.3
|
||||
== What's New in 2.0
|
||||
|
||||
Below are the highlights of what is new in Spring Session 1.3. You can find a complete list of what's new by referring to the changelogs of
|
||||
https://github.com/spring-projects/spring-session/milestone/6?closed=1[1.3.0.M1],
|
||||
https://github.com/spring-projects/spring-session/milestone/18?closed=1[1.3.0.M2],
|
||||
https://github.com/spring-projects/spring-session/milestone/16?closed=1[1.3.0.RC1], and
|
||||
https://github.com/spring-projects/spring-session/milestone/19?closed=1[1.3.0.RELEASE].
|
||||
Below are the highlights of what is new in Spring Session 2.0. You can find a complete list of what's new by referring to the changelogs of
|
||||
https://github.com/spring-projects/spring-session/milestone/17?closed=1[2.0.0.M1],
|
||||
https://github.com/spring-projects/spring-session/milestone/22?closed=1[2.0.0.M2],
|
||||
https://github.com/spring-projects/spring-session/milestone/23?closed=1[2.0.0.M3],
|
||||
https://github.com/spring-projects/spring-session/milestone/24?closed=1[2.0.0.M4],
|
||||
https://github.com/spring-projects/spring-session/milestone/25?closed=1[2.0.0.M5],
|
||||
https://github.com/spring-projects/spring-session/milestone/26?closed=1[2.0.0.RC1],
|
||||
https://github.com/spring-projects/spring-session/milestone/27?closed=1[2.0.0.RC2], and
|
||||
https://github.com/spring-projects/spring-session/milestone/30?closed=1[2.0.0.RELEASE].
|
||||
|
||||
* First class support for http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#httpsession-hazelcast[Hazelcast]
|
||||
* First class support for http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/#spring-security-concurrent-sessions-how[Spring Security's concurrent session management]
|
||||
* Added https://github.com/maseev/spring-session-orientdb[OrientDB Community Extension]
|
||||
* https://github.com/spring-projects/spring-session/tree/1.3.0.RELEASE/samples/httpsession-redis-json[GenericJackson2JsonRedisSerializer sample] with Spring Security's new Jackson Support
|
||||
* Guides now https://github.com/spring-projects/spring-session/pull/652[use Lettuce]
|
||||
* `spring.session.cleanup.cron.expression` can be used to override the cleanup task’s cron expression
|
||||
* Upgraded to Java 8 and Spring Framework 5 as baseline
|
||||
* https://github.com/spring-projects/spring-session/issues/683[Added support for managing Spring WebFlux's `WebSession`] with https://github.com/spring-projects/spring-session/issues/816[Redis `ReactiveSessionRepository`]
|
||||
* https://github.com/spring-projects/spring-session/issues/768[Extracted `SessionRepository` implementations to separate modules]
|
||||
* Improved https://github.com/spring-projects/spring-session/issues/682[`Session`] and https://github.com/spring-projects/spring-session/issues/809[`SessionRepository`] APIs
|
||||
* Improved and harmonized configuration support for all supported session stores
|
||||
* https://github.com/spring-projects/spring-session/pull/713[Added support for configuring default `CookieSerializer` using `SessionCookieConfig`]
|
||||
* Lots of performance improvements and bug fixes
|
||||
|
||||
[[samples]]
|
||||
@@ -63,6 +64,10 @@ If you are looking to get started with Spring Session, the best place to start i
|
||||
| Demonstrates how to use Spring Session with WebSockets.
|
||||
| link:guides/boot-websocket.html[WebSockets Guide]
|
||||
|
||||
| {gh-samples-url}boot/webflux[WebFlux]
|
||||
| Demonstrates how to use Spring Session to replace the Spring WebFlux's `WebSession` with Redis.
|
||||
| TBD
|
||||
|
||||
| {gh-samples-url}boot/redis-json[HttpSession with Redis JSON serialization]
|
||||
| Demonstrates how to use Spring Session to replace the `HttpSession` with Redis using JSON serialization.
|
||||
| TBD
|
||||
@@ -127,6 +132,27 @@ If you are looking to get started with Spring Session, the best place to start i
|
||||
|
||||
|===
|
||||
|
||||
[[modules]]
|
||||
== Spring Session Modules
|
||||
|
||||
In Spring Session 1.x all of the Spring Session's `SessionRepository` implementations were available within the `spring-session` artifact.
|
||||
While convenient, this approach wasn't sustainable long-term as more features and `SessionRepository` implementations were added to the project.
|
||||
|
||||
Starting with Spring Session 2.0, the project has been split up to Spring Session Core module, and several other modules that carry `SessionRepository` implementations and functionality related to the specific data store.
|
||||
The users of Spring Data will find this arrangement familiar, with Spring Session Core module taking a role equivalent to Spring Data Commons and providing core functionalities and APIs with other modules containing data store specific implementations.
|
||||
As a part of this split, the Spring Session Data MongoDB and Spring Session Data GemFire modules were moved to separate repositories so the situation with project's repositories/modules is a follows:
|
||||
|
||||
* https://github.com/spring-projects/spring-session[`spring-session` repository]
|
||||
** Hosts Spring Session Core, Spring Session Data Redis, Spring Session JDBC and Spring Session Hazelcast modules
|
||||
* https://github.com/spring-projects/spring-session-data-mongodb[`spring-session-data-mongodb` repository]
|
||||
** Hosts Spring Session Data MongoDB module
|
||||
* https://github.com/spring-projects/spring-session-data-geode[`spring-session-data-geode` repository]
|
||||
** Hosts Spring Session Data Geode/GemFire module
|
||||
|
||||
Going forward, the plan is to externalize each of the `SessionRepository` implementations into a dedicated repository and provide a Maven BOM (as in "bill of materials") module in order to help users with version management concerns.
|
||||
|
||||
Modules maintained by the members of Spring Team will be hosted within the https://github.com/spring-projects[`spring-projects` organization], while the community maintained modules will continue to be promoted via <<community-extensions,Community Extensions>> section of this manual.
|
||||
|
||||
[[httpsession]]
|
||||
== HttpSession Integration
|
||||
|
||||
@@ -139,7 +165,7 @@ This means that developers can switch the `HttpSession` implementation out with
|
||||
We have already mentioned that Spring Session provides transparent integration with `HttpSession`, but what benefits do we get out of this?
|
||||
|
||||
* **Clustered Sessions** - Spring Session makes it trivial to support <<httpsession-redis,clustered sessions>> without being tied to an application container specific solution.
|
||||
* **RESTful APIs** - Spring Session allows providing session ids in headers to work with <<httpsession-rest,RESTful APIs>>
|
||||
* **RESTful APIs** - Spring Session allows providing session IDs in headers to work with <<httpsession-rest,RESTful APIs>>
|
||||
|
||||
[[httpsession-redis]]
|
||||
=== HttpSession with Redis
|
||||
@@ -348,6 +374,116 @@ Before using WebSocket integration, you should be sure that you have <<httpsessi
|
||||
|
||||
include::guides/boot-websocket.adoc[tags=config,leveloffset=+2]
|
||||
|
||||
[[websession]]
|
||||
== WebSession Integration
|
||||
|
||||
Spring Session provides transparent integration with Spring WebFlux's `WebSession`.
|
||||
This means that developers can switch the `WebSession` implementation out with an implementation that is backed by Spring Session.
|
||||
|
||||
[[websession-why]]
|
||||
=== Why Spring Session & WebSession?
|
||||
|
||||
We have already mentioned that Spring Session provides transparent integration with Spring WebFlux's `WebSession`, but what benefits do we get out of this?
|
||||
As with `HttpSession`, Spring Session makes it trivial to support <<websession-redis,clustered sessions>> without being tied to an application container specific solution.
|
||||
|
||||
[[websession-redis]]
|
||||
=== WebSession with Redis
|
||||
|
||||
Using Spring Session with `WebSession` is enabled by simply registering a `WebSessionManager` implementation backed by Spring Session's `ReactiveSessionRepository`.
|
||||
The Spring configuration is responsible for creating a `WebSessionManager` that replaces the `WebSession` implementation with an implementation backed by Spring Session.
|
||||
Add the following Spring Configuration:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@EnableRedisWebSession // <1>
|
||||
public class SessionConfiguration {
|
||||
|
||||
@Bean
|
||||
public LettuceConnectionFactory redisConnectionFactory() {
|
||||
return new LettuceConnectionFactory(); // <2>
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
<1> The `@EnableRedisWebSession` annotation creates a Spring Bean with the name of `webSessionManager` that implements the `WebSessionManager`.
|
||||
This is what is in charge of replacing the `WebSession` 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].
|
||||
|
||||
[[websession-how]]
|
||||
=== How WebSession Integration Works
|
||||
|
||||
With Spring WebFlux and it's `WebSession` things are considerably simpler for Spring Session to integrate with, compared to Servlet API and it's `HttpSession`.
|
||||
Spring WebFlux provides `WebSessionStore` API which presents a strategy for persisting `WebSession`.
|
||||
|
||||
NOTE: This section describes how Spring Session provides transparent integration with `WebSession`. The intent is so that user's can understand what is happening under the covers. This functionality is already integrated and you do NOT need to implement this logic yourself.
|
||||
|
||||
First we create a custom `SpringSessionWebSession` that delegates to Spring Session's `Session`.
|
||||
It looks something like the following:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
public class SpringSessionWebSession implements WebSession {
|
||||
|
||||
enum State {
|
||||
NEW, STARTED
|
||||
}
|
||||
|
||||
private final S session;
|
||||
|
||||
private AtomicReference<State> state = new AtomicReference<>();
|
||||
|
||||
SpringSessionWebSession(S session, State state) {
|
||||
this.session = session;
|
||||
this.state.set(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.state.compareAndSet(State.NEW, State.STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
State value = this.state.get();
|
||||
return (State.STARTED.equals(value)
|
||||
|| (State.NEW.equals(value) && !this.session.getAttributes().isEmpty()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> changeSessionId() {
|
||||
return Mono.defer(() -> {
|
||||
this.session.changeSessionId();
|
||||
return save();
|
||||
});
|
||||
}
|
||||
|
||||
// ... other methods delegate to the original Session
|
||||
}
|
||||
----
|
||||
|
||||
Next, we create a custom `WebSessionStore` that delegates to the `ReactiveSessionRepository` and wraps `Session` into custom `WebSession` implementation:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
public class SpringSessionWebSessionStore<S extends Session> implements WebSessionStore {
|
||||
|
||||
private final ReactiveSessionRepository<S> sessions;
|
||||
|
||||
public SpringSessionWebSessionStore(ReactiveSessionRepository<S> reactiveSessionRepository) {
|
||||
this.sessions = reactiveSessionRepository;
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
In order to be detected by Spring WebFlux, this custom `WebSessionStore` needs to be registered with `ApplicationContext` as bean named `webSessionManager`.
|
||||
For additional information on Spring WebFlux, refer to the https://docs.spring.io/spring/docs/{spring-framework-version}/spring-framework-reference/web-reactive.html[Spring Framework Reference Documentation].
|
||||
|
||||
[[spring-security]]
|
||||
== Spring Security Integration
|
||||
|
||||
@@ -356,7 +492,7 @@ Spring Session provides integration with Spring Security.
|
||||
[[spring-security-rememberme]]
|
||||
=== Spring Security Remember-Me Support
|
||||
|
||||
Spring Session provides integration with http://docs.spring.io/spring-security/site/docs/4.2.x/reference/htmlsingle/#remember-me[Spring Security's Remember-Me Authentication].
|
||||
Spring Session provides integration with https://docs.spring.io/spring-security/site/docs/{spring-security-version}/reference/htmlsingle/#remember-me[Spring Security's Remember-Me Authentication].
|
||||
The support will:
|
||||
|
||||
* Change the session expiration length
|
||||
@@ -502,6 +638,14 @@ Once the session is indexed, it can be found using the following:
|
||||
include::{docs-test-dir}docs/FindByIndexNameSessionRepositoryTests.java[tags=findby-username]
|
||||
----
|
||||
|
||||
[[api-reactivesessionrepository]]
|
||||
=== ReactiveSessionRepository
|
||||
|
||||
A `ReactiveSessionRepository` is in charge of creating, retrieving, and persisting `Session` instances in a non-blocking and reactive manner.
|
||||
|
||||
If possible, developers should not interact directly with a `ReactiveSessionRepository` or a `Session`.
|
||||
Instead, developers should prefer interacting with `ReactiveSessionRepository` and `Session` indirectly through the <<websession,WebSession>> integration.
|
||||
|
||||
[[api-enablespringhttpsession]]
|
||||
=== EnableSpringHttpSession
|
||||
|
||||
@@ -518,6 +662,22 @@ It is important to note that no infrastructure for session expirations is config
|
||||
This is because things like session expiration are highly implementation dependent.
|
||||
This means if you require cleaning up expired sessions, you are responsible for cleaning up the expired sessions.
|
||||
|
||||
[[api-enablespringwebsession]]
|
||||
=== EnableSpringWebSession
|
||||
|
||||
The `@EnableSpringWebSession` annotation can be added to an `@Configuration` class to expose the `WebSessionManager` as a bean named "webSessionManager".
|
||||
In order to leverage the annotation, a single `ReactiveSessionRepository` bean must be provided.
|
||||
For example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{docs-test-dir}docs/SpringWebSessionConfig.java[tags=class]
|
||||
----
|
||||
|
||||
It is important to note that no infrastructure for session expirations is configured for you out of the box.
|
||||
This is because things like session expiration are highly implementation dependent.
|
||||
This means if you require cleaning up expired sessions, you are responsible for cleaning up the expired sessions.
|
||||
|
||||
[[api-redisoperationssessionrepository]]
|
||||
=== RedisOperationsSessionRepository
|
||||
|
||||
@@ -545,7 +705,7 @@ Complete example usage can be found in the <<samples>>
|
||||
You can use the following attributes to customize the configuration:
|
||||
|
||||
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds
|
||||
* **redisNamespace** - allows configuring an application specific namespace for the sessions. Redis keys and channel ids will start with the prefix of `spring:session:<redisNamespace>:`.
|
||||
* **redisNamespace** - allows configuring an application specific namespace for the sessions. Redis keys and channel IDs will start with the prefix of `<redisNamespace>:`.
|
||||
* **redisFlushMode** - allows specifying when data will be written to Redis. The default is only when `save` is invoked on `SessionRepository`.
|
||||
A value of `RedisFlushMode.IMMEDIATE` will write to Redis as soon as possible.
|
||||
|
||||
@@ -557,7 +717,7 @@ You can customize the serialization by creating a Bean named `springSessionDefau
|
||||
|
||||
`RedisOperationsSessionRepository` is subscribed to receive events from redis using a `RedisMessageListenerContainer`.
|
||||
You can customize the way those events are dispatched, by creating a Bean named `springSessionRedisTaskExecutor` and/or a Bean `springSessionRedisSubscriptionExecutor`.
|
||||
More details on configuring redis task executors can be found http://docs.spring.io/spring-data-redis/docs/current/reference/html/#redis:pubsub:subscribe:containers[here].
|
||||
More details on configuring redis task executors can be found https://docs.spring.io/spring-data-redis/docs/{spring-data-redis-version}/reference/html/#redis:pubsub:subscribe:containers[here].
|
||||
|
||||
[[api-redisoperationssessionrepository-storage]]
|
||||
==== Storage Details
|
||||
@@ -596,7 +756,7 @@ HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime
|
||||
|
||||
In this example, the session following statements are true about the session:
|
||||
|
||||
* The session id is 33fdd1b6-b496-4b33-9f7d-df96679d32fe
|
||||
* The session ID is 33fdd1b6-b496-4b33-9f7d-df96679d32fe
|
||||
* The session was created at 1404360000000 in milliseconds since midnight of 1/1/1970 GMT.
|
||||
* The session expires in 1800 seconds (30 minutes).
|
||||
* The session was last accessed at 1404360000000 in milliseconds since midnight of 1/1/1970 GMT.
|
||||
@@ -636,7 +796,7 @@ The `SessionRepository.findById(String)` method ensures that no expired sessions
|
||||
This means there is no need to check the expiration before using a session.
|
||||
====
|
||||
|
||||
Spring Session relies on the delete and expired http://redis.io/topics/notifications[keyspace notifications] from Redis to fire a <<api-redisoperationssessionrepository-sessiondestroyedevent,SessionDeletedEvent>> and <<api-redisoperationssessionrepository-sessiondestroyedevent,SessionExpiredEvent>> respectively.
|
||||
Spring Session relies on the delete and expired https://redis.io/topics/notifications[keyspace notifications] from Redis to fire a <<api-redisoperationssessionrepository-sessiondestroyedevent,SessionDeletedEvent>> and <<api-redisoperationssessionrepository-sessiondestroyedevent,SessionExpiredEvent>> respectively.
|
||||
It is the `SessionDeletedEvent` or `SessionExpiredEvent` that ensures resources associated with the Session are cleaned up.
|
||||
For example, when using Spring Session's WebSocket support the Redis expired or delete event is what triggers any WebSocket connections associated with the session to be closed.
|
||||
|
||||
@@ -651,7 +811,7 @@ When a session expires key is deleted or expires, the keyspace notification trig
|
||||
|
||||
One problem with relying on Redis expiration exclusively is that Redis makes no guarantee of when the expired event will be fired if the key has not been accessed.
|
||||
Specifically the background task that Redis uses to clean up expired keys is a low priority task and may not trigger the key expiration.
|
||||
For additional details see http://redis.io/topics/notifications[Timing of expired events] section in the Redis documentation.
|
||||
For additional details see https://redis.io/topics/notifications[Timing of expired events] section in the Redis documentation.
|
||||
|
||||
To circumvent the fact that expired events are not guaranteed to happen we can ensure that each key is accessed when it is expected to expire.
|
||||
This means that if the TTL is expired on the key, Redis will remove the key and fire the expired event when we try to access the key.
|
||||
@@ -686,7 +846,7 @@ This is necessary to ensure resources associated with the `Session` are properly
|
||||
|
||||
For example, when integrating with WebSockets the `SessionDestroyedEvent` is in charge of closing any active WebSocket connections.
|
||||
|
||||
Firing `SessionDeletedEvent` or `SessionExpiredEvent` is made available through the `SessionMessageListener` which listens to http://redis.io/topics/notifications[Redis Keyspace events].
|
||||
Firing `SessionDeletedEvent` or `SessionExpiredEvent` is made available through the `SessionMessageListener` which listens to https://redis.io/topics/notifications[Redis Keyspace events].
|
||||
In order for this to work, Redis Keyspace events for Generic commands and Expired events needs to be enabled.
|
||||
For example:
|
||||
|
||||
@@ -718,14 +878,14 @@ include::{docs-test-resources-dir}docs/HttpSessionConfigurationNoOpConfigureRedi
|
||||
==== SessionCreatedEvent
|
||||
|
||||
When a session is created an event is sent to Redis with the channel of `spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe`
|
||||
such that `33fdd1b6-b496-4b33-9f7d-df96679d32fe` is the session id. The body of the event will be the session that was created.
|
||||
such that `33fdd1b6-b496-4b33-9f7d-df96679d32fe` is the session ID. The body of the event will be the session that was created.
|
||||
|
||||
If registered as a MessageListener (default), then `RedisOperationsSessionRepository` will then translate the Redis message into a `SessionCreatedEvent`.
|
||||
|
||||
[[api-redisoperationssessionrepository-cli]]
|
||||
==== Viewing the Session in Redis
|
||||
|
||||
After http://redis.io/topics/quickstart[installing redis-cli], you can inspect the values in Redis http://redis.io/commands#hash[using the redis-cli].
|
||||
After https://redis.io/topics/quickstart[installing redis-cli], you can inspect the values in Redis https://redis.io/commands#hash[using the redis-cli].
|
||||
For example, enter the following into a terminal:
|
||||
|
||||
[source,bash]
|
||||
@@ -737,7 +897,70 @@ redis 127.0.0.1:6379> keys *
|
||||
----
|
||||
|
||||
<1> The suffix of this key is the session identifier of the Spring Session.
|
||||
<2> This key contains all the session ids that should be deleted at the time `1418772300000`.
|
||||
<2> This key contains all the session IDs that should be deleted at the time `1418772300000`.
|
||||
|
||||
You can also view the attributes of each session.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
redis 127.0.0.1:6379> hkeys spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021
|
||||
1) "lastAccessedTime"
|
||||
2) "creationTime"
|
||||
3) "maxInactiveInterval"
|
||||
4) "sessionAttr:username"
|
||||
redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021 sessionAttr:username
|
||||
"\xac\xed\x00\x05t\x00\x03rob"
|
||||
----
|
||||
|
||||
[[api-reactiveredisoperationssessionrepository]]
|
||||
=== ReactiveRedisOperationsSessionRepository
|
||||
|
||||
`ReactiveRedisOperationsSessionRepository` is a `ReactiveSessionRepository` that is implemented using Spring Data's `ReactiveRedisOperations`.
|
||||
In a web environment, this is typically used in combination with `WebSessionStore`.
|
||||
|
||||
[[api-reactiveredisoperationssessionrepository-new]]
|
||||
==== Instantiating a ReactiveRedisOperationsSessionRepository
|
||||
|
||||
A typical example of how to create a new instance can be seen below:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{indexdoc-tests}[tags=new-reactiveredisoperationssessionrepository]
|
||||
----
|
||||
|
||||
For additional information on how to create a `ReactiveRedisConnectionFactory`, refer to the Spring Data Redis Reference.
|
||||
|
||||
[[api-reactiveredisoperationssessionrepository-config]]
|
||||
==== EnableRedisWebSession
|
||||
|
||||
In a web environment, the simplest way to create a new `ReactiveRedisOperationsSessionRepository` is to use `@EnableRedisWebSession`.
|
||||
You can use the following attributes to customize the configuration:
|
||||
|
||||
* **maxInactiveIntervalInSeconds** - the amount of time before the session will expire in seconds
|
||||
* **redisNamespace** - allows configuring an application specific namespace for the sessions. Redis keys and channel IDs will start with the prefix of `<redisNamespace>:`.
|
||||
* **redisFlushMode** - allows specifying when data will be written to Redis. The default is only when `save` is invoked on `ReactiveSessionRepository`.
|
||||
A value of `RedisFlushMode.IMMEDIATE` will write to Redis as soon as possible.
|
||||
|
||||
[[api-reactiveredisoperationssessionrepository-writes]]
|
||||
===== Optimized Writes
|
||||
|
||||
The `Session` instances managed by `ReactiveRedisOperationsSessionRepository` keeps track of the properties that have changed and only updates those.
|
||||
This means if an attribute is written once and read many times we only need to write that attribute once.
|
||||
|
||||
[[api-reactiveredisoperationssessionrepository-cli]]
|
||||
==== Viewing the Session in Redis
|
||||
|
||||
After https://redis.io/topics/quickstart[installing redis-cli], you can inspect the values in Redis https://redis.io/commands#hash[using the redis-cli].
|
||||
For example, enter the following into a terminal:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ redis-cli
|
||||
redis 127.0.0.1:6379> keys *
|
||||
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" <1>
|
||||
----
|
||||
|
||||
<1> The suffix of this key is the session identifier of the Spring Session.
|
||||
|
||||
You can also view the attributes of each session.
|
||||
|
||||
@@ -755,7 +978,7 @@ redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed
|
||||
[[api-mapsessionrepository]]
|
||||
=== MapSessionRepository
|
||||
|
||||
The `MapSessionRepository` allows for persisting `Session` in a `Map` with the key being the `Session` id and the value being the `Session`.
|
||||
The `MapSessionRepository` allows for persisting `Session` in a `Map` with the key being the `Session` ID and the value being the `Session`.
|
||||
The implementation can be used with a `ConcurrentHashMap` as a testing or convenience mechanism.
|
||||
Alternatively, it can be used with distributed `Map` implementations. For example, it can be used with Hazelcast.
|
||||
|
||||
@@ -786,6 +1009,13 @@ To run it use the following:
|
||||
|
||||
./gradlew :samples:hazelcast-spring:tomcatRun
|
||||
|
||||
[[api-reactivemapsessionrepository]]
|
||||
=== ReactiveMapSessionRepository
|
||||
|
||||
The `ReactiveMapSessionRepository` allows for persisting `Session` in a `Map` with the key being the `Session` ID and the value being the `Session`.
|
||||
The implementation can be used with a `ConcurrentHashMap` as a testing or convenience mechanism.
|
||||
Alternatively, it can be used with distributed `Map` implementations with the requirement that the supplied `Map` must be a non-blocking.
|
||||
|
||||
[[api-jdbcoperationssessionrepository]]
|
||||
=== JdbcOperationsSessionRepository
|
||||
|
||||
@@ -803,7 +1033,7 @@ A typical example of how to create a new instance can be seen below:
|
||||
include::{indexdoc-tests}[tags=new-jdbcoperationssessionrepository]
|
||||
----
|
||||
|
||||
For additional information on how to create and configure `JdbcTemplate` and `PlatformTransactionManager`, refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-data-tier.html[Spring Framework Reference Documentation].
|
||||
For additional information on how to create and configure `JdbcTemplate` and `PlatformTransactionManager`, refer to the https://docs.spring.io/spring/docs/{spring-framework-version}/spring-framework-reference/data-access.html[Spring Framework Reference Documentation].
|
||||
|
||||
[[api-jdbcoperationssessionrepository-config]]
|
||||
==== EnableJdbcHttpSession
|
||||
@@ -870,12 +1100,12 @@ A typical example of how to create a new instance can be seen below:
|
||||
include::{indexdoc-tests}[tags=new-hazelcastsessionrepository]
|
||||
----
|
||||
|
||||
For additional information on how to create and configure Hazelcast instance, refer to the http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#hazelcast-configuration[Hazelcast documentation].
|
||||
For additional information on how to create and configure Hazelcast instance, refer to the http://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#hazelcast-configuration[Hazelcast documentation].
|
||||
|
||||
[[api-enablehazelcasthttpsession]]
|
||||
==== EnableHazelcastHttpSession
|
||||
|
||||
If you wish to use http://hazelcast.org/[Hazelcast] as your backing source for the `SessionRepository`, then the `@EnableHazelcastHttpSession` annotation
|
||||
If you wish to use https://hazelcast.org/[Hazelcast] as your backing source for the `SessionRepository`, then the `@EnableHazelcastHttpSession` annotation
|
||||
can be added to an `@Configuration` class. This extends the functionality provided by the `@EnableSpringHttpSession` annotation but makes the `SessionRepository` for you in Hazelcast.
|
||||
You must provide a single `HazelcastInstance` bean for the configuration to work.
|
||||
Complete configuration example can be found in the <<samples>>
|
||||
@@ -907,6 +1137,70 @@ Note that if you use Hazelcast's `MapStore` to persist your sessions `IMap` ther
|
||||
* reload triggers `EntryAddedListener` which results in `SessionCreatedEvent` being re-published
|
||||
* reload uses default TTL for a given `IMap` which results in sessions losing their original TTL
|
||||
|
||||
[[custom-sessionrepository]]
|
||||
== Custom SessionRepository
|
||||
|
||||
Implementing a custom <<api-sessionrepository,`SessionRepository`>> API should be a fairly straightforward task.
|
||||
Coupling the custom implementation with <<api-enablespringhttpsession,`@EnableSpringHttpSession`>> support allow to easily reuse existing Spring Session configuration facilities and infrastructure.
|
||||
There are however a couple of aspects that deserve a closer consideration.
|
||||
|
||||
During a lifecycle of an HTTP request, the `HttpSession` is typically is persisted to `SessionRepository` twice.
|
||||
First to ensure that the session is available to the clients as soon as the client has access to the session ID, and it is also necessary to write after the session is committed because further modifications to the session might be made.
|
||||
Having this in mind, it is generally recommended for a `SessionRepository` implementation to keep track of changes to ensure that only deltas are saved.
|
||||
This is in particular very important in highly concurrent environments, where multiple requests operate on the same `HttpSession` and therefore cause race conditions, with requests overriding each others changes to session attributes.
|
||||
All of the `SessionRepository` implementations provided by Spring Session use the described approach to persisting session changes and can be used for guidance while implementing custom `SessionRepository`.
|
||||
|
||||
Note that the same recommendations apply for implementing a custom <<api-reactivesessionrepository,`ReactiveSessionRepository`>> as well.
|
||||
Of course, in this case the <<api-enablespringwebsession,`@EnableSpringWebSession`>> should be used.
|
||||
|
||||
[[upgrading-2.0]]
|
||||
== Upgrading to 2.x
|
||||
|
||||
With the new major release version, the Spring Session team took the opportunity to make some non-passive changes.
|
||||
The focus of these changes is to improve and harmonize Spring Session's APIs, as well as remove the deprecated components.
|
||||
|
||||
=== Baseline update
|
||||
|
||||
Spring Session 2.0 requires Java 8 and Spring Framework 5.0 as a baseline, since its entire codebase is now based on Java 8 source code.
|
||||
Refer to guide for https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-5.x[Upgrading to Spring Framework 5.x] for reference on upgrading Spring Framework.
|
||||
|
||||
=== Replaced and Removed Modules
|
||||
|
||||
As a part of the project's split the modules, the existing `spring-session` has been replaced with `spring-session-core` module.
|
||||
The `spring-session-core` module holds only the common set of APIs and components while other modules contain the implementation of appropriate `SessionRepository` and functionality related to that data store.
|
||||
This applies to several existing that were previously a simple dependency aggregator helper modules but with new module arrangement actually carry the implementation:
|
||||
|
||||
* Spring Session Data Redis
|
||||
* Spring Session JDBC
|
||||
* Spring Session Hazelcast
|
||||
|
||||
Also the following modules were removed from the main project repository:
|
||||
|
||||
* Spring Session Data MongoDB
|
||||
* Spring Session Data GemFire
|
||||
|
||||
Note that these two have moved to separate repositories, and will continue to be available albeit under a changed artifact names:
|
||||
|
||||
* https://github.com/spring-projects/spring-session-data-mongodb[`spring-session-data-mongodb`]
|
||||
* https://github.com/spring-projects/spring-session-data-geode[`spring-session-data-geode`]
|
||||
|
||||
=== Replaced and Removed Packages, Classes and Methods
|
||||
|
||||
* `ExpiringSession` API has been merged into `Session` API
|
||||
* `Session` API has been enhanced to make full use of Java 8
|
||||
* `Session` API has been extended with `changeSessionId` support
|
||||
* `SessionRepository` API has been updated to better align with Spring Data method naming conventions
|
||||
* `AbstractSessionEvent` and its subclasses are no longer constructable without an underlying `Session` object
|
||||
* Redis namespace used by `RedisOperationsSessionRepository` is now fully configurable, instead of being partial configurable
|
||||
* Redis configuration support has been updated to avoid registering a Spring Session specific `RedisTemplate` bean
|
||||
* JDBC configuration support has been updated to avoid registering a Spring Session specific `JdbcTemplate` bean
|
||||
* Previously deprecated classes and methods have been removed across the codebase
|
||||
|
||||
=== Dropped Support
|
||||
|
||||
As a part of the changes to `HttpSessionStrategy` and it's alignment to the counterpart from the reactive world, the support for managing multiple users' sessions in a single browser instance has been removed.
|
||||
This introduction of new API to replace this functionality in consideration for future releases.
|
||||
|
||||
[[community]]
|
||||
== Spring Session Community
|
||||
|
||||
@@ -916,18 +1210,18 @@ Please find additional information below.
|
||||
[[community-support]]
|
||||
=== Support
|
||||
|
||||
You can get help by asking questions on http://stackoverflow.com/questions/tagged/spring-session[StackOverflow with the tag spring-session].
|
||||
You can get help by asking questions on https://stackoverflow.com/questions/tagged/spring-session[StackOverflow with the tag spring-session].
|
||||
Similarly we encourage helping others by answering questions on StackOverflow.
|
||||
|
||||
[[community-source]]
|
||||
=== Source Code
|
||||
|
||||
Our source code can be found on github at https://github.com/spring-projects/spring-session/
|
||||
Our source code can be found on GitHub at https://github.com/spring-projects/spring-session/
|
||||
|
||||
[[community-issues]]
|
||||
=== Issue Tracking
|
||||
|
||||
We track issues in github issues at https://github.com/spring-projects/spring-session/issues
|
||||
We track issues in GitHub issues at https://github.com/spring-projects/spring-session/issues
|
||||
|
||||
[[community-contributing]]
|
||||
=== Contributing
|
||||
@@ -937,7 +1231,7 @@ We appreciate https://help.github.com/articles/using-pull-requests/[Pull Request
|
||||
[[community-license]]
|
||||
=== 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[Apache 2.0 license].
|
||||
|
||||
[[community-extensions]]
|
||||
=== Community Extensions
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -33,7 +33,7 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class HttpSessionConfigurationNoOpConfigureRedisActionXmlTests {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,17 +22,22 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import com.hazelcast.config.Config;
|
||||
import com.hazelcast.core.Hazelcast;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.core.IMap;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.ReactiveRedisTemplate;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.hazelcast.HazelcastSessionRepository;
|
||||
import org.springframework.session.jdbc.JdbcOperationsSessionRepository;
|
||||
@@ -112,12 +117,35 @@ public class IndexDocTests {
|
||||
@SuppressWarnings("unused")
|
||||
public void newRedisOperationsSessionRepository() {
|
||||
// tag::new-redisoperationssessionrepository[]
|
||||
LettuceConnectionFactory factory = new LettuceConnectionFactory();
|
||||
SessionRepository<? extends Session> repository = new RedisOperationsSessionRepository(
|
||||
factory);
|
||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
|
||||
// ... configure redisTemplate ...
|
||||
|
||||
SessionRepository<? extends Session> repository =
|
||||
new RedisOperationsSessionRepository(redisTemplate);
|
||||
// end::new-redisoperationssessionrepository[]
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void newReactiveRedisOperationsSessionRepository() {
|
||||
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory();
|
||||
RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext
|
||||
.<String, Object>newSerializationContext(
|
||||
new JdkSerializationRedisSerializer())
|
||||
.build();
|
||||
|
||||
// tag::new-reactiveredisoperationssessionrepository[]
|
||||
// ... create and configure connectionFactory and serializationContext ...
|
||||
|
||||
ReactiveRedisTemplate<String, Object> redisTemplate = new ReactiveRedisTemplate<>(
|
||||
connectionFactory, serializationContext);
|
||||
|
||||
ReactiveSessionRepository<? extends Session> repository =
|
||||
new ReactiveRedisOperationsSessionRepository(redisTemplate);
|
||||
// end::new-reactiveredisoperationssessionrepository[]
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void mapRepository() {
|
||||
@@ -155,11 +183,8 @@ public class IndexDocTests {
|
||||
|
||||
HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
|
||||
|
||||
IMap<String, MapSession> sessions = hazelcastInstance
|
||||
.getMap("spring:session:sessions");
|
||||
|
||||
HazelcastSessionRepository repository =
|
||||
new HazelcastSessionRepository(sessions);
|
||||
new HazelcastSessionRepository(hazelcastInstance);
|
||||
// end::new-hazelcastsessionrepository[]
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.ConfigureRedisAction;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -33,7 +33,7 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RedisHttpSessionConfigurationNoOpConfigureRedisActionTests {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,21 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
package docs;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
|
||||
import org.springframework.session.ReactiveMapSessionRepository;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
|
||||
|
||||
@Import(EmbeddedRedisConfig.class)
|
||||
// tag::class[]
|
||||
@EnableRedisWebSession
|
||||
public class HelloWebfluxSessionConfig {
|
||||
|
||||
@EnableSpringWebSession
|
||||
public class SpringWebSessionConfig {
|
||||
@Bean
|
||||
public LettuceConnectionFactory lettuceConnectionFactory() {
|
||||
return new LettuceConnectionFactory();
|
||||
public ReactiveSessionRepository reactiveSessionRepository() {
|
||||
return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
|
||||
}
|
||||
}
|
||||
// end::class[]
|
||||
@@ -29,7 +29,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.security.core.session.SessionDestroyedEvent;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -42,7 +42,7 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Mark Paluch
|
||||
* @since 1.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@WebAppConfiguration
|
||||
public abstract class AbstractHttpSessionListenerTests {
|
||||
@Autowired
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,7 +41,7 @@ public class HazelcastHttpSessionConfig {
|
||||
|
||||
Config config = new Config();
|
||||
|
||||
config.getMapConfig("spring:session:sessions") // <2>
|
||||
config.getMapConfig(HazelcastSessionRepository.DEFAULT_SESSION_MAP_NAME) // <2>
|
||||
.addMapAttributeConfig(attributeConfig)
|
||||
.addMapIndexConfig(new MapIndexConfig(
|
||||
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
|
||||
|
||||
@@ -67,10 +67,8 @@ public class RememberMeSecurityConfiguration extends WebSecurityConfigurerAdapte
|
||||
@Override
|
||||
@Bean
|
||||
public InMemoryUserDetailsManager userDetailsService() {
|
||||
InMemoryUserDetailsManager uds = new InMemoryUserDetailsManager();
|
||||
uds.createUser(
|
||||
User.withUsername("user").password("{noop}password").roles("USER").build());
|
||||
return uds;
|
||||
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
@@ -44,7 +44,7 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv
|
||||
/**
|
||||
* @author rwinch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = RememberMeSecurityConfiguration.class)
|
||||
@WebAppConfiguration
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
@@ -44,7 +44,7 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv
|
||||
/**
|
||||
* @author rwinch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<suppressions>
|
||||
<suppress files=".+Application\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files=".+Configuration\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files=".+SSEFluxWebConfig\.java" checks="RegexpHeader"/>
|
||||
|
||||
<suppress files="[\\/]src[\\/]test[\\/]java[\\/]" checks="Javadoc"/>
|
||||
<suppress files="[\\/]src[\\/]integration-test[\\/]java[\\/]" checks="Javadoc"/>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
springBootVersion=2.0.0.M5
|
||||
version=2.0.0.RC1
|
||||
springBootVersion=2.0.0.M7
|
||||
version=2.0.1.RELEASE
|
||||
|
||||
@@ -1,55 +1,39 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.2'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR3'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.0.1.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR1'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.0.0.RC1'
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.4'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR5'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.0.3.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR3'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.0.1.RELEASE'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.9') {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.9.2') {
|
||||
entry 'hazelcast'
|
||||
entry 'hazelcast-client'
|
||||
}
|
||||
|
||||
dependencySet(group: 'org.testcontainers', version: '1.4.3') {
|
||||
dependencySet(group: 'org.testcontainers', version: '1.5.1') {
|
||||
entry 'mariadb'
|
||||
entry 'mssqlserver'
|
||||
entry 'mysql'
|
||||
entry 'postgresql'
|
||||
entry 'testcontainers'
|
||||
}
|
||||
|
||||
dependency 'ch.qos.logback:logback-classic:1.2.3'
|
||||
dependency 'com.h2database:h2:1.4.196'
|
||||
dependency 'com.maxmind.geoip2:geoip2:2.3.1'
|
||||
dependency 'commons-codec:commons-codec:1.11'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:6.2.2.jre8'
|
||||
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
|
||||
dependency 'io.lettuce:lettuce-core:5.0.0.RELEASE'
|
||||
dependency 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1'
|
||||
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
|
||||
dependency 'io.lettuce:lettuce-core:5.0.1.RELEASE'
|
||||
dependency 'javax.servlet:javax.servlet-api:3.1.0'
|
||||
dependency 'junit:junit:4.12'
|
||||
dependency 'mysql:mysql-connector-java:5.1.44'
|
||||
dependency 'mysql:mysql-connector-java:5.1.45'
|
||||
dependency 'org.apache.derby:derby:10.14.1.0'
|
||||
dependency 'org.apache.httpcomponents:httpclient:4.5.3'
|
||||
dependency 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
|
||||
dependency 'org.assertj:assertj-core:3.8.0'
|
||||
dependency 'org.assertj:assertj-core:3.9.0'
|
||||
dependency 'org.hsqldb:hsqldb:2.4.0'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
|
||||
dependency 'org.mockito:mockito-core:2.11.0'
|
||||
dependency 'org.postgresql:postgresql:42.1.4'
|
||||
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.27'
|
||||
dependency 'org.slf4j:jcl-over-slf4j:1.7.25'
|
||||
dependency 'org.slf4j:log4j-over-slf4j:1.7.25'
|
||||
dependency 'org.thymeleaf.extras:thymeleaf-extras-java8time:3.0.1.RELEASE'
|
||||
dependency 'org.thymeleaf:thymeleaf-spring5:3.0.8.RELEASE'
|
||||
dependency 'org.webjars:bootstrap:2.3.2'
|
||||
dependency 'org.webjars:html5shiv:3.7.3'
|
||||
dependency 'org.webjars:jquery:1.12.4'
|
||||
dependency 'org.webjars:knockout:2.3.0'
|
||||
dependency 'org.webjars:sockjs-client:0.3.4'
|
||||
dependency 'org.webjars:stomp-websocket:2.3.0'
|
||||
dependency 'org.webjars:webjars-taglib:0.3'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.2.1'
|
||||
dependency 'org.mockito:mockito-core:2.13.0'
|
||||
dependency 'org.postgresql:postgresql:42.2.0'
|
||||
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.29.0'
|
||||
}
|
||||
}
|
||||
|
||||
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
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,7 +49,7 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
|
||||
@ContextConfiguration(initializers = FindByUsernameTests.Initializer.class)
|
||||
public class FindByUsernameTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@ClassRule
|
||||
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
|
||||
@@ -37,10 +37,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Bean
|
||||
@Override
|
||||
public UserDetailsService userDetailsService() {
|
||||
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
|
||||
manager.createUser(
|
||||
User.withUsername("user").password("password").roles("USER").build());
|
||||
return manager;
|
||||
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
||||
@@ -24,7 +24,7 @@ import sample.config.GeoConfig;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = GeoConfig.class)
|
||||
public class SessionDetailsFilterTests {
|
||||
@Autowired
|
||||
|
||||
@@ -38,10 +38,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Bean
|
||||
@Override
|
||||
public UserDetailsService userDetailsService() {
|
||||
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
|
||||
manager.createUser(
|
||||
User.withUsername("user").password("password").roles("USER").build());
|
||||
return manager;
|
||||
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -53,7 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ContextConfiguration(initializers = HttpRedisJsonTest.Initializer.class)
|
||||
public class HttpRedisJsonTest {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@ClassRule
|
||||
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ContextConfiguration(initializers = RedisSerializerTest.Initializer.class)
|
||||
public class RedisSerializerTest {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@ClassRule
|
||||
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
|
||||
@@ -37,10 +37,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Bean
|
||||
@Override
|
||||
public UserDetailsService userDetailsService() {
|
||||
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
|
||||
manager.createUser(
|
||||
User.withUsername("user").password("password").roles("USER").build());
|
||||
return manager;
|
||||
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,7 +48,7 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
|
||||
@ContextConfiguration(initializers = BootTests.Initializer.class)
|
||||
public class BootTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@ClassRule
|
||||
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
|
||||
@@ -37,10 +37,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Bean
|
||||
@Override
|
||||
public UserDetailsService userDetailsService() {
|
||||
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
|
||||
manager.createUser(
|
||||
User.withUsername("user").password("password").roles("USER").build());
|
||||
return manager;
|
||||
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-boot'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework.boot:spring-boot-starter-webflux"
|
||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf"
|
||||
compile "org.springframework.boot:spring-boot-starter-data-redis"
|
||||
compile "org.springframework.boot:spring-boot-devtools"
|
||||
compile 'org.webjars:bootstrap'
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
|
||||
integrationTestCompile seleniumDependencies
|
||||
integrationTestCompile "org.testcontainers:testcontainers"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,21 +16,26 @@
|
||||
|
||||
package sample;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import sample.pages.HomePage;
|
||||
import sample.pages.HomePage.Attribute;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -40,11 +45,18 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = HelloWebfluxApplication.class)
|
||||
@TestPropertySource(properties = { "spring.profiles.active=embedded-redis", "server.port=0" })
|
||||
@SpringBootTest(classes = HelloWebFluxApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@ContextConfiguration(initializers = AttributeTests.Initializer.class)
|
||||
public class AttributeTests {
|
||||
@Value("#{@nettyContext.address().getPort()}")
|
||||
int port;
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@ClassRule
|
||||
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
.withExposedPorts(6379);
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
@@ -87,4 +99,18 @@ public class AttributeTests {
|
||||
assertThat(row.getAttributeValue()).isEqualTo("b");
|
||||
}
|
||||
|
||||
static class Initializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
@Override
|
||||
public void initialize(
|
||||
ConfigurableApplicationContext configurableApplicationContext) {
|
||||
TestPropertyValues
|
||||
.of("spring.redis.host=" + redisContainer.getContainerIpAddress(),
|
||||
"spring.redis.port=" + redisContainer.getFirstMappedPort())
|
||||
.applyTo(configurableApplicationContext.getEnvironment());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class HelloWebFluxApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(HelloWebFluxApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
BIN
samples/boot/webflux/src/main/resources/static/favicon.ico
Normal file
BIN
samples/boot/webflux/src/main/resources/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -37,10 +37,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Bean
|
||||
@Override
|
||||
public UserDetailsService userDetailsService() {
|
||||
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
|
||||
manager.createUser(
|
||||
User.withUsername("user").password("password").roles("USER").build());
|
||||
return manager;
|
||||
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -55,7 +55,7 @@ import org.springframework.web.socket.sockjs.client.WebSocketTransport;
|
||||
@ContextConfiguration(initializers = ApplicationTests.Initializer.class)
|
||||
public class ApplicationTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@ClassRule
|
||||
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
|
||||
18
samples/gradle/dependency-management.gradle
Normal file
18
samples/gradle/dependency-management.gradle
Normal file
@@ -0,0 +1,18 @@
|
||||
dependencyManagement {
|
||||
dependencies {
|
||||
dependency 'ch.qos.logback:logback-classic:1.2.3'
|
||||
dependency 'com.maxmind.geoip2:geoip2:2.3.1'
|
||||
dependency 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1'
|
||||
dependency 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02'
|
||||
dependency 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
|
||||
dependency 'org.slf4j:jcl-over-slf4j:1.7.25'
|
||||
dependency 'org.slf4j:log4j-over-slf4j:1.7.25'
|
||||
dependency 'org.webjars:bootstrap:2.3.2'
|
||||
dependency 'org.webjars:html5shiv:3.7.3'
|
||||
dependency 'org.webjars:jquery:1.12.4'
|
||||
dependency 'org.webjars:knockout:2.3.0'
|
||||
dependency 'org.webjars:sockjs-client:0.3.4'
|
||||
dependency 'org.webjars:stomp-websocket:2.3.0'
|
||||
dependency 'org.webjars:webjars-taglib:0.3'
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@Bean(initMethod = "start")
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -19,6 +19,7 @@ package sample;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
@@ -27,6 +28,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
public class SecurityConfig {
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
|
||||
auth.inMemoryAuthentication().withUser(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -57,7 +57,7 @@ public class SessionConfig {
|
||||
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
|
||||
.setExtractor(PrincipalNameExtractor.class.getName());
|
||||
|
||||
config.getMapConfig("spring:session:sessions")
|
||||
config.getMapConfig(HazelcastSessionRepository.DEFAULT_SESSION_MAP_NAME)
|
||||
.addMapAttributeConfig(attributeConfig)
|
||||
.addMapIndexConfig(new MapIndexConfig(
|
||||
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@Bean(initMethod = "start")
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,7 +35,7 @@ import org.springframework.session.web.http.HeaderHttpSessionIdResolver;
|
||||
import org.springframework.session.web.http.HttpSessionIdResolver;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
@@ -49,13 +49,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = { RestMockMvcTests.Config.class, SecurityConfig.class,
|
||||
MvcConfig.class })
|
||||
@WebAppConfiguration
|
||||
public class RestMockMvcTests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@ClassRule
|
||||
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@Bean(initMethod = "start")
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
|
||||
@EnableWebSecurity
|
||||
@@ -40,12 +41,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("{noop}password").roles("USER");
|
||||
auth.inMemoryAuthentication().withUser(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@Bean(initMethod = "start")
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -19,6 +19,7 @@ package sample;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
@@ -27,6 +28,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
public class SecurityConfig {
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication().withUser("user").password("{noop}password").roles("USER");
|
||||
auth.inMemoryAuthentication().withUser(User.withDefaultPasswordEncoder()
|
||||
.username("user").password("password").roles("USER").build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-redis')
|
||||
compile 'io.lettuce:lettuce-core'
|
||||
compile 'io.netty:netty-buffer'
|
||||
compile 'io.projectreactor.ipc:reactor-netty'
|
||||
compile 'org.springframework:spring-context'
|
||||
compile 'org.springframework:spring-web'
|
||||
compile 'org.springframework:spring-webflux'
|
||||
compile 'org.thymeleaf.extras:thymeleaf-extras-java8time'
|
||||
compile 'org.thymeleaf:thymeleaf-spring5'
|
||||
compile 'org.webjars:bootstrap'
|
||||
compile 'org.webjars:webjars-taglib'
|
||||
compile jstlDependencies
|
||||
compile slf4jDependencies
|
||||
compile 'org.testcontainers:testcontainers'
|
||||
|
||||
testCompile 'junit:junit'
|
||||
testCompile 'org.assertj:assertj-core'
|
||||
|
||||
integrationTestCompile seleniumDependencies
|
||||
}
|
||||
@@ -1,59 +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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
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 REDIS_DOCKER_IMAGE = "redis:4.0.2";
|
||||
|
||||
@Bean(initMethod = "start")
|
||||
public GenericContainer redisContainer() {
|
||||
return new GenericContainer(REDIS_DOCKER_IMAGE) {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
try {
|
||||
this.dockerClient.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}.withExposedPorts(6379);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public LettuceConnectionFactory redisConnectionFactory() {
|
||||
return new LettuceConnectionFactory(redisContainer().getContainerIpAddress(),
|
||||
redisContainer().getFirstMappedPort());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +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;
|
||||
|
||||
import reactor.ipc.netty.NettyContext;
|
||||
import reactor.ipc.netty.http.server.HttpServer;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
@ComponentScan
|
||||
public class HelloWebfluxApplication {
|
||||
@Value("${server.port:8080}")
|
||||
private int port = 8080;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
HelloWebfluxApplication.class)) {
|
||||
context.getBean(NettyContext.class).onClose().block();
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NettyContext nettyContext(ApplicationContext context) {
|
||||
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build();
|
||||
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
|
||||
HttpServer httpServer = HttpServer.create("localhost", this.port);
|
||||
return httpServer.newHandler(adapter).block();
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
*
|
||||
* Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org)
|
||||
*
|
||||
* 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.thymeleaf.spring5.ISpringWebFluxTemplateEngine;
|
||||
import org.thymeleaf.spring5.SpringWebFluxTemplateEngine;
|
||||
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
|
||||
import org.thymeleaf.spring5.view.reactive.ThymeleafReactiveViewResolver;
|
||||
import org.thymeleaf.templatemode.TemplateMode;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class SSEFluxWebConfig {
|
||||
|
||||
// TODO * Once there is a Spring Boot starter for thymeleaf-spring5, there would be no
|
||||
// need to have
|
||||
// TODO that @EnableConfigurationProperties annotation or use it for declaring the
|
||||
// beans down in the
|
||||
// TODO "thymeleaf" section below.
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public SSEFluxWebConfig(final ApplicationContext applicationContext) {
|
||||
super();
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* -------------------------------------- THYMELEAF CONFIGURATION
|
||||
* --------------------------------------
|
||||
*/
|
||||
|
||||
// TODO * If there was a Spring Boot starter for thymeleaf-spring5 most probably some
|
||||
// or all of these
|
||||
// TODO resolver and engine beans would not need to be specifically declared here.
|
||||
|
||||
@Bean
|
||||
public SpringResourceTemplateResolver thymeleafTemplateResolver() {
|
||||
|
||||
final SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
|
||||
resolver.setApplicationContext(this.applicationContext);
|
||||
resolver.setPrefix("classpath:/templates/");
|
||||
resolver.setSuffix(".html");
|
||||
resolver.setTemplateMode(TemplateMode.HTML);
|
||||
resolver.setCacheable(false);
|
||||
resolver.setCheckExistence(true);
|
||||
return resolver;
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ISpringWebFluxTemplateEngine thymeleafTemplateEngine() {
|
||||
// We override here the SpringTemplateEngine instance that would otherwise be
|
||||
// instantiated by
|
||||
// Spring Boot because we want to apply the SpringWebFlux-specific context
|
||||
// factory, link builder...
|
||||
final SpringWebFluxTemplateEngine templateEngine = new SpringWebFluxTemplateEngine();
|
||||
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
|
||||
return templateEngine;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThymeleafReactiveViewResolver thymeleafChunkedAndDataDrivenViewResolver() {
|
||||
final ThymeleafReactiveViewResolver viewResolver = new ThymeleafReactiveViewResolver();
|
||||
viewResolver.setTemplateEngine(thymeleafTemplateEngine());
|
||||
viewResolver.setOrder(1);
|
||||
viewResolver.setResponseMaxChunkSizeBytes(8192); // OUTPUT BUFFER size limit
|
||||
return viewResolver;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.config.ViewResolverRegistry;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@Configuration
|
||||
public class ThymeleafWebfluxConfig implements WebFluxConfigurer {
|
||||
|
||||
@Autowired(required = false)
|
||||
List<ViewResolver> views = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void configureViewResolvers(ViewResolverRegistry registry) {
|
||||
for (ViewResolver view : this.views) {
|
||||
registry.viewResolver(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@Bean(initMethod = "start")
|
||||
public GenericContainer redisContainer() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,7 +52,7 @@ public interface FindByIndexNameSessionRepository<S extends Session>
|
||||
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} and the value of
|
||||
* the specified principal name.
|
||||
*
|
||||
* @param indexName the name if the index (i.e.
|
||||
* @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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,8 +27,8 @@ import org.springframework.session.events.SessionExpiredEvent;
|
||||
/**
|
||||
* A {@link ReactiveSessionRepository} backed by a {@link Map} and that uses a
|
||||
* {@link MapSession}. The injected {@link java.util.Map} can be backed by a distributed
|
||||
* NoSQL store like Hazelcast, for instance. Note that the supplied map itself is
|
||||
* responsible for purging the expired sessions.
|
||||
* NoSQL store like Hazelcast, for instance. Note that the supplied map must be a
|
||||
* non-blocking map, and is itself responsible for purging the expired sessions.
|
||||
*
|
||||
* <p>
|
||||
* The implementation does NOT support firing {@link SessionDeletedEvent} or
|
||||
@@ -38,7 +38,7 @@ import org.springframework.session.events.SessionExpiredEvent;
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapReactiveSessionRepository implements ReactiveSessionRepository<MapSession> {
|
||||
public class ReactiveMapSessionRepository implements ReactiveSessionRepository<MapSession> {
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override
|
||||
@@ -54,7 +54,7 @@ public class MapReactiveSessionRepository implements ReactiveSessionRepository<M
|
||||
*
|
||||
* @param sessions the {@link Map} to use. Cannot be null.
|
||||
*/
|
||||
public MapReactiveSessionRepository(Map<String, Session> sessions) {
|
||||
public ReactiveMapSessionRepository(Map<String, Session> sessions) {
|
||||
if (sessions == null) {
|
||||
throw new IllegalArgumentException("sessions cannot be null");
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,7 +25,7 @@ import org.springframework.context.annotation.Import;
|
||||
/**
|
||||
* Add this annotation to a {@code @Configuration} class to configure a {@code WebSessionManager} for a WebFlux
|
||||
* application. This annotation assumes a {@code ReactiveSessionRepository} is defined somewhere in the application
|
||||
* context. If not, it will fail with a clear error messages. For example:
|
||||
* context. If not, it will fail with a clear error message. For example:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
@@ -35,7 +35,7 @@ import org.springframework.context.annotation.Import;
|
||||
*
|
||||
* {@literal @Bean}
|
||||
* public ReactiveSessionRepository sessionRepository() {
|
||||
* return new MapReactiveSessionRepository();
|
||||
* return new ReactiveMapSessionRepository();
|
||||
* }
|
||||
*
|
||||
* }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,7 +47,7 @@ public class SpringWebSessionConfiguration {
|
||||
/**
|
||||
* Configure a {@link WebSessionManager} using a provided {@link ReactiveSessionRepository}.
|
||||
*
|
||||
* @param repository - a bean that implements {@link ReactiveSessionRepository}.
|
||||
* @param repository a bean that implements {@link ReactiveSessionRepository}.
|
||||
* @return a configured {@link WebSessionManager} registered with a preconfigured name.
|
||||
*/
|
||||
@Bean(WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,16 +29,11 @@ import org.springframework.session.SessionRepository;
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractSessionEvent extends ApplicationEvent {
|
||||
|
||||
private final String sessionId;
|
||||
|
||||
private final Session session;
|
||||
|
||||
protected AbstractSessionEvent(Object source, String sessionId) {
|
||||
super(source);
|
||||
this.sessionId = sessionId;
|
||||
this.session = null;
|
||||
}
|
||||
|
||||
AbstractSessionEvent(Object source, Session session) {
|
||||
super(source);
|
||||
this.session = session;
|
||||
@@ -62,4 +57,5 @@ public abstract class AbstractSessionEvent extends ApplicationEvent {
|
||||
public String getSessionId() {
|
||||
return this.sessionId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,19 +25,14 @@ import org.springframework.session.SessionRepository;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SessionCreatedEvent extends AbstractSessionEvent {
|
||||
|
||||
public SessionCreatedEvent(Object source, String sessionId) {
|
||||
super(source, sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SessionCreatedEvent}.
|
||||
* @param source The Source of the SessionCreatedEvent
|
||||
* @param session the Session that was created
|
||||
* @param source the source of the event
|
||||
* @param session the session that was created
|
||||
*/
|
||||
public SessionCreatedEvent(Object source, Session session) {
|
||||
super(source, session);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,16 +26,17 @@ import org.springframework.session.SessionRepository;
|
||||
* @author Mark Anderson
|
||||
* @author Rob Winch
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SessionDeletedEvent extends SessionDestroyedEvent {
|
||||
|
||||
public SessionDeletedEvent(Object source, String sessionId) {
|
||||
super(source, sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SessionDeletedEvent}.
|
||||
* @param source the source of the event
|
||||
* @param session the session that was created
|
||||
*/
|
||||
public SessionDeletedEvent(Object source, Session session) {
|
||||
super(source, session);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,21 +23,17 @@ import org.springframework.session.Session;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SessionDestroyedEvent extends AbstractSessionEvent {
|
||||
|
||||
public SessionDestroyedEvent(Object source, String sessionId) {
|
||||
super(source, sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SessionDestroyedEvent}.
|
||||
* @param source The Source of the SessionDestoryedEvent
|
||||
* @param session the Session that was created
|
||||
* @param source the source of the event
|
||||
* @param session the session that was created
|
||||
*/
|
||||
public SessionDestroyedEvent(Object source, Session session) {
|
||||
super(source, session);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,16 +26,17 @@ import org.springframework.session.SessionRepository;
|
||||
* @author Mark Anderson
|
||||
* @author Rob Winch
|
||||
* @since 1.1
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SessionExpiredEvent extends SessionDestroyedEvent {
|
||||
|
||||
public SessionExpiredEvent(Object source, String sessionId) {
|
||||
super(source, sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SessionExpiredEvent}.
|
||||
* @param source the source of the event
|
||||
* @param session the session that was created
|
||||
*/
|
||||
public SessionExpiredEvent(Object source, Session session) {
|
||||
super(source, session);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,12 +37,22 @@ import org.springframework.session.Session;
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
|
||||
private S session;
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private boolean invalidated;
|
||||
|
||||
private boolean old;
|
||||
|
||||
HttpSessionAdapter(S session, ServletContext servletContext) {
|
||||
if (session == null) {
|
||||
throw new IllegalArgumentException("session cannot be null");
|
||||
}
|
||||
if (servletContext == null) {
|
||||
throw new IllegalArgumentException("servletContext cannot be null");
|
||||
}
|
||||
this.session = session;
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
@@ -162,6 +172,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
}
|
||||
|
||||
private static final HttpSessionContext NOOP_SESSION_CONTEXT = new HttpSessionContext() {
|
||||
|
||||
@Override
|
||||
public HttpSession getSession(String sessionId) {
|
||||
return null;
|
||||
@@ -171,9 +182,11 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
public Enumeration<String> getIds() {
|
||||
return EMPTY_ENUMERATION;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static final Enumeration<String> EMPTY_ENUMERATION = new Enumeration<String>() {
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return false;
|
||||
@@ -183,5 +196,7 @@ class HttpSessionAdapter<S extends Session> implements HttpSession {
|
||||
public String nextElement() {
|
||||
throw new NoSuchElementException("a");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -29,20 +29,20 @@ import org.junit.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link MapReactiveSessionRepository}.
|
||||
* Tests for {@link ReactiveMapSessionRepository}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapReactiveSessionRepositoryTests {
|
||||
public class ReactiveMapSessionRepositoryTests {
|
||||
|
||||
private MapReactiveSessionRepository repository;
|
||||
private ReactiveMapSessionRepository repository;
|
||||
|
||||
private MapSession session;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.repository = new MapReactiveSessionRepository(new HashMap<>());
|
||||
this.repository = new ReactiveMapSessionRepository(new HashMap<>());
|
||||
this.session = new MapSession("session-id");
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class MapReactiveSessionRepositoryTests {
|
||||
public void constructorMapThenFound() {
|
||||
Map<String, Session> sessions = new HashMap<>();
|
||||
sessions.put(this.session.getId(), this.session);
|
||||
this.repository = new MapReactiveSessionRepository(sessions);
|
||||
this.repository = new ReactiveMapSessionRepository(sessions);
|
||||
|
||||
Session findByIdSession = this.repository.findById(this.session.getId()).block();
|
||||
assertThat(findByIdSession).isNotNull();
|
||||
@@ -60,7 +60,7 @@ public class MapReactiveSessionRepositoryTests {
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorMapWhenNullThenThrowsIllegalArgumentException() {
|
||||
Map<String, Session> sessions = null;
|
||||
new MapReactiveSessionRepository(sessions);
|
||||
new ReactiveMapSessionRepository(sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,7 +86,7 @@ public class MapReactiveSessionRepositoryTests {
|
||||
|
||||
Map<String, Session> sessions = new ConcurrentHashMap<>();
|
||||
sessions.put("session-id", this.session);
|
||||
this.repository = new MapReactiveSessionRepository(sessions);
|
||||
this.repository = new ReactiveMapSessionRepository(sessions);
|
||||
|
||||
assertThat(this.repository.findById(this.session.getId()).block()).isNull();
|
||||
assertThat(sessions).isEmpty();
|
||||
@@ -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.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -57,7 +57,7 @@ import static org.mockito.Mockito.verify;
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session;
|
||||
package org.springframework.session.config.annotation.web.server;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -24,7 +24,8 @@ import org.junit.Test;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
|
||||
import org.springframework.session.ReactiveMapSessionRepository;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
import org.springframework.web.server.session.CookieWebSessionIdResolver;
|
||||
import org.springframework.web.server.session.DefaultWebSessionManager;
|
||||
@@ -112,7 +113,7 @@ public class SpringWebSessionConfigurationTests {
|
||||
*/
|
||||
@Bean
|
||||
ReactiveSessionRepository<?> reactiveSessionRepository() {
|
||||
return new MapReactiveSessionRepository(new HashMap<>());
|
||||
return new ReactiveMapSessionRepository(new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ public class SpringWebSessionConfigurationTests {
|
||||
|
||||
@Bean
|
||||
ReactiveSessionRepository<?> reactiveSessionRepository() {
|
||||
return new MapReactiveSessionRepository(new HashMap<>());
|
||||
return new ReactiveMapSessionRepository(new HashMap<>());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -31,6 +31,7 @@ import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
@@ -42,28 +43,37 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link SessionEventHttpSessionListenerAdapter}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.1
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SessionEventHttpSessionListenerAdapterTests {
|
||||
@Mock
|
||||
HttpSessionListener listener1;
|
||||
@Mock
|
||||
HttpSessionListener listener2;
|
||||
@Mock
|
||||
ServletContext servletContext;
|
||||
@Captor
|
||||
ArgumentCaptor<HttpSessionEvent> sessionEvent;
|
||||
|
||||
SessionDestroyedEvent destroyed;
|
||||
SessionCreatedEvent created;
|
||||
SessionEventHttpSessionListenerAdapter listener;
|
||||
@Mock
|
||||
private HttpSessionListener listener1;
|
||||
|
||||
@Mock
|
||||
private HttpSessionListener listener2;
|
||||
|
||||
@Mock
|
||||
private ServletContext servletContext;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<HttpSessionEvent> sessionEvent;
|
||||
|
||||
private SessionDestroyedEvent destroyed;
|
||||
|
||||
private SessionCreatedEvent created;
|
||||
|
||||
private SessionEventHttpSessionListenerAdapter listener;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.listener = new SessionEventHttpSessionListenerAdapter(
|
||||
Arrays.asList(this.listener1, this.listener2));
|
||||
this.listener.setServletContext(new MockServletContext());
|
||||
|
||||
Session session = new MapSession();
|
||||
this.destroyed = new SessionDestroyedEvent(this, session);
|
||||
@@ -114,4 +124,5 @@ public class SessionEventHttpSessionListenerAdapterTests {
|
||||
assertThat(this.sessionEvent.getValue().getSession().getId())
|
||||
.isEqualTo(this.created.getSessionId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.events.SessionDeletedEvent;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.session.web.socket.events.SessionConnectEvent;
|
||||
@@ -46,36 +47,40 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class WebSocketRegistryListenerTests {
|
||||
|
||||
@Mock
|
||||
WebSocketSession wsSession;
|
||||
private WebSocketSession wsSession;
|
||||
|
||||
@Mock
|
||||
WebSocketSession wsSession2;
|
||||
private WebSocketSession wsSession2;
|
||||
|
||||
@Mock
|
||||
Message<byte[]> message;
|
||||
private Message<byte[]> message;
|
||||
|
||||
@Mock
|
||||
Principal principal;
|
||||
private Principal principal;
|
||||
|
||||
SessionConnectEvent connect;
|
||||
private SessionConnectEvent connect;
|
||||
|
||||
SessionConnectEvent connect2;
|
||||
private SessionConnectEvent connect2;
|
||||
|
||||
SessionDisconnectEvent disconnect;
|
||||
private SessionDisconnectEvent disconnect;
|
||||
|
||||
SessionDeletedEvent deleted;
|
||||
private SessionDeletedEvent deleted;
|
||||
|
||||
SessionExpiredEvent expired;
|
||||
private SessionExpiredEvent expired;
|
||||
|
||||
Map<String, Object> attributes;
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
String sessionId;
|
||||
|
||||
WebSocketRegistryListener listener;
|
||||
private WebSocketRegistryListener listener;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.sessionId = "session-id";
|
||||
String sessionId = "session-id";
|
||||
MapSession session = new MapSession(sessionId);
|
||||
|
||||
this.attributes = new HashMap<>();
|
||||
SessionRepositoryMessageInterceptor.setSessionId(this.attributes, this.sessionId);
|
||||
SessionRepositoryMessageInterceptor.setSessionId(this.attributes, sessionId);
|
||||
|
||||
given(this.wsSession.getAttributes()).willReturn(this.attributes);
|
||||
given(this.wsSession.getPrincipal()).willReturn(this.principal);
|
||||
@@ -94,8 +99,8 @@ public class WebSocketRegistryListenerTests {
|
||||
this.connect2 = new SessionConnectEvent(this.listener, this.wsSession2);
|
||||
this.disconnect = new SessionDisconnectEvent(this.listener, this.message,
|
||||
this.wsSession.getId(), CloseStatus.NORMAL);
|
||||
this.deleted = new SessionDeletedEvent(this.listener, this.sessionId);
|
||||
this.expired = new SessionExpiredEvent(this.listener, this.sessionId);
|
||||
this.deleted = new SessionDeletedEvent(this.listener, session);
|
||||
this.expired = new SessionExpiredEvent(this.listener, session);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -170,4 +175,5 @@ public class WebSocketRegistryListenerTests {
|
||||
verify(this.wsSession2).close(WebSocketRegistryListener.SESSION_EXPIRED_STATUS);
|
||||
verify(this.wsSession, times(0)).close(any(CloseStatus.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
*/
|
||||
public abstract class AbstractRedisITests {
|
||||
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.2";
|
||||
private static final String DOCKER_IMAGE = "redis:4.0.7";
|
||||
|
||||
@ClassRule
|
||||
public static GenericContainer redisContainer = new GenericContainer(DOCKER_IMAGE)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,7 +24,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
@@ -176,6 +176,21 @@ public class ReactiveRedisOperationsSessionRepositoryITests extends AbstractRedi
|
||||
assertThat(this.repository.findById(originalId).block()).isNull();
|
||||
}
|
||||
|
||||
// gh-954
|
||||
@Test
|
||||
public void changeSessionIdSaveTwice() {
|
||||
ReactiveRedisOperationsSessionRepository.RedisSession toSave = this.repository
|
||||
.createSession().block();
|
||||
String originalId = toSave.getId();
|
||||
toSave.changeSessionId();
|
||||
|
||||
this.repository.save(toSave).block();
|
||||
this.repository.save(toSave).block();
|
||||
|
||||
assertThat(this.repository.findById(toSave.getId()).block()).isNotNull();
|
||||
assertThat(this.repository.findById(originalId).block()).isNull();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRedisWebSession
|
||||
static class Config extends BaseConfig {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,12 +42,12 @@ import org.springframework.session.data.redis.config.annotation.web.http.EnableR
|
||||
import org.springframework.session.events.SessionCreatedEvent;
|
||||
import org.springframework.session.events.SessionDestroyedEvent;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests {
|
||||
@@ -89,7 +89,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
public void saves() throws InterruptedException {
|
||||
String username = "saves-" + System.currentTimeMillis();
|
||||
|
||||
String usernameSessionKey = "spring:session:RedisOperationsSessionRepositoryITests:index:"
|
||||
String usernameSessionKey = "RedisOperationsSessionRepositoryITests:index:"
|
||||
+ INDEX_NAME + ":" + username;
|
||||
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
@@ -188,7 +188,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
String body = "spring:session:RedisOperationsSessionRepositoryITests:sessions:expires:"
|
||||
String body = "RedisOperationsSessionRepositoryITests:sessions:expires:"
|
||||
+ toSave.getId();
|
||||
String channel = ":expired";
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
|
||||
@@ -356,7 +356,7 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
|
||||
this.repository.save(toSave);
|
||||
|
||||
String body = "spring:session:RedisOperationsSessionRepositoryITests:sessions:expires:"
|
||||
String body = "RedisOperationsSessionRepositoryITests:sessions:expires:"
|
||||
+ toSave.getId();
|
||||
String channel = ":expired";
|
||||
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
|
||||
@@ -567,6 +567,20 @@ public class RedisOperationsSessionRepositoryITests extends AbstractRedisITests
|
||||
assertThat(this.repository.findById(originalId)).isNull();
|
||||
}
|
||||
|
||||
// gh-962
|
||||
@Test
|
||||
public void changeSessionIdSaveTwice() {
|
||||
RedisSession toSave = this.repository.createSession();
|
||||
String originalId = toSave.getId();
|
||||
toSave.changeSessionId();
|
||||
|
||||
this.repository.save(toSave);
|
||||
this.repository.save(toSave);
|
||||
|
||||
assertThat(this.repository.findById(toSave.getId())).isNotNull();
|
||||
assertThat(this.repository.findById(originalId)).isNull();
|
||||
}
|
||||
|
||||
private String getSecurityName() {
|
||||
return this.context.getAuthentication().getName();
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@ import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.data.redis.AbstractRedisITests;
|
||||
import org.springframework.session.events.SessionExpiredEvent;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Session>
|
||||
|
||||
@@ -27,12 +27,12 @@ import org.springframework.session.data.redis.AbstractRedisITests;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RedisOperationsSessionRepositoryFlushImmediatelyITests<S extends Session>
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.springframework.session.data.redis.AbstractRedisITests;
|
||||
import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* @author Vladimir Tsanev
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITests {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +24,7 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
@@ -43,9 +44,9 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
ReactiveSessionRepository<ReactiveRedisOperationsSessionRepository.RedisSession> {
|
||||
|
||||
/**
|
||||
* The default prefix for each key and channel in Redis used by Spring Session.
|
||||
* The default namespace for each key and channel in Redis used by Spring Session.
|
||||
*/
|
||||
static final String DEFAULT_SPRING_SESSION_REDIS_PREFIX = "spring:session:";
|
||||
public static final String DEFAULT_NAMESPACE = "spring:session";
|
||||
|
||||
/**
|
||||
* The key in the Hash representing {@link Session#getCreationTime()}.
|
||||
@@ -63,19 +64,19 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
static final String MAX_INACTIVE_INTERVAL_KEY = "maxInactiveInterval";
|
||||
|
||||
/**
|
||||
* The prefix of the key for used for session attributes. The suffix is the name of
|
||||
* The prefix of the key used for session attributes. The suffix is the name of
|
||||
* the session attribute. For example, if the session contained an attribute named
|
||||
* attributeName, then there would be an entry in the hash named
|
||||
* sessionAttr:attributeName that mapped to its value.
|
||||
*/
|
||||
static final String ATTRIBUTE_PREFIX = "attribute:";
|
||||
static final String ATTRIBUTE_PREFIX = "sessionAttr:";
|
||||
|
||||
private final ReactiveRedisOperations<String, Object> sessionRedisOperations;
|
||||
|
||||
/**
|
||||
* The prefix for every key used by Spring Session in Redis.
|
||||
* The namespace for every key used by Spring Session in Redis.
|
||||
*/
|
||||
private String keyPrefix = DEFAULT_SPRING_SESSION_REDIS_PREFIX;
|
||||
private String namespace = DEFAULT_NAMESPACE + ":";
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override the default value for
|
||||
@@ -93,7 +94,7 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
|
||||
public void setRedisKeyNamespace(String namespace) {
|
||||
Assert.hasText(namespace, "namespace cannot be null or empty");
|
||||
this.keyPrefix = DEFAULT_SPRING_SESSION_REDIS_PREFIX + namespace.trim() + ":";
|
||||
this.namespace = namespace.trim() + ":";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +168,7 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
private String getSessionKey(String sessionId) {
|
||||
return this.keyPrefix + "sessions:" + sessionId;
|
||||
return this.namespace + "sessions:" + sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,18 +320,25 @@ public class ReactiveRedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
private Mono<Void> saveChangeSessionId(String sessionId) {
|
||||
if (isNew() || sessionId.equals(this.originalSessionId)) {
|
||||
if (sessionId.equals(this.originalSessionId)) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
String originalSessionKey = getSessionKey(this.originalSessionId);
|
||||
String sessionKey = getSessionKey(sessionId);
|
||||
Publisher<Void> replaceSessionId = s -> {
|
||||
this.originalSessionId = sessionId;
|
||||
s.onComplete();
|
||||
};
|
||||
|
||||
return ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.rename(originalSessionKey, sessionKey).and(s -> {
|
||||
this.originalSessionId = sessionId;
|
||||
s.onComplete();
|
||||
});
|
||||
if (isNew()) {
|
||||
return Mono.from(replaceSessionId);
|
||||
}
|
||||
else {
|
||||
String originalSessionKey = getSessionKey(this.originalSessionId);
|
||||
String sessionKey = getSessionKey(sessionId);
|
||||
|
||||
return ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
|
||||
.rename(originalSessionKey, sessionKey).and(replaceSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,13 +30,10 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.BoundHashOperations;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
@@ -63,9 +60,12 @@ import org.springframework.util.Assert;
|
||||
* A typical example of how to create a new instance can be seen below:
|
||||
*
|
||||
* <pre>
|
||||
* LettuceConnectionFactory factory = new LettuceConnectionFactory();
|
||||
* RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
*
|
||||
* RedisOperationsSessionRepository redisSessionRepository = new RedisOperationsSessionRepository(factory);
|
||||
* // ... configure redisTemplate ...
|
||||
*
|
||||
* RedisOperationsSessionRepository redisSessionRepository =
|
||||
* new RedisOperationsSessionRepository(redisTemplate);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
@@ -253,9 +253,9 @@ public class RedisOperationsSessionRepository implements
|
||||
static PrincipalNameResolver PRINCIPAL_NAME_RESOLVER = new PrincipalNameResolver();
|
||||
|
||||
/**
|
||||
* The default prefix for each key and channel in Redis used by Spring Session.
|
||||
* The default namespace for each key and channel in Redis used by Spring Session.
|
||||
*/
|
||||
static final String DEFAULT_SPRING_SESSION_REDIS_PREFIX = "spring:session:";
|
||||
public static final String DEFAULT_NAMESPACE = "spring:session";
|
||||
|
||||
/**
|
||||
* The key in the Hash representing
|
||||
@@ -277,7 +277,7 @@ public class RedisOperationsSessionRepository implements
|
||||
static final String LAST_ACCESSED_ATTR = "lastAccessedTime";
|
||||
|
||||
/**
|
||||
* The prefix of the key for used for session attributes. The suffix is the name of
|
||||
* The prefix of the key used for session attributes. The suffix is the name of
|
||||
* the session attribute. For example, if the session contained an attribute named
|
||||
* attributeName, then there would be an entry in the hash named
|
||||
* sessionAttr:attributeName that mapped to its value.
|
||||
@@ -285,9 +285,9 @@ public class RedisOperationsSessionRepository implements
|
||||
static final String SESSION_ATTR_PREFIX = "sessionAttr:";
|
||||
|
||||
/**
|
||||
* The prefix for every key used by Spring Session in Redis.
|
||||
* The namespace for every key used by Spring Session in Redis.
|
||||
*/
|
||||
private String keyPrefix = DEFAULT_SPRING_SESSION_REDIS_PREFIX;
|
||||
private String namespace = DEFAULT_NAMESPACE + ":";
|
||||
|
||||
private final RedisOperations<Object, Object> sessionRedisOperations;
|
||||
|
||||
@@ -313,17 +313,6 @@ public class RedisOperationsSessionRepository implements
|
||||
|
||||
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
|
||||
|
||||
/**
|
||||
* Allows creating an instance and uses a default {@link RedisOperations} for both
|
||||
* managing the session and the expirations.
|
||||
*
|
||||
* @param redisConnectionFactory the {@link RedisConnectionFactory} to use.
|
||||
*/
|
||||
public RedisOperationsSessionRepository(
|
||||
RedisConnectionFactory redisConnectionFactory) {
|
||||
this(createDefaultTemplate(redisConnectionFactory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance. For an example, refer to the class level javadoc.
|
||||
*
|
||||
@@ -527,6 +516,12 @@ public class RedisOperationsSessionRepository implements
|
||||
|
||||
RedisSession session = getSession(sessionId, true);
|
||||
|
||||
if (session == null) {
|
||||
logger.warn("Unable to publish SessionDestroyedEvent for session "
|
||||
+ sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Publishing SessionDestroyedEvent for session " + sessionId);
|
||||
}
|
||||
@@ -534,18 +529,15 @@ public class RedisOperationsSessionRepository implements
|
||||
cleanupPrincipalIndex(session);
|
||||
|
||||
if (isDeleted) {
|
||||
handleDeleted(sessionId, session);
|
||||
handleDeleted(session);
|
||||
}
|
||||
else {
|
||||
handleExpired(sessionId, session);
|
||||
handleExpired(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupPrincipalIndex(RedisSession session) {
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
String sessionId = session.getId();
|
||||
String principal = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(session);
|
||||
if (principal != null) {
|
||||
@@ -554,28 +546,18 @@ public class RedisOperationsSessionRepository implements
|
||||
}
|
||||
}
|
||||
|
||||
public void handleCreated(Map<Object, Object> loaded, String channel) {
|
||||
private void handleCreated(Map<Object, Object> loaded, String channel) {
|
||||
String id = channel.substring(channel.lastIndexOf(":") + 1);
|
||||
Session session = loadSession(id, loaded);
|
||||
publishEvent(new SessionCreatedEvent(this, session));
|
||||
}
|
||||
|
||||
private void handleDeleted(String sessionId, RedisSession session) {
|
||||
if (session == null) {
|
||||
publishEvent(new SessionDeletedEvent(this, sessionId));
|
||||
}
|
||||
else {
|
||||
publishEvent(new SessionDeletedEvent(this, session));
|
||||
}
|
||||
private void handleDeleted(RedisSession session) {
|
||||
publishEvent(new SessionDeletedEvent(this, session));
|
||||
}
|
||||
|
||||
private void handleExpired(String sessionId, RedisSession session) {
|
||||
if (session == null) {
|
||||
publishEvent(new SessionExpiredEvent(this, sessionId));
|
||||
}
|
||||
else {
|
||||
publishEvent(new SessionExpiredEvent(this, session));
|
||||
}
|
||||
private void handleExpired(RedisSession session) {
|
||||
publishEvent(new SessionExpiredEvent(this, session));
|
||||
}
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
@@ -588,7 +570,8 @@ public class RedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
public void setRedisKeyNamespace(String namespace) {
|
||||
this.keyPrefix = DEFAULT_SPRING_SESSION_REDIS_PREFIX + namespace + ":";
|
||||
Assert.hasText(namespace, "namespace cannot be null or empty");
|
||||
this.namespace = namespace.trim() + ":";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -598,17 +581,17 @@ public class RedisOperationsSessionRepository implements
|
||||
* @return the Hash key for this session by prefixing it appropriately.
|
||||
*/
|
||||
String getSessionKey(String sessionId) {
|
||||
return this.keyPrefix + "sessions:" + sessionId;
|
||||
return this.namespace + "sessions:" + sessionId;
|
||||
}
|
||||
|
||||
String getPrincipalKey(String principalName) {
|
||||
return this.keyPrefix + "index:"
|
||||
return this.namespace + "index:"
|
||||
+ FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME + ":"
|
||||
+ principalName;
|
||||
}
|
||||
|
||||
String getExpirationsKey(long expiration) {
|
||||
return this.keyPrefix + "expirations:" + expiration;
|
||||
return this.namespace + "expirations:" + expiration;
|
||||
}
|
||||
|
||||
private String getExpiredKey(String sessionId) {
|
||||
@@ -620,7 +603,7 @@ public class RedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
private String getExpiredKeyPrefix() {
|
||||
return this.keyPrefix + "sessions:" + "expires:";
|
||||
return this.namespace + "sessions:" + "expires:";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -630,7 +613,7 @@ public class RedisOperationsSessionRepository implements
|
||||
* @return the prefix for the channel that SessionCreatedEvent are published to
|
||||
*/
|
||||
public String getSessionCreatedChannelPrefix() {
|
||||
return this.keyPrefix + "event:created:";
|
||||
return this.namespace + "event:created:";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -654,17 +637,6 @@ public class RedisOperationsSessionRepository implements
|
||||
return SESSION_ATTR_PREFIX + attributeName;
|
||||
}
|
||||
|
||||
private static RedisTemplate<Object, Object> createDefaultTemplate(
|
||||
RedisConnectionFactory connectionFactory) {
|
||||
Assert.notNull(connectionFactory, "connectionFactory cannot be null");
|
||||
RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom implementation of {@link Session} that uses a {@link MapSession} as the
|
||||
* basis for its mapping. It keeps track of any attributes that have changed. When
|
||||
@@ -832,10 +804,12 @@ public class RedisOperationsSessionRepository implements
|
||||
}
|
||||
|
||||
private void saveChangeSessionId(String sessionId) {
|
||||
if (!isNew() && !sessionId.equals(this.originalSessionId)) {
|
||||
String originalSessionIdKey = getSessionKey(this.originalSessionId);
|
||||
String sessionIdKey = getSessionKey(sessionId);
|
||||
RedisOperationsSessionRepository.this.sessionRedisOperations.rename(originalSessionIdKey, sessionIdKey);
|
||||
if (!sessionId.equals(this.originalSessionId)) {
|
||||
if (!isNew()) {
|
||||
String originalSessionIdKey = getSessionKey(this.originalSessionId);
|
||||
String sessionIdKey = getSessionKey(sessionId);
|
||||
RedisOperationsSessionRepository.this.sessionRedisOperations.rename(originalSessionIdKey, sessionIdKey);
|
||||
}
|
||||
this.originalSessionId = sessionId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,19 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
|
||||
/**
|
||||
* Add this annotation to an {@code @Configuration} class to expose the
|
||||
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by
|
||||
* Redis. In order to leverage the annotation, a single {@link RedisConnectionFactory}
|
||||
* must be provided. For example:
|
||||
* {@link SessionRepositoryFilter} as a bean named {@code springSessionRepositoryFilter}
|
||||
* and backed by Redis. In order to leverage the annotation, a single
|
||||
* {@link RedisConnectionFactory} must be provided. For example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
@@ -41,7 +45,7 @@ import org.springframework.session.data.redis.RedisFlushMode;
|
||||
* public class RedisHttpSessionConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public LettuceConnectionFactory connectionFactory() {
|
||||
* public LettuceConnectionFactory redisConnectionFactory() {
|
||||
* return new LettuceConnectionFactory();
|
||||
* }
|
||||
*
|
||||
@@ -67,38 +71,27 @@ public @interface EnableRedisHttpSession {
|
||||
* This should be a non-negative integer.
|
||||
* @return the seconds a session can be inactive before expiring
|
||||
*/
|
||||
int maxInactiveIntervalInSeconds() default 1800;
|
||||
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Defines a unique namespace for keys. The value is used to isolate sessions by
|
||||
* changing the prefix from "spring:session:" to
|
||||
* "spring:session:<redisNamespace>:". The default is "" such that all Redis
|
||||
* keys begin with "spring:session".
|
||||
* </p>
|
||||
*
|
||||
* changing the prefix from default {@code spring:session:} to
|
||||
* {@code <redisNamespace>:}.
|
||||
* <p>
|
||||
* For example, if you had an application named "Application A" that needed to keep
|
||||
* the sessions isolated from "Application B" you could set two different values for
|
||||
* the applications and they could function within the same Redis instance.
|
||||
* </p>
|
||||
*
|
||||
* @return the unique namespace for keys
|
||||
*/
|
||||
String redisNamespace() default "";
|
||||
String redisNamespace() default RedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
/**
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
* updates the backing Redis when {@link SessionRepository#save(Session)} is invoked.
|
||||
* In a web environment this happens just before the HTTP response is committed.
|
||||
* <p>
|
||||
* Sets the flush mode for the Redis sessions. The default is ON_SAVE which only
|
||||
* updates the backing Redis when
|
||||
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In
|
||||
* a web environment this happens just before the HTTP response is committed.
|
||||
* </p>
|
||||
* <p>
|
||||
* Setting the value to IMMEDIATE will ensure that the any updates to the Session are
|
||||
* immediately written to the Redis instance.
|
||||
* </p>
|
||||
*
|
||||
* Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
|
||||
* Session are immediately written to the Redis instance.
|
||||
* @return the {@link RedisFlushMode} to use
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -107,6 +100,7 @@ public @interface EnableRedisHttpSession {
|
||||
/**
|
||||
* The cron expression for expired session cleanup job. By default runs every minute.
|
||||
* @return the session cleanup cron expression
|
||||
* @since 2.0.0
|
||||
*/
|
||||
String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import java.util.concurrent.Executor;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -44,6 +45,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
|
||||
@@ -57,7 +59,7 @@ import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* Exposes the {@link SessionRepositoryFilter} as a bean named
|
||||
* "springSessionRepositoryFilter". In order to use this a single
|
||||
* {@code springSessionRepositoryFilter}. In order to use this a single
|
||||
* {@link RedisConnectionFactory} must be exposed as a Bean.
|
||||
*
|
||||
* @author Rob Winch
|
||||
@@ -69,13 +71,14 @@ import org.springframework.util.StringValueResolver;
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
|
||||
implements EmbeddedValueResolverAware, ImportAware, SchedulingConfigurer {
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
|
||||
SchedulingConfigurer {
|
||||
|
||||
static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = 1800;
|
||||
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
private String redisNamespace = "";
|
||||
private String redisNamespace = RedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
|
||||
|
||||
@@ -93,12 +96,13 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
|
||||
private Executor redisSubscriptionExecutor;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
@Bean
|
||||
public RedisOperationsSessionRepository sessionRepository() {
|
||||
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate(
|
||||
this.redisConnectionFactory, this.defaultRedisSerializer);
|
||||
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
|
||||
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
|
||||
redisTemplate);
|
||||
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
|
||||
@@ -115,8 +119,7 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisMessageListenerContainer redisMessageListenerContainer(
|
||||
RedisOperationsSessionRepository messageListener) {
|
||||
public RedisMessageListenerContainer redisMessageListenerContainer() {
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(this.redisConnectionFactory);
|
||||
if (this.redisTaskExecutor != null) {
|
||||
@@ -125,12 +128,12 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
if (this.redisSubscriptionExecutor != null) {
|
||||
container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
|
||||
}
|
||||
container.addMessageListener(messageListener,
|
||||
container.addMessageListener(sessionRepository(),
|
||||
Arrays.asList(new PatternTopic("__keyevent@*:del"),
|
||||
new PatternTopic("__keyevent@*:expired")));
|
||||
container.addMessageListener(messageListener,
|
||||
container.addMessageListener(sessionRepository(),
|
||||
Collections.singletonList(new PatternTopic(
|
||||
messageListener.getSessionCreatedChannelPrefix() + "*")));
|
||||
sessionRepository().getSessionCreatedChannelPrefix() + "*")));
|
||||
return container;
|
||||
}
|
||||
|
||||
@@ -205,6 +208,11 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
this.redisSubscriptionExecutor = redisSubscriptionExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||
this.embeddedValueResolver = resolver;
|
||||
@@ -235,16 +243,15 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
|
||||
this.cleanupCron);
|
||||
}
|
||||
|
||||
private static RedisTemplate<Object, Object> createRedisTemplate(
|
||||
RedisConnectionFactory redisConnectionFactory,
|
||||
RedisSerializer<Object> defaultRedisSerializer) {
|
||||
private RedisTemplate<Object, Object> createRedisTemplate() {
|
||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
if (defaultRedisSerializer != null) {
|
||||
redisTemplate.setDefaultSerializer(defaultRedisSerializer);
|
||||
if (this.defaultRedisSerializer != null) {
|
||||
redisTemplate.setDefaultSerializer(this.defaultRedisSerializer);
|
||||
}
|
||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||
redisTemplate.setConnectionFactory(this.redisConnectionFactory);
|
||||
redisTemplate.setBeanClassLoader(this.classLoader);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
@@ -17,23 +17,29 @@
|
||||
package org.springframework.session.data.redis.config.annotation.web.server;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.ReactiveSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
/**
|
||||
* Add this annotation to an {@code @Configuration} class to expose the
|
||||
* {@link org.springframework.web.server.session.WebSessionManager} as a bean named
|
||||
* {@code webSessionManager} and backed by Reactive Redis. In order to leverage the
|
||||
* annotation, a single {@link ReactiveRedisConnectionFactory} must be provided. For
|
||||
* example: <pre class="code">
|
||||
* {@link WebSessionManager} as a bean named {@code webSessionManager} and backed by
|
||||
* Reactive Redis. In order to leverage the annotation, a single
|
||||
* {@link ReactiveRedisConnectionFactory} must be provided. For example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableRedisWebSession
|
||||
* public class RedisWebSessionConfig {
|
||||
@@ -46,53 +52,46 @@ import org.springframework.session.data.redis.RedisFlushMode;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* More advanced configurations can extend {@link RedisWebSessionConfiguration}
|
||||
* instead.
|
||||
* More advanced configurations can extend {@link RedisWebSessionConfiguration} instead.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0.0
|
||||
* @see EnableSpringWebSession
|
||||
*/
|
||||
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@Target({ java.lang.annotation.ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(RedisWebSessionConfiguration.class)
|
||||
@Configuration
|
||||
public @interface EnableRedisWebSession {
|
||||
|
||||
int maxInactiveIntervalInSeconds() default 1800;
|
||||
/**
|
||||
* The session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
|
||||
* This should be a non-negative integer.
|
||||
* @return the seconds a session can be inactive before expiring
|
||||
*/
|
||||
int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Defines a unique namespace for keys. The value is used to isolate sessions by
|
||||
* changing the prefix from {@code spring:session:} to
|
||||
* {@code spring:session:<redisNamespace>:}. The default is "" such that all Redis
|
||||
* keys begin with {@code spring:session:}.
|
||||
* </p>
|
||||
*
|
||||
* changing the prefix from default {@code spring:session:} to
|
||||
* {@code <redisNamespace>:}.
|
||||
* <p>
|
||||
* For example, if you had an application named "Application A" that needed to keep
|
||||
* the sessions isolated from "Application B" you could set two different values for
|
||||
* the applications and they could function within the same Redis instance.
|
||||
* </p>
|
||||
*
|
||||
* @return the unique namespace for keys
|
||||
*/
|
||||
String redisNamespace() default "";
|
||||
String redisNamespace() default ReactiveRedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the flush mode for the Redis sessions. The default is ON_SAVE which only
|
||||
* Flush mode for the Redis sessions. The default is {@code ON_SAVE} which only
|
||||
* updates the backing Redis when {@link ReactiveSessionRepository#save(Session)} is
|
||||
* invoked. In a web environment this happens just before the HTTP response is
|
||||
* committed.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Setting the value to IMMEDIATE will ensure that the any updates to the Session are
|
||||
* immediately written to the Redis instance.
|
||||
* </p>
|
||||
*
|
||||
* Setting the value to {@code IMMEDIATE} will ensure that the any updates to the
|
||||
* Session are immediately written to the Redis instance.
|
||||
* @return the {@link RedisFlushMode} to use
|
||||
*/
|
||||
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.session.data.redis.config.annotation.web.server;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration;
|
||||
import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository;
|
||||
import org.springframework.session.data.redis.RedisFlushMode;
|
||||
@@ -52,35 +54,31 @@ import org.springframework.web.server.session.WebSessionManager;
|
||||
*/
|
||||
@Configuration
|
||||
public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
implements EmbeddedValueResolverAware, ImportAware {
|
||||
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
|
||||
|
||||
private static final RedisSerializer<String> keySerializer = new StringRedisSerializer();
|
||||
private Integer maxInactiveIntervalInSeconds = MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
|
||||
|
||||
private static final RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer();
|
||||
|
||||
private Integer maxInactiveIntervalInSeconds = 1800;
|
||||
|
||||
private String redisNamespace = "";
|
||||
private String redisNamespace = ReactiveRedisOperationsSessionRepository.DEFAULT_NAMESPACE;
|
||||
|
||||
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
|
||||
|
||||
private ReactiveRedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
@Bean
|
||||
public ReactiveRedisOperationsSessionRepository sessionRepository() {
|
||||
ReactiveRedisTemplate<String, Object> reactiveRedisTemplate = createReactiveRedisTemplate();
|
||||
ReactiveRedisOperationsSessionRepository sessionRepository = new ReactiveRedisOperationsSessionRepository(
|
||||
createDefaultTemplate(this.redisConnectionFactory));
|
||||
reactiveRedisTemplate);
|
||||
sessionRepository
|
||||
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
|
||||
|
||||
if (StringUtils.hasText(this.redisNamespace)) {
|
||||
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
|
||||
}
|
||||
|
||||
sessionRepository.setRedisFlushMode(this.redisFlushMode);
|
||||
|
||||
return sessionRepository;
|
||||
}
|
||||
|
||||
@@ -109,6 +107,11 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
this.redisConnectionFactory = redisConnectionFactoryToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||
this.embeddedValueResolver = resolver;
|
||||
@@ -116,29 +119,28 @@ public class RedisWebSessionConfiguration extends SpringWebSessionConfiguration
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
Map<String, Object> enableAttrMap = importMetadata
|
||||
Map<String, Object> attributeMap = importMetadata
|
||||
.getAnnotationAttributes(EnableRedisWebSession.class.getName());
|
||||
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
|
||||
|
||||
if (enableAttrs != null) {
|
||||
this.maxInactiveIntervalInSeconds = enableAttrs
|
||||
.getNumber("maxInactiveIntervalInSeconds");
|
||||
String redisNamespaceValue = enableAttrs.getString("redisNamespace");
|
||||
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||
this.redisNamespace = this.embeddedValueResolver
|
||||
.resolveStringValue(redisNamespaceValue);
|
||||
}
|
||||
this.redisFlushMode = enableAttrs.getEnum("redisFlushMode");
|
||||
AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
|
||||
this.maxInactiveIntervalInSeconds = attributes
|
||||
.getNumber("maxInactiveIntervalInSeconds");
|
||||
String redisNamespaceValue = attributes.getString("redisNamespace");
|
||||
if (StringUtils.hasText(redisNamespaceValue)) {
|
||||
this.redisNamespace = this.embeddedValueResolver
|
||||
.resolveStringValue(redisNamespaceValue);
|
||||
}
|
||||
this.redisFlushMode = attributes.getEnum("redisFlushMode");
|
||||
}
|
||||
|
||||
private static ReactiveRedisTemplate<String, Object> createDefaultTemplate(
|
||||
ReactiveRedisConnectionFactory connectionFactory) {
|
||||
private ReactiveRedisTemplate<String, Object> createReactiveRedisTemplate() {
|
||||
RedisSerializer<String> keySerializer = new StringRedisSerializer();
|
||||
RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer(
|
||||
this.classLoader);
|
||||
RedisSerializationContext<String, Object> serializationContext = RedisSerializationContext
|
||||
.<String, Object>newSerializationContext(valueSerializer)
|
||||
.key(keySerializer).hashKey(keySerializer).build();
|
||||
|
||||
return new ReactiveRedisTemplate<>(connectionFactory, serializationContext);
|
||||
return new ReactiveRedisTemplate<>(this.redisConnectionFactory,
|
||||
serializationContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,9 +85,8 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
|
||||
public void customRedisKeyNamespace() {
|
||||
this.repository.setRedisKeyNamespace("test");
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(this.repository, "keyPrefix")).isEqualTo(
|
||||
ReactiveRedisOperationsSessionRepository.DEFAULT_SPRING_SESSION_REDIS_PREFIX
|
||||
+ "test:");
|
||||
assertThat(ReflectionTestUtils.getField(this.repository, "namespace"))
|
||||
.isEqualTo("test:");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -110,27 +110,11 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
this.cached.setLastAccessedTime(Instant.ofEpochMilli(1404360000000L));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void constructorNullConnectionFactory() {
|
||||
new RedisOperationsSessionRepository((RedisConnectionFactory) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setApplicationEventPublisherNull() {
|
||||
this.redisRepository.setApplicationEventPublisher(null);
|
||||
}
|
||||
|
||||
// gh-61
|
||||
@Test
|
||||
public void constructorConnectionFactory() {
|
||||
this.redisRepository = new RedisOperationsSessionRepository(this.factory);
|
||||
RedisSession session = this.redisRepository.createSession();
|
||||
|
||||
given(this.factory.getConnection()).willReturn(this.connection);
|
||||
|
||||
this.redisRepository.save(session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionId() {
|
||||
RedisSession createSession = this.redisRepository.createSession();
|
||||
@@ -709,6 +693,36 @@ public class RedisOperationsSessionRepositoryTests {
|
||||
this.redisRepository.setRedisFlushMode(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeRedisNamespace() {
|
||||
String namespace = "foo:bar";
|
||||
this.redisRepository.setRedisKeyNamespace(namespace);
|
||||
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
|
||||
session.setMaxInactiveInterval(Duration.ZERO);
|
||||
given(this.redisOperations.boundHashOps(anyString()))
|
||||
.willReturn(this.boundHashOperations);
|
||||
given(this.redisOperations.boundSetOps(anyString()))
|
||||
.willReturn(this.boundSetOperations);
|
||||
|
||||
this.redisRepository.save(session);
|
||||
|
||||
String id = session.getId();
|
||||
verify(this.redisOperations, atLeastOnce())
|
||||
.delete(namespace + ":sessions:expires:" + id);
|
||||
verify(this.redisOperations, never())
|
||||
.boundValueOps(namespace + ":sessions:expires:" + id);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setRedisKeyNamespaceNullNamespace() {
|
||||
this.redisRepository.setRedisKeyNamespace(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setRedisKeyNamespaceEmptyNamespace() {
|
||||
this.redisRepository.setRedisKeyNamespace(" ");
|
||||
}
|
||||
|
||||
private String getKey(String id) {
|
||||
return "spring:session:sessions:" + id;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -35,7 +35,7 @@ import static org.mockito.Mockito.mock;
|
||||
* @author Mark Paluch
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class RedisHttpSessionConfigurationClassPathXmlApplicationContextTests {
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user