Compare commits

..

21 Commits

Author SHA1 Message Date
Eleftheria Stein
3cbd3a9e25 Release 2.3.0.M1 2020-01-29 21:54:34 +01:00
Eleftheria Stein
4c914d46c9 Upgrade Spring Framework to 5.2.3.RELEASE
Resolves: #1575
2020-01-29 21:47:32 +01:00
Eleftheria Stein
adf411ecc3 Upgrade Spring Security to 5.3.0.M1
Resolves: #1568
2020-01-29 21:12:50 +01:00
Eleftheria Stein
95b39a203f Upgrade Spring Data to Neumann-M2
Resolves: #1567
2020-01-29 21:12:06 +01:00
Eleftheria Stein
3d653b3b50 Next Development Build 2020-01-29 21:08:14 +01:00
Eleftheria Stein
938fd3c2e5 Next Development Build 2020-01-29 20:55:04 +01:00
Eleftheria Stein
45bb0f9b0c Run deployArtifacts before finalizeDeployArtifacts in build
This commit is needed to fix the release

Resolves: #1574
2020-01-29 16:39:07 +01:00
Eleftheria Stein
cddd84d564 Release 2.2.1.RELEASE 2020-01-28 10:57:10 +01:00
Eleftheria Stein
6931d40e6e Upgrade samples to Spring Boot 2.2.4.RELEASE
Resolves: #1563
2020-01-28 10:26:32 +01:00
Eleftheria Stein
3b672787f3 Upgrade Spring Data to Moore-SR4
Resolves: #1564
2020-01-28 10:22:14 +01:00
Eleftheria Stein
c0ee52b33b Upgrade Reactor to Dysprosium-SR4
Resolves: #1565
2020-01-28 10:21:32 +01:00
Eleftheria Stein
68f8641233 Upgrade Spring Security to 5.2.1.RELEASE
Resolves: #1566
2020-01-28 10:20:48 +01:00
Eleftheria Stein
e7b2af47e1 Upgrade Hazelcast to 3.12.5
Resolves: #1569
2020-01-28 10:19:16 +01:00
Eleftheria Stein
1ad6cbd7f8 Update note in custom-cookie index page
Resolves: gh-1559
2020-01-13 11:57:20 +01:00
Rob Winch
195af52d0b Upgrade to Spring Framework 5.2.2.RELEASE
Fixes gh-1548
2019-12-13 07:06:19 -06:00
Vedran Pavic
bc9d5f1299 Start building against Spring Framework 5.2.2.RELEASE snapshots
See: #1548
2019-11-16 10:20:11 +01:00
Vedran Pavic
3a4345eb6a Upgrade Gradle to 5.6.4 2019-11-15 22:23:23 +01:00
Vedran Pavic
6c41dea893 Polish contribution
Resolves: #1543
2019-11-15 22:10:35 +01:00
Eleftheria Stein
ee1d5b3b3c Document support for SameSite cookie directive
See: #1543
2019-11-15 22:06:13 +01:00
Christoph Dreis
89a4255679 Parse expression only once in PrincipalNameIndexResolver
Resolves: #1539
2019-11-15 12:13:42 +01:00
Rob Winch
6d2e51a0b9 Next Development Build 2019-10-15 15:32:01 -05:00
10 changed files with 102 additions and 40 deletions

3
Jenkinsfile vendored
View File

@@ -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'
}
}
}

View File

@@ -4,7 +4,7 @@ buildscript {
snapshotBuild = version.endsWith('SNAPSHOT')
milestoneBuild = !(releaseBuild || snapshotBuild)
springBootVersion = '2.2.0.RC1'
springBootVersion = '2.2.4.RELEASE'
}
repositories {

View File

@@ -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.M1

View File

@@ -1,15 +1,15 @@
dependencyManagement {
imports {
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-RELEASE'
mavenBom 'io.projectreactor:reactor-bom:Dysprosium-SR4'
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 'org.springframework:spring-framework-bom:5.2.3.RELEASE'
mavenBom 'org.springframework.data:spring-data-releasetrain:Neumann-M2'
mavenBom 'org.springframework.security:spring-security-bom:5.3.0.M1'
mavenBom 'org.testcontainers:testcontainers-bom:1.12.2'
}
dependencies {
dependencySet(group: 'com.hazelcast', version: '3.12.3') {
dependencySet(group: 'com.hazelcast', version: '3.12.5') {
entry 'hazelcast'
entry 'hazelcast-client'
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -1,5 +1,5 @@
= Spring Session - Custom Cookie
Rob Winch
Rob Winch; Eleftheria Stein-Kousathana
:toc:
This guide describes how to configure Spring Session to use custom cookies with Java Configuration.
@@ -58,6 +58,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].

View File

@@ -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`

View File

@@ -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>