Add support for SameSite cookie directive
Closes gh-1005
This commit is contained in:
@@ -16,8 +16,13 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -41,6 +46,22 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultCookieSerializer.class);
|
||||
|
||||
private static final BitSet domainValid = new BitSet(128);
|
||||
|
||||
static {
|
||||
for (char c = '0'; c <= '9'; c++) {
|
||||
domainValid.set(c);
|
||||
}
|
||||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
domainValid.set(c);
|
||||
}
|
||||
for (char c = 'A'; c <= 'Z'; c++) {
|
||||
domainValid.set(c);
|
||||
}
|
||||
domainValid.set('.');
|
||||
domainValid.set('-');
|
||||
}
|
||||
|
||||
private String cookieName = "SESSION";
|
||||
|
||||
private Boolean useSecureCookie;
|
||||
@@ -61,6 +82,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
|
||||
private String rememberMeRequestAttribute;
|
||||
|
||||
private String sameSite = "Lax";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
@@ -75,7 +98,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (this.cookieName.equals(cookie.getName())) {
|
||||
String sessionId = (this.useBase64Encoding
|
||||
? base64Decode(cookie.getValue()) : cookie.getValue());
|
||||
? base64Decode(cookie.getValue())
|
||||
: cookie.getValue());
|
||||
if (sessionId == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -101,37 +125,43 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
HttpServletRequest request = cookieValue.getRequest();
|
||||
HttpServletResponse response = cookieValue.getResponse();
|
||||
|
||||
String requestedCookieValue = cookieValue.getCookieValue();
|
||||
String actualCookieValue = (this.jvmRoute != null
|
||||
? requestedCookieValue + this.jvmRoute : requestedCookieValue);
|
||||
|
||||
Cookie sessionCookie = new Cookie(this.cookieName, this.useBase64Encoding
|
||||
? base64Encode(actualCookieValue) : actualCookieValue);
|
||||
sessionCookie.setSecure(isSecureCookie(request));
|
||||
sessionCookie.setPath(getCookiePath(request));
|
||||
String domainName = getDomainName(request);
|
||||
if (domainName != null) {
|
||||
sessionCookie.setDomain(domainName);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.cookieName).append('=');
|
||||
String value = getValue(cookieValue);
|
||||
if (value != null && value.length() > 0) {
|
||||
validateValue(value);
|
||||
sb.append(value);
|
||||
}
|
||||
int maxAge = getMaxAge(cookieValue);
|
||||
if (maxAge > -1) {
|
||||
sb.append("; Max-Age=").append(cookieValue.getCookieMaxAge());
|
||||
OffsetDateTime expires = (maxAge != 0
|
||||
? OffsetDateTime.now().plusSeconds(maxAge)
|
||||
: Instant.EPOCH.atOffset(ZoneOffset.UTC));
|
||||
sb.append("; Expires=")
|
||||
.append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));
|
||||
}
|
||||
String domain = getDomainName(request);
|
||||
if (domain != null && domain.length() > 0) {
|
||||
validateDomain(domain);
|
||||
sb.append("; Domain=").append(domain);
|
||||
}
|
||||
String path = getCookiePath(request);
|
||||
if (path != null && path.length() > 0) {
|
||||
validatePath(path);
|
||||
sb.append("; Path=").append(path);
|
||||
}
|
||||
if (isSecureCookie(request)) {
|
||||
sb.append("; Secure");
|
||||
}
|
||||
|
||||
if (this.useHttpOnlyCookie) {
|
||||
sessionCookie.setHttpOnly(true);
|
||||
sb.append("; HttpOnly");
|
||||
}
|
||||
if (this.sameSite != null) {
|
||||
sb.append("; SameSite=").append(this.sameSite);
|
||||
}
|
||||
|
||||
if (cookieValue.getCookieMaxAge() < 0) {
|
||||
if (this.rememberMeRequestAttribute != null
|
||||
&& request.getAttribute(this.rememberMeRequestAttribute) != null) {
|
||||
// the cookie is only written at time of session creation, so we rely on
|
||||
// session expiration rather than cookie expiration if remember me is enabled
|
||||
cookieValue.setCookieMaxAge(Integer.MAX_VALUE);
|
||||
}
|
||||
else if (this.cookieMaxAge != null) {
|
||||
cookieValue.setCookieMaxAge(this.cookieMaxAge);
|
||||
}
|
||||
}
|
||||
sessionCookie.setMaxAge(cookieValue.getCookieMaxAge());
|
||||
|
||||
response.addCookie(sessionCookie);
|
||||
response.addHeader("Set-Cookie", sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,6 +192,81 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
return new String(encodedCookieBytes);
|
||||
}
|
||||
|
||||
private String getValue(CookieValue cookieValue) {
|
||||
String requestedCookieValue = cookieValue.getCookieValue();
|
||||
String actualCookieValue = requestedCookieValue;
|
||||
if (this.jvmRoute != null) {
|
||||
actualCookieValue = requestedCookieValue + this.jvmRoute;
|
||||
}
|
||||
if (this.useBase64Encoding) {
|
||||
actualCookieValue = base64Encode(actualCookieValue);
|
||||
}
|
||||
return actualCookieValue;
|
||||
}
|
||||
|
||||
private void validateValue(String value) {
|
||||
int start = 0;
|
||||
int end = value.length();
|
||||
if ((end > 1) && (value.charAt(0) == '"') && (value.charAt(end - 1) == '"')) {
|
||||
start = 1;
|
||||
end--;
|
||||
}
|
||||
char[] chars = value.toCharArray();
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = chars[i];
|
||||
if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c
|
||||
|| c == 0x7f) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid character in cookie value: " + Integer.toString(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getMaxAge(CookieValue cookieValue) {
|
||||
int maxAge = cookieValue.getCookieMaxAge();
|
||||
if (maxAge < 0) {
|
||||
if (this.rememberMeRequestAttribute != null && cookieValue.getRequest()
|
||||
.getAttribute(this.rememberMeRequestAttribute) != null) {
|
||||
// the cookie is only written at time of session creation, so we rely on
|
||||
// session expiration rather than cookie expiration if remember me is
|
||||
// enabled
|
||||
cookieValue.setCookieMaxAge(Integer.MAX_VALUE);
|
||||
}
|
||||
else if (this.cookieMaxAge != null) {
|
||||
cookieValue.setCookieMaxAge(this.cookieMaxAge);
|
||||
}
|
||||
}
|
||||
return cookieValue.getCookieMaxAge();
|
||||
}
|
||||
|
||||
private void validateDomain(String domain) {
|
||||
int i = 0;
|
||||
int cur = -1;
|
||||
int prev;
|
||||
char[] chars = domain.toCharArray();
|
||||
while (i < chars.length) {
|
||||
prev = cur;
|
||||
cur = chars[i];
|
||||
if (!domainValid.get(cur)
|
||||
|| ((prev == '.' || prev == -1) && (cur == '.' || cur == '-'))
|
||||
|| (prev == '-' && cur == '.')) {
|
||||
throw new IllegalArgumentException("Invalid cookie domain: " + domain);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (cur == '.' || cur == '-') {
|
||||
throw new IllegalArgumentException("Invalid cookie domain: " + domain);
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePath(String path) {
|
||||
for (char ch : path.toCharArray()) {
|
||||
if (ch < 0x20 || ch > 0x7E || ch == ';') {
|
||||
throw new IllegalArgumentException("Invalid cookie path: " + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if a Cookie marked as secure should be used. The default is to use the value
|
||||
* of {@link HttpServletRequest#isSecure()}.
|
||||
@@ -317,6 +422,16 @@ public class DefaultCookieSerializer implements CookieSerializer {
|
||||
this.rememberMeRequestAttribute = rememberMeRequestAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for the {@code SameSite} cookie directive. The default value is
|
||||
* {@code Lax}.
|
||||
* @param sameSite the SameSite directive value
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public void setSameSite(String sameSite) {
|
||||
this.sameSite = sameSite;
|
||||
}
|
||||
|
||||
private String getDomainName(HttpServletRequest request) {
|
||||
if (this.domainName != null) {
|
||||
return this.domainName;
|
||||
|
||||
@@ -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,12 +18,14 @@ package org.springframework.session.web.http;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.MapSession;
|
||||
@@ -83,7 +85,7 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
|
||||
assertThat(this.response.getCookies()).hasSize(1);
|
||||
assertThat(this.response.getHeaders("Set-Cookie")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -93,11 +95,12 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
this.strategy.setSessionId(this.request, this.response, newSession.getId());
|
||||
|
||||
Cookie[] cookies = this.response.getCookies();
|
||||
List<ResponseCookie> cookies = ResponseCookieParser.parse(this.response);
|
||||
assertThat(cookies).hasSize(2);
|
||||
|
||||
assertThat(base64Decode(cookies[0].getValue())).isEqualTo(this.session.getId());
|
||||
assertThat(base64Decode(cookies[1].getValue())).isEqualTo(newSession.getId());
|
||||
assertThat(base64Decode(cookies.get(0).getValue()))
|
||||
.isEqualTo(this.session.getId());
|
||||
assertThat(base64Decode(cookies.get(1).getValue())).isEqualTo(newSession.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,7 +108,7 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.request.setContextPath("/somethingunique");
|
||||
this.strategy.setSessionId(this.request, this.response, this.session.getId());
|
||||
|
||||
Cookie sessionCookie = this.response.getCookie(this.cookieName);
|
||||
ResponseCookie sessionCookie = getCookie();
|
||||
assertThat(sessionCookie.getPath())
|
||||
.isEqualTo(this.request.getContextPath() + "/");
|
||||
}
|
||||
@@ -128,7 +131,7 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.request.setContextPath("/somethingunique");
|
||||
this.strategy.expireSession(this.request, this.response);
|
||||
|
||||
Cookie sessionCookie = this.response.getCookie(this.cookieName);
|
||||
ResponseCookie sessionCookie = getCookie();
|
||||
assertThat(sessionCookie.getPath())
|
||||
.isEqualTo(this.request.getContextPath() + "/");
|
||||
}
|
||||
@@ -173,8 +176,12 @@ public class CookieHttpSessionIdResolverTests {
|
||||
this.request.setCookies(new Cookie(this.cookieName, base64Encode(value)));
|
||||
}
|
||||
|
||||
private ResponseCookie getCookie() {
|
||||
return ResponseCookieParser.parse(this.response, this.cookieName);
|
||||
}
|
||||
|
||||
private String getSessionId() {
|
||||
return base64Decode(this.response.getCookie(this.cookieName).getValue());
|
||||
return base64Decode(getCookie().getValue());
|
||||
}
|
||||
|
||||
private static String base64Encode(String value) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
@@ -324,7 +325,7 @@ public class DefaultCookieSerializerTests {
|
||||
public void writeCookieCookieMaxAgeDefault() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(-1);
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -333,7 +334,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -342,7 +343,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(""));
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(0);
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -352,7 +353,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue);
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(100);
|
||||
}
|
||||
|
||||
// --- secure ---
|
||||
@@ -361,7 +362,7 @@ public class DefaultCookieSerializerTests {
|
||||
public void writeCookieDefaultInsecureRequest() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
assertThat(getCookie().isSecure()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -371,7 +372,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isTrue();
|
||||
assertThat(getCookie().isSecure()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -380,7 +381,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isTrue();
|
||||
assertThat(getCookie().isSecure()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -390,7 +391,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
assertThat(getCookie().isSecure()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -399,7 +400,7 @@ public class DefaultCookieSerializerTests {
|
||||
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSecure()).isFalse();
|
||||
assertThat(getCookie().isSecure()).isFalse();
|
||||
}
|
||||
|
||||
// --- jvmRoute ---
|
||||
@@ -452,7 +453,7 @@ public class DefaultCookieSerializerTests {
|
||||
this.serializer.setRememberMeRequestAttribute("rememberMe");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(Integer.MAX_VALUE);
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -463,7 +464,40 @@ public class DefaultCookieSerializerTests {
|
||||
cookieValue.setCookieMaxAge(100);
|
||||
this.serializer.writeCookieValue(cookieValue);
|
||||
|
||||
assertThat(getCookie().getMaxAge()).isEqualTo(100);
|
||||
assertThat(getCookie().getMaxAge().getSeconds()).isEqualTo(100);
|
||||
}
|
||||
|
||||
// --- sameSite ---
|
||||
|
||||
@Test
|
||||
public void writeCookieDefaultSameSiteLax() {
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteLax() {
|
||||
this.serializer.setSameSite("Lax");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteStrict() {
|
||||
this.serializer.setSameSite("Strict");
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isEqualTo("Strict");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeCookieSetSameSiteNull() {
|
||||
this.serializer.setSameSite(null);
|
||||
this.serializer.writeCookieValue(cookieValue(this.sessionId));
|
||||
|
||||
assertThat(getCookie().getSameSite()).isNull();
|
||||
}
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
@@ -478,8 +512,8 @@ public class DefaultCookieSerializerTests {
|
||||
return new Cookie(name, value);
|
||||
}
|
||||
|
||||
private Cookie getCookie() {
|
||||
return this.response.getCookie(this.cookieName);
|
||||
private ResponseCookie getCookie() {
|
||||
return ResponseCookieParser.parse(this.response, this.cookieName);
|
||||
}
|
||||
|
||||
private String getCookieValue() {
|
||||
@@ -487,9 +521,6 @@ public class DefaultCookieSerializerTests {
|
||||
if (!this.useBase64Encoding) {
|
||||
return value;
|
||||
}
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(Base64.getDecoder().decode(value));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
final class ResponseCookieParser {
|
||||
|
||||
private ResponseCookieParser() {
|
||||
}
|
||||
|
||||
static List<ResponseCookie> parse(HttpServletResponse response) {
|
||||
return doParse(response, null);
|
||||
}
|
||||
|
||||
static ResponseCookie parse(HttpServletResponse response, String cookieName) {
|
||||
List<ResponseCookie> responseCookies = doParse(response, cookieName);
|
||||
return (!responseCookies.isEmpty() ? responseCookies.get(0) : null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<ResponseCookie> doParse(HttpServletResponse response,
|
||||
String cookieName) {
|
||||
List<ResponseCookie> responseCookies = new ArrayList<>();
|
||||
for (String setCookieHeader : response.getHeaders(HttpHeaders.SET_COOKIE)) {
|
||||
String[] cookieParts = setCookieHeader.split("\\s*=\\s*", 2);
|
||||
if (cookieParts.length != 2) {
|
||||
return null;
|
||||
}
|
||||
String name = cookieParts[0];
|
||||
if (cookieName != null && !name.equals(cookieName)) {
|
||||
continue;
|
||||
}
|
||||
String[] valueAndDirectives = cookieParts[1].split("\\s*;\\s*", 2);
|
||||
String value = valueAndDirectives[0];
|
||||
String[] directives = valueAndDirectives[1].split("\\s*;\\s*");
|
||||
String domain = null;
|
||||
int maxAge = -1;
|
||||
String path = null;
|
||||
boolean secure = false;
|
||||
boolean httpOnly = false;
|
||||
String sameSite = null;
|
||||
for (String directive : directives) {
|
||||
if (directive.startsWith("Domain")) {
|
||||
domain = directive.split("=")[1];
|
||||
}
|
||||
if (directive.startsWith("Max-Age")) {
|
||||
maxAge = Integer.parseInt(directive.split("=")[1]);
|
||||
}
|
||||
if (directive.startsWith("Path")) {
|
||||
path = directive.split("=")[1];
|
||||
}
|
||||
if (directive.startsWith("Secure")) {
|
||||
secure = true;
|
||||
}
|
||||
if (directive.startsWith("HttpOnly")) {
|
||||
httpOnly = true;
|
||||
}
|
||||
if (directive.startsWith("SameSite")) {
|
||||
sameSite = directive.split("=")[1];
|
||||
}
|
||||
}
|
||||
responseCookies.add(ResponseCookie.from(name, value).maxAge(maxAge).path(path)
|
||||
.domain(domain).secure(secure).httpOnly(httpOnly).sameSite(sameSite)
|
||||
.build());
|
||||
}
|
||||
return responseCookies;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,6 +51,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
@@ -423,7 +424,7 @@ public class SessionRepositoryFilterTests {
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(this.response.getCookie("SESSION")).isNull();
|
||||
assertThat(getSessionCookie()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -441,7 +442,7 @@ public class SessionRepositoryFilterTests {
|
||||
wrappedRequest.getSession();
|
||||
}
|
||||
});
|
||||
assertThat(this.response.getCookie("SESSION")).isNotNull();
|
||||
assertThat(getSessionCookie()).isNotNull();
|
||||
|
||||
nextRequest();
|
||||
|
||||
@@ -453,7 +454,7 @@ public class SessionRepositoryFilterTests {
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(this.response.getCookie("SESSION")).isNotNull();
|
||||
assertThat(getSessionCookie()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -653,10 +654,10 @@ public class SessionRepositoryFilterTests {
|
||||
}
|
||||
});
|
||||
|
||||
Cookie session = getSessionCookie();
|
||||
ResponseCookie session = getSessionCookie();
|
||||
assertThat(session.isHttpOnly()).describedAs("Session Cookie should be HttpOnly")
|
||||
.isTrue();
|
||||
assertThat(session.getSecure())
|
||||
assertThat(session.isSecure())
|
||||
.describedAs("Session Cookie should be marked as Secure").isTrue();
|
||||
}
|
||||
|
||||
@@ -1509,13 +1510,13 @@ public class SessionRepositoryFilterTests {
|
||||
// --- helper methods
|
||||
|
||||
private void assertNewSession() {
|
||||
Cookie cookie = getSessionCookie();
|
||||
ResponseCookie cookie = getSessionCookie();
|
||||
assertThat(cookie).isNotNull();
|
||||
assertThat(cookie.getMaxAge()).isEqualTo(-1);
|
||||
assertThat(cookie.getMaxAge().getSeconds()).isEqualTo(-1);
|
||||
assertThat(cookie.getValue()).isNotEqualTo("INVALID");
|
||||
assertThat(cookie.isHttpOnly()).describedAs("Cookie is expected to be HTTP Only")
|
||||
.isTrue();
|
||||
assertThat(cookie.getSecure())
|
||||
assertThat(cookie.isSecure())
|
||||
.describedAs(
|
||||
"Cookie secured is expected to be " + this.request.isSecure())
|
||||
.isEqualTo(this.request.isSecure());
|
||||
@@ -1525,15 +1526,15 @@ public class SessionRepositoryFilterTests {
|
||||
}
|
||||
|
||||
private void assertNoSession() {
|
||||
Cookie cookie = getSessionCookie();
|
||||
ResponseCookie cookie = getSessionCookie();
|
||||
assertThat(cookie).isNull();
|
||||
assertThat(this.request.getSession(false))
|
||||
.describedAs("The original HttpServletRequest HttpSession should be null")
|
||||
.isNull();
|
||||
}
|
||||
|
||||
private Cookie getSessionCookie() {
|
||||
return this.response.getCookie("SESSION");
|
||||
private ResponseCookie getSessionCookie() {
|
||||
return ResponseCookieParser.parse(this.response, "SESSION");
|
||||
}
|
||||
|
||||
private void setSessionCookie(String sessionId) {
|
||||
@@ -1556,6 +1557,9 @@ public class SessionRepositoryFilterTests {
|
||||
for (Cookie cookie : this.response.getCookies()) {
|
||||
nameToCookie.put(cookie.getName(), cookie);
|
||||
}
|
||||
ResponseCookieParser.parse(this.response)
|
||||
.forEach((responseCookie) -> nameToCookie.put(responseCookie.getName(),
|
||||
toServletCookie(responseCookie)));
|
||||
Cookie[] nextRequestCookies = new ArrayList<>(nameToCookie.values())
|
||||
.toArray(new Cookie[0]);
|
||||
|
||||
@@ -1586,6 +1590,19 @@ public class SessionRepositoryFilterTests {
|
||||
return new String(Base64.getDecoder().decode(value));
|
||||
}
|
||||
|
||||
private static Cookie toServletCookie(ResponseCookie responseCookie) {
|
||||
Cookie cookie = new Cookie(responseCookie.getName(), responseCookie.getValue());
|
||||
String domain = responseCookie.getDomain();
|
||||
if (domain != null) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
cookie.setMaxAge((int) responseCookie.getMaxAge().getSeconds());
|
||||
cookie.setPath(responseCookie.getPath());
|
||||
cookie.setSecure(responseCookie.isSecure());
|
||||
cookie.setHttpOnly(responseCookie.isHttpOnly());
|
||||
return cookie;
|
||||
}
|
||||
|
||||
private static class SessionRepositoryFilterDefaultOrder implements Ordered {
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user