Compare commits
36 Commits
2.2.0.RELE
...
2.3.0.RC1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dda13b5619 | ||
|
|
366f13bd25 | ||
|
|
3535137c47 | ||
|
|
a9bca9088f | ||
|
|
31de86ecef | ||
|
|
d123960f89 | ||
|
|
16d2923efd | ||
|
|
24015d0854 | ||
|
|
d8f160c178 | ||
|
|
0318f6e2c1 | ||
|
|
43dd571345 | ||
|
|
e7fb9fce47 | ||
|
|
f13eb8d73e | ||
|
|
1a07ba5114 | ||
|
|
7125aac567 | ||
|
|
3cbd3a9e25 | ||
|
|
4c914d46c9 | ||
|
|
adf411ecc3 | ||
|
|
95b39a203f | ||
|
|
3d653b3b50 | ||
|
|
938fd3c2e5 | ||
|
|
45bb0f9b0c | ||
|
|
cddd84d564 | ||
|
|
6931d40e6e | ||
|
|
3b672787f3 | ||
|
|
c0ee52b33b | ||
|
|
68f8641233 | ||
|
|
e7b2af47e1 | ||
|
|
1ad6cbd7f8 | ||
|
|
195af52d0b | ||
|
|
bc9d5f1299 | ||
|
|
3a4345eb6a | ||
|
|
6c41dea893 | ||
|
|
ee1d5b3b3c | ||
|
|
89a4255679 | ||
|
|
6d2e51a0b9 |
3
Jenkinsfile
vendored
3
Jenkinsfile
vendored
@@ -120,7 +120,8 @@ try {
|
||||
withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) {
|
||||
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
|
||||
withEnv(["JAVA_HOME=${tool 'jdk8'}"]) {
|
||||
sh './gradlew deployArtifacts finalizeDeployArtifacts --no-daemon --stacktrace -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password=$SIGNING_PASSWORD -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD'
|
||||
sh './gradlew deployArtifacts --no-daemon --stacktrace -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password=$SIGNING_PASSWORD -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD'
|
||||
sh './gradlew finalizeDeployArtifacts --no-daemon --stacktrace -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password=$SIGNING_PASSWORD -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ buildscript {
|
||||
snapshotBuild = version.endsWith('SNAPSHOT')
|
||||
milestoneBuild = !(releaseBuild || snapshotBuild)
|
||||
|
||||
springBootVersion = '2.2.0.RC1'
|
||||
springBootVersion = '2.2.5.RELEASE'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -13,7 +13,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.27.RELEASE'
|
||||
classpath 'io.spring.gradle:spring-build-conventions:0.0.28.RELEASE'
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.parallel=true
|
||||
version=2.2.0.RELEASE
|
||||
version=2.3.0.RC1
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-RELEASE'
|
||||
mavenBom 'org.junit:junit-bom:5.5.2'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.2.0.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Moore-RELEASE'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.2.0.RELEASE'
|
||||
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-SR5'
|
||||
mavenBom 'org.junit:junit-bom:5.6.0'
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.2.4.RELEASE'
|
||||
mavenBom 'org.springframework.data:spring-data-releasetrain:Neumann-M3'
|
||||
mavenBom 'org.springframework.security:spring-security-bom:5.3.0.RC1'
|
||||
mavenBom 'org.testcontainers:testcontainers-bom:1.12.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.12.3') {
|
||||
dependencySet(group: 'com.hazelcast', version: '3.12.6') {
|
||||
entry 'hazelcast'
|
||||
entry 'hazelcast-client'
|
||||
}
|
||||
|
||||
dependency 'com.h2database:h2:1.4.199'
|
||||
dependency 'com.h2database:h2:1.4.200'
|
||||
dependency 'com.ibm.db2:jcc:11.5.0.0'
|
||||
dependency 'com.microsoft.sqlserver:mssql-jdbc:7.4.1.jre8'
|
||||
dependency 'com.oracle.ojdbc:ojdbc8:19.3.0.0'
|
||||
dependency 'com.zaxxer:HikariCP:3.4.1'
|
||||
dependency 'edu.umd.cs.mtc:multithreadedtc:1.01'
|
||||
dependency 'io.lettuce:lettuce-core:5.2.0.RELEASE'
|
||||
dependency 'io.lettuce:lettuce-core:5.2.2.RELEASE'
|
||||
dependency 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
dependency 'javax.servlet:javax.servlet-api:4.0.1'
|
||||
dependency 'junit:junit:4.12'
|
||||
dependency 'mysql:mysql-connector-java:8.0.17'
|
||||
dependency 'junit:junit:4.13'
|
||||
dependency 'mysql:mysql-connector-java:8.0.19'
|
||||
dependency 'org.apache.derby:derby:10.14.2.0'
|
||||
dependency 'org.assertj:assertj-core:3.13.2'
|
||||
dependency 'org.assertj:assertj-core:3.15.0'
|
||||
dependency 'org.hsqldb:hsqldb:2.5.0'
|
||||
dependency 'org.mariadb.jdbc:mariadb-java-client:2.4.4'
|
||||
dependency 'org.mockito:mockito-core:3.0.0'
|
||||
dependency 'org.postgresql:postgresql:42.2.8'
|
||||
dependency 'org.mockito:mockito-core:3.3.0'
|
||||
dependency 'org.postgresql:postgresql:42.2.10'
|
||||
}
|
||||
}
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -32,7 +32,7 @@ public class PrincipalNameIndexResolver<S extends Session> extends SingleIndexRe
|
||||
|
||||
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
|
||||
|
||||
private static final SpelExpressionParser parser = new SpelExpressionParser();
|
||||
private static final Expression expression = new SpelExpressionParser().parseExpression("authentication?.name");
|
||||
|
||||
public PrincipalNameIndexResolver() {
|
||||
super(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
|
||||
@@ -45,7 +45,6 @@ public class PrincipalNameIndexResolver<S extends Session> extends SingleIndexRe
|
||||
}
|
||||
Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT);
|
||||
if (authentication != null) {
|
||||
Expression expression = parser.parseExpression("authentication?.name");
|
||||
return expression.getValue(authentication, String.class);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.springframework.session.events.SessionDestroyedEvent;
|
||||
*
|
||||
* {@literal @Bean}
|
||||
* public MapSessionRepository sessionRepository() {
|
||||
* return new MapSessionRepository();
|
||||
* return new MapSessionRepository(new ConcurrentHashMap<>());
|
||||
* }
|
||||
*
|
||||
* }
|
||||
|
||||
@@ -58,7 +58,7 @@ import org.springframework.util.ObjectUtils;
|
||||
*
|
||||
* {@literal @Bean}
|
||||
* public MapSessionRepository sessionRepository() {
|
||||
* return new MapSessionRepository();
|
||||
* return new MapSessionRepository(new ConcurrentHashMap<>());
|
||||
* }
|
||||
*
|
||||
* }
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.context.annotation.Import;
|
||||
*
|
||||
* {@literal @Bean}
|
||||
* public ReactiveSessionRepository sessionRepository() {
|
||||
* return new ReactiveMapSessionRepository();
|
||||
* return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
|
||||
* }
|
||||
*
|
||||
* }
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.session.web.http;
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
@@ -28,7 +30,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.web.MockCookie;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
@@ -52,7 +53,7 @@ class DefaultCookieSerializerTests {
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private CookiePreservingMockHttpServletResponse response;
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
private DefaultCookieSerializer serializer;
|
||||
|
||||
@@ -62,7 +63,7 @@ class DefaultCookieSerializerTests {
|
||||
void setup() {
|
||||
this.cookieName = "SESSION";
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new CookiePreservingMockHttpServletResponse();
|
||||
this.response = new MockHttpServletResponse();
|
||||
this.sessionId = "sessionId";
|
||||
this.serializer = new DefaultCookieSerializer();
|
||||
}
|
||||
@@ -213,14 +214,14 @@ class DefaultCookieSerializerTests {
|
||||
this.request.setServerName(domain);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
assertThat(getCookie().getDomain()).isEqualTo("example.com");
|
||||
this.response = new CookiePreservingMockHttpServletResponse();
|
||||
this.response = new MockHttpServletResponse();
|
||||
}
|
||||
String[] notMatchingDomains = { "example.com", "localhost", "127.0.0.1" };
|
||||
for (String domain : notMatchingDomains) {
|
||||
this.request.setServerName(domain);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
assertThat(getCookie().getDomain()).isNull();
|
||||
this.response = new CookiePreservingMockHttpServletResponse();
|
||||
this.response = new MockHttpServletResponse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,7 +292,7 @@ class DefaultCookieSerializerTests {
|
||||
void writeCookieCookieMaxAgeDefault() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(-1);
|
||||
assertThat(this.response.rawCookie).doesNotContain("Expires");
|
||||
assertThat(getCookie().getExpires()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -299,8 +300,11 @@ class DefaultCookieSerializerTests {
|
||||
this.serializer.setClock(Clock.fixed(Instant.parse("2019-10-07T20:10:00Z"), ZoneOffset.UTC));
|
||||
this.serializer.setCookieMaxAge(100);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
assertThat(this.response.rawCookie).contains("Expires=Mon, 7 Oct 2019 20:11:40 GMT");
|
||||
MockCookie cookie = getCookie();
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(100);
|
||||
ZonedDateTime expires = cookie.getExpires();
|
||||
assertThat(expires).isNotNull();
|
||||
assertThat(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME)).isEqualTo("Mon, 7 Oct 2019 20:11:40 GMT");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -308,8 +312,11 @@ class DefaultCookieSerializerTests {
|
||||
this.serializer.setClock(Clock.fixed(Instant.parse("2019-10-07T20:10:00Z"), ZoneOffset.UTC));
|
||||
this.serializer.setCookieMaxAge(100);
|
||||
this.serializer.writeCookieValue(cookieValue(""));
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(0);
|
||||
assertThat(this.response.rawCookie).contains("Expires=Thu, 1 Jan 1970 00:00:00 GMT");
|
||||
MockCookie cookie = getCookie();
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(0);
|
||||
ZonedDateTime expires = cookie.getExpires();
|
||||
assertThat(expires).isNotNull();
|
||||
assertThat(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME)).isEqualTo("Thu, 1 Jan 1970 00:00:00 GMT");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -318,8 +325,11 @@ class DefaultCookieSerializerTests {
|
||||
CookieValue cookieValue = cookieValue(this.sessionId);
|
||||
cookieValue.setCookieMaxAge(100);
|
||||
this.serializer.writeCookieValue(cookieValue);
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
assertThat(this.response.rawCookie).contains("Expires=Mon, 7 Oct 2019 20:11:40 GMT");
|
||||
MockCookie cookie = getCookie();
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(100);
|
||||
ZonedDateTime expires = cookie.getExpires();
|
||||
assertThat(expires).isNotNull();
|
||||
assertThat(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME)).isEqualTo("Mon, 7 Oct 2019 20:11:40 GMT");
|
||||
}
|
||||
|
||||
// --- secure ---
|
||||
@@ -482,18 +492,4 @@ class DefaultCookieSerializerTests {
|
||||
return new CookieValue(this.request, this.response, cookieValue);
|
||||
}
|
||||
|
||||
private static class CookiePreservingMockHttpServletResponse extends MockHttpServletResponse {
|
||||
|
||||
private String rawCookie;
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, String value) {
|
||||
if (HttpHeaders.SET_COOKIE.equals(name)) {
|
||||
this.rawCookie = value;
|
||||
}
|
||||
super.addHeader(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ import org.springframework.util.Assert;
|
||||
* <p>
|
||||
* 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 sesion id. The body of the event will be
|
||||
* "33fdd1b6-b496-4b33-9f7d-df96679d32fe" is the session id. The body of the event will be
|
||||
* the session that was created.
|
||||
* </p>
|
||||
*
|
||||
|
||||
@@ -46,5 +46,7 @@ asciidoctor {
|
||||
'spring-session-version': project.version,
|
||||
'version-milestone': milestoneBuild,
|
||||
'version-release': releaseBuild,
|
||||
'version-snapshot': snapshotBuild
|
||||
'version-snapshot': snapshotBuild,
|
||||
'highlightjsdir@': "js/highlight",
|
||||
'docinfodir@': "."
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
= Spring Session - find by username
|
||||
Rob Winch
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to find sessions by username.
|
||||
|
||||
NOTE: You can find the completed guide in the <<findbyusername-sample, findbyusername application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
[[findbyusername-assumptions]]
|
||||
== Assumptions
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
= Spring Session - Spring Boot
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` when you use Spring Boot.
|
||||
|
||||
NOTE: You can find the completed guide in the <<httpsession-jdbc-boot-sample, httpsession-jdbc-boot sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
|
||||
Before you use Spring Session, you must update your dependencies.
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
= Spring Session - Spring Boot
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when you use Spring Boot.
|
||||
|
||||
NOTE: You can find the completed guide in the <<boot-sample, boot sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
|
||||
Before you use Spring Session, you must ensure your dependencies.
|
||||
@@ -151,6 +157,6 @@ To do so, enter the following into your terminal, being sure to replace `7e8383a
|
||||
----
|
||||
$ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
----
|
||||
=====
|
||||
====
|
||||
|
||||
Now you can visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
= Spring Session - WebSocket
|
||||
Rob Winch
|
||||
:toc:
|
||||
:toc: left
|
||||
:websocketdoc-test-dir: {docs-test-dir}docs/websocket/
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to ensure that WebSocket messages keep your HttpSession alive.
|
||||
|
||||
@@ -12,6 +15,9 @@ Specifically,it does not work with using https://www.jcp.org/en/jsr/detail?id=35
|
||||
|
||||
// end::disclaimer[]
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== HttpSession Setup
|
||||
|
||||
The first step is to integrate Spring Session with the HttpSession. These steps are already outlined in the link:httpsession.html[HttpSession Guide].
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<script type="text/javascript" src="../js/tocbot/tocbot.min.js"></script>
|
||||
<script type="text/javascript" src="../js/toc.js"></script>
|
||||
@@ -1,12 +1,18 @@
|
||||
= Spring Session - Custom Cookie
|
||||
Rob Winch
|
||||
:toc:
|
||||
Rob Winch; Eleftheria Stein-Kousathana
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to configure Spring Session to use custom cookies with Java Configuration.
|
||||
The guide assumes you have already link:./httpsession.html[set up Spring Session in your project].
|
||||
|
||||
NOTE: You can find the completed guide in the <<custom-cookie-sample, Custom Cookie sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
[[custom-cookie-spring-configuration]]
|
||||
== Spring Java Configuration
|
||||
|
||||
@@ -58,6 +64,9 @@ See `domainNamePattern` as an alternative.
|
||||
The pattern should provide a single grouping that is used to extract the value of the cookie domain.
|
||||
If the regular expression does not match, no domain is set and the existing domain is used.
|
||||
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] is used as the domain.
|
||||
* `sameSite`: The value for the `SameSite` cookie directive.
|
||||
To disable the serialization of the `SameSite` cookie directive, you may set this value to `null`.
|
||||
Default: `Lax`
|
||||
|
||||
WARNING: You should only match on valid domain characters, since the domain name is reflected in the response.
|
||||
Doing so prevents a malicious user from performing such attacks as https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
= Spring Session and Spring Security with Hazelcast
|
||||
Tommy Ludwig; Rob Winch
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session along with Spring Security when you use Hazelcast as your data store.
|
||||
It assumes that you have already applied Spring Security to your application.
|
||||
|
||||
NOTE: You cand find the completed guide in the <<hazelcast-spring-security-sample, Hazelcast Spring Security sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
|
||||
Before you use Spring Session, you must update your dependencies.
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage a relational database to back a web application's `HttpSession` with Java Configuration.
|
||||
|
||||
NOTE: You can find the completed guide in the <<httpsession-jdbc-sample, httpsession-jdbc sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
|
||||
Before you use Spring Session, you must update your dependencies.
|
||||
@@ -99,7 +105,7 @@ For additional information on how to configure data access related concerns, see
|
||||
|
||||
== Java Servlet Container Initialization
|
||||
|
||||
Our <<httpsession-spring-configuration,Spring Configuration>> created a Spring bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
Our <<httpsession-jdbc-spring-configuration,Spring Configuration>> created a Spring bean named `springSessionRepositoryFilter` that implements `Filter`.
|
||||
The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session.
|
||||
|
||||
In order for our `Filter` to do its magic, Spring needs to load our `Config` class.
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch
|
||||
:toc:
|
||||
:toc: left
|
||||
:version-snapshot: true
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` with Java Configuration.
|
||||
|
||||
NOTE: You can find the completed guide in the <<httpsession-sample, httpsession sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must update your dependencies.
|
||||
If you are using Maven, you must add the following dependencies:
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
= Spring Session - REST
|
||||
Rob Winch
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when you use REST endpoints.
|
||||
|
||||
NOTE: You can find the completed guide in the <<rest-sample, rest sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
|
||||
Before you use Spring Session, you must update your dependencies.
|
||||
@@ -241,7 +247,7 @@ $ curl -v http://localhost:8080/ -u user:password
|
||||
|
||||
In the output, you should notice the following:
|
||||
|
||||
===
|
||||
====
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
...
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
= Spring Session and Spring Security
|
||||
Rob Winch
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session along with Spring Security.
|
||||
It assumes you have already applied Spring Security to your application.
|
||||
|
||||
NOTE: You can find the completed guide in the <<security-sample, security sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must update your dependencies.
|
||||
If you use Maven, you must add the following dependencies:
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch, Vedran Pavić
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage a relational to back a web application's `HttpSession` with XML based configuration.
|
||||
|
||||
NOTE: You can find the completed guide in the <<httpsession-jdbc-xml-sample, httpsession-jdbc-xml sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
|
||||
Before you use Spring Session, you must update your dependencies.
|
||||
@@ -68,8 +74,8 @@ You must have the following in your pom.xml:
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
endif::[]
|
||||
====
|
||||
endif::[]
|
||||
|
||||
// tag::config[]
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
= Spring Session - HttpSession (Quick Start)
|
||||
Rob Winch
|
||||
:toc:
|
||||
:toc: left
|
||||
:stylesdir: ../
|
||||
:highlightjsdir: ../js/highlight
|
||||
:docinfodir: guides
|
||||
|
||||
This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` with XML-based configuration.
|
||||
|
||||
NOTE: You can find the completed guide in the <<httpsession-xml-sample, httpsession-xml sample application>>.
|
||||
|
||||
[#index-link]
|
||||
link:../index.html[Index]
|
||||
|
||||
== Updating Dependencies
|
||||
Before you use Spring Session, you must update your dependencies.
|
||||
If you use Maven, you must add the following dependencies:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
= Spring Session
|
||||
Rob Winch; Vedran Pavić; Jay Bryant
|
||||
Rob Winch; Vedran Pavić; Jay Bryant; Eleftheria Stein-Kousathana
|
||||
:doctype: book
|
||||
:indexdoc-tests: {docs-test-dir}docs/IndexDocTests.java
|
||||
:websocketdoc-test-dir: {docs-test-dir}docs/websocket/
|
||||
@@ -596,6 +596,7 @@ You can browse the complete link:../../api/[Javadoc] online. The key APIs are de
|
||||
* <<api-reactivemapsessionrepository>>
|
||||
* <<api-jdbcindexedsessionrepository>>
|
||||
* <<api-hazelcastindexedsessionrepository>>
|
||||
* <<api-cookieserializer>>
|
||||
|
||||
[[api-session]]
|
||||
=== Using `Session`
|
||||
@@ -1230,6 +1231,68 @@ Note that if you use Hazelcast's `MapStore` to persist your sessions `IMap`, the
|
||||
* Reloading triggers `EntryAddedListener` results in `SessionCreatedEvent` being re-published
|
||||
* Reloading uses default TTL for a given `IMap` results in sessions losing their original TTL
|
||||
|
||||
[[api-cookieserializer]]
|
||||
=== Using `CookieSerializer`
|
||||
|
||||
A `CookieSerializer` is responsible for defining how the session cookie is written.
|
||||
Spring Session comes with a default implementation using `DefaultCookieSerializer`.
|
||||
|
||||
[[api-cookieserializer-bean]]
|
||||
==== Exposing `CookieSerializer` as a bean
|
||||
Exposing the `CookieSerializer` as a Spring bean augments the existing configuration when you use configurations like `@EnableRedisHttpSession`.
|
||||
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{samples-dir}spring-session-sample-javaconfig-custom-cookie/src/main/java/sample/Config.java[tags=cookie-serializer]
|
||||
----
|
||||
|
||||
<1> We customize the name of the cookie to be `JSESSIONID`.
|
||||
<2> We customize the path of the cookie to be `/` (rather than the default of the context root).
|
||||
<3> We customize the domain name pattern (a regular expression) to be `^.+?\\.(\\w+\\.[a-z]+)$`.
|
||||
This allows sharing a session across domains and applications.
|
||||
If the regular expression does not match, no domain is set and the existing domain is used.
|
||||
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] is used as the domain.
|
||||
This means that a request to https://child.example.com sets the domain to `example.com`.
|
||||
However, a request to http://localhost:8080/ or https://192.168.1.100:8080/ leaves the cookie unset and, thus, still works in development without any changes being necessary for production.
|
||||
====
|
||||
|
||||
WARNING: You should only match on valid domain characters, since the domain name is reflected in the response.
|
||||
Doing so prevents a malicious user from performing such attacks as https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
|
||||
|
||||
[[api-cookieserializer-customization]]
|
||||
==== Customizing `CookieSerializer`
|
||||
|
||||
You can customize how the session cookie is written by using any of the following configuration options on the `DefaultCookieSerializer`.
|
||||
|
||||
* `cookieName`: The name of the cookie to use.
|
||||
Default: `SESSION`.
|
||||
* `useSecureCookie`: Specifies whether a secure cookie should be used.
|
||||
Default: Use the value of `HttpServletRequest.isSecure()` at the time of creation.
|
||||
* `cookiePath`: The path of the cookie.
|
||||
Default: The context root.
|
||||
* `cookieMaxAge`: Specifies the max age of the cookie to be set at the time the session is created.
|
||||
Default: `-1`, which indicates the cookie should be removed when the browser is closed.
|
||||
* `jvmRoute`: Specifies a suffix to be appended to the session ID and included in the cookie.
|
||||
Used to identify which JVM to route to for session affinity.
|
||||
With some implementations (that is, Redis) this option provides no performance benefit.
|
||||
However, it can help with tracing logs of a particular user.
|
||||
* `domainName`: Allows specifying a specific domain name to be used for the cookie.
|
||||
This option is simple to understand but often requires a different configuration between development and production environments.
|
||||
See `domainNamePattern` as an alternative.
|
||||
* `domainNamePattern`: A case-insensitive pattern used to extract the domain name from the `HttpServletRequest#getServerName()`.
|
||||
The pattern should provide a single grouping that is used to extract the value of the cookie domain.
|
||||
If the regular expression does not match, no domain is set and the existing domain is used.
|
||||
If the regular expression matches, the first https://docs.oracle.com/javase/tutorial/essential/regex/groups.html[grouping] is used as the domain.
|
||||
* `sameSite`: The value for the `SameSite` cookie directive.
|
||||
To disable the serialization of the `SameSite` cookie directive, you may set this value to `null`.
|
||||
Default: `Lax`
|
||||
|
||||
WARNING: You should only match on valid domain characters, since the domain name is reflected in the response.
|
||||
Doing so prevents a malicious user from performing such attacks as https://en.wikipedia.org/wiki/HTTP_response_splitting[HTTP Response Splitting].
|
||||
|
||||
[[custom-sessionrepository]]
|
||||
== Customing `SessionRepository`
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package docs.security;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.Customizer;
|
||||
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;
|
||||
@@ -41,14 +42,16 @@ public class RememberMeSecurityConfiguration extends WebSecurityConfigurerAdapte
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// ... additional configuration ...
|
||||
.rememberMe()
|
||||
.rememberMeServices(rememberMeServices());
|
||||
.rememberMe((rememberMe) -> rememberMe
|
||||
.rememberMeServices(rememberMeServices())
|
||||
);
|
||||
// end::http-rememberme[]
|
||||
|
||||
http
|
||||
.formLogin().and()
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated();
|
||||
.formLogin(Customizer.withDefaults())
|
||||
.authorizeRequests((authorize) -> authorize
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
}
|
||||
|
||||
// tag::rememberme-bean[]
|
||||
|
||||
@@ -40,9 +40,10 @@ public class SecurityConfiguration<S extends Session> extends WebSecurityConfigu
|
||||
// @formatter:off
|
||||
http
|
||||
// other config goes here...
|
||||
.sessionManagement()
|
||||
.sessionManagement((sessionManagement) -> sessionManagement
|
||||
.maximumSessions(2)
|
||||
.sessionRegistry(sessionRegistry());
|
||||
.sessionRegistry(sessionRegistry())
|
||||
);
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ import org.springframework.util.StringUtils;
|
||||
* );
|
||||
*
|
||||
* CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
|
||||
* CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (EXPIRY_TIME);
|
||||
* CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
|
||||
* CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
|
||||
*
|
||||
* CREATE TABLE SPRING_SESSION_ATTRIBUTES (
|
||||
|
||||
@@ -35,13 +35,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.authorizeRequests((authorize) -> authorize
|
||||
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
)
|
||||
.formLogin((formLogin) -> formLogin
|
||||
.loginPage("/login")
|
||||
.permitAll();
|
||||
.permitAll()
|
||||
);
|
||||
}
|
||||
// end::config[]
|
||||
// @formatter:on
|
||||
|
||||
@@ -44,12 +44,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.authorizeRequests((authorize) -> authorize
|
||||
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.permitAll();
|
||||
)
|
||||
.formLogin((formLogin) -> formLogin
|
||||
.permitAll()
|
||||
);
|
||||
}
|
||||
// end::config[]
|
||||
// @formatter:on
|
||||
|
||||
@@ -34,13 +34,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.authorizeRequests((authorize) -> authorize
|
||||
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
)
|
||||
.formLogin((formLogin) -> formLogin
|
||||
.loginPage("/login")
|
||||
.permitAll();
|
||||
.permitAll()
|
||||
);
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
|
||||
@@ -28,12 +28,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.authorizeRequests((authorize) -> authorize
|
||||
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.permitAll();
|
||||
)
|
||||
.formLogin((formLogin) -> formLogin
|
||||
.permitAll()
|
||||
);
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
|
||||
@@ -35,12 +35,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.authorizeRequests((authorize) -> authorize
|
||||
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.permitAll();
|
||||
)
|
||||
.formLogin((formLogin) -> formLogin
|
||||
.permitAll()
|
||||
);
|
||||
}
|
||||
// end::config[]
|
||||
// @formatter:on
|
||||
|
||||
@@ -53,12 +53,13 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.authorizeRequests((authorize) -> authorize
|
||||
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.permitAll();
|
||||
)
|
||||
.formLogin((formLogin) -> formLogin
|
||||
.permitAll()
|
||||
);
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Description</h1>
|
||||
<p>This application demonstrates how to use a Redis instance to back your session. Notice that there is no JSESSIONID cookie. We are also able to customize the way of identifying what the requested session id is.</p>
|
||||
<p>This application demonstrates how to customize the session cookie. Notice that the name of the cookie is JSESSIONID.</p>
|
||||
|
||||
<h1>Try it</h1>
|
||||
|
||||
|
||||
@@ -37,4 +37,4 @@ public class SessionServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = 2878267318695777395L;
|
||||
|
||||
}
|
||||
// tag::end[]
|
||||
// end::class[]
|
||||
|
||||
@@ -38,4 +38,4 @@ public class SessionServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = 2878267318695777395L;
|
||||
|
||||
}
|
||||
// tag::end[]
|
||||
// end::class[]
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package sample;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@@ -31,13 +32,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeRequests()
|
||||
.authorizeRequests((authorize) -> authorize
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.requestCache()
|
||||
)
|
||||
.requestCache((requestCache) -> requestCache
|
||||
.requestCache(new NullRequestCache())
|
||||
.and()
|
||||
.httpBasic();
|
||||
)
|
||||
.httpBasic(Customizer.withDefaults());
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
|
||||
Reference in New Issue
Block a user