diff --git a/spring-session/src/main/java/org/springframework/session/web/http/CookieHttpSessionStrategy.java b/spring-session/src/main/java/org/springframework/session/web/http/CookieHttpSessionStrategy.java index 4fec4b0c..06a50eeb 100644 --- a/spring-session/src/main/java/org/springframework/session/web/http/CookieHttpSessionStrategy.java +++ b/spring-session/src/main/java/org/springframework/session/web/http/CookieHttpSessionStrategy.java @@ -139,6 +139,11 @@ import org.springframework.util.Assert; */ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy, HttpSessionManager { + /** + * The default delimiter for both serialization and deserialization. + */ + private static final String DEFAULT_DELIMITER = " "; + private static final String SESSION_IDS_WRITTEN_ATTR = CookieHttpSessionStrategy.class .getName().concat(".SESSIONS_WRITTEN_ATTR"); @@ -152,6 +157,17 @@ public final class CookieHttpSessionStrategy private CookieSerializer cookieSerializer = new DefaultCookieSerializer(); + /** + * The delimiter between a session alias and a session id when reading a cookie value. The default value is " ". + */ + private String deserializationDelimiter = DEFAULT_DELIMITER; + + /** + * The delimiter between a session alias and a session id when writing a cookie value. + * The default is " ". + */ + private String serializationDelimiter = DEFAULT_DELIMITER; + public String getRequestedSessionId(HttpServletRequest request) { Map sessionIds = getSessionIds(request); String sessionAlias = getCurrentSessionAlias(request); @@ -238,9 +254,9 @@ public final class CookieHttpSessionStrategy String id = entry.getValue(); buffer.append(alias); - buffer.append(" "); + buffer.append(this.serializationDelimiter); buffer.append(id); - buffer.append(" "); + buffer.append(this.serializationDelimiter); } buffer.deleteCharAt(buffer.length() - 1); return buffer.toString(); @@ -290,12 +306,36 @@ public final class CookieHttpSessionStrategy this.cookieSerializer = serializer; } + /** + * Sets the delimiter between a session alias and a session id when deserializing a cookie. The default is " " + * This is useful when using RFC + * 6265 for writing the cookies which doesn't allow for spaces in the cookie + * values. + * + * @param delimiter the delimiter to set (i.e. "_ " will try a delimeter of either "_" or " ") + */ + public void setDeserializationDelimiter(String delimiter) { + this.deserializationDelimiter = delimiter; + } + + /** + * Sets the delimiter between a session alias and a session id when deserializing a cookie. The default is " ". + * This is useful when using RFC + * 6265 for writing the cookies which doesn't allow for spaces in the cookie + * values. + * + * @param delimiter the delimiter to set (i.e. "_") + */ + public void setSerializationDelimiter(String delimiter) { + this.serializationDelimiter = delimiter; + } + public Map getSessionIds(HttpServletRequest request) { List cookieValues = this.cookieSerializer.readCookieValues(request); String sessionCookieValue = cookieValues.isEmpty() ? "" : cookieValues.iterator().next(); Map result = new LinkedHashMap(); - StringTokenizer tokens = new StringTokenizer(sessionCookieValue, " "); + StringTokenizer tokens = new StringTokenizer(sessionCookieValue, this.deserializationDelimiter); if (tokens.countTokens() == 1) { result.put(DEFAULT_ALIAS, tokens.nextToken()); return result; diff --git a/spring-session/src/test/java/org/springframework/session/web/http/CookieHttpSessionStrategyTests.java b/spring-session/src/test/java/org/springframework/session/web/http/CookieHttpSessionStrategyTests.java index c446fd89..9bb1a6c0 100644 --- a/spring-session/src/test/java/org/springframework/session/web/http/CookieHttpSessionStrategyTests.java +++ b/spring-session/src/test/java/org/springframework/session/web/http/CookieHttpSessionStrategyTests.java @@ -114,6 +114,18 @@ public class CookieHttpSessionStrategyTests { .isEqualTo("0 " + existing.getId() + " new " + this.session.getId()); } + @Test + public void onNewSessionExistingSessionNewAliasCustomDelimiter() throws Exception { + this.strategy.setSerializationDelimiter("_"); + Session existing = new MapSession(); + setSessionCookie(existing.getId()); + this.request.setParameter( + CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new"); + this.strategy.onNewSession(this.session, this.request, this.response); + assertThat(getSessionId()) + .isEqualTo("0_" + existing.getId() + "_new_" + this.session.getId()); + } + // gh-321 @Test public void onNewSessionExplicitAlias() throws Exception { @@ -463,6 +475,53 @@ public class CookieHttpSessionStrategyTests { assertThat(sessionIds.get("1")).isEqualTo("b"); } + @Test + public void getSessionIdsMultiCustomDelimeter() { + this.strategy.setDeserializationDelimiter("_"); + setSessionCookie("0_a_1_b"); + + Map sessionIds = this.strategy.getSessionIds(this.request); + assertThat(sessionIds.size()).isEqualTo(2); + assertThat(sessionIds.get("0")).isEqualTo("a"); + assertThat(sessionIds.get("1")).isEqualTo("b"); + } + + @Test + public void getSessionIdsMultiCustomDelimeterMigration() { + this.strategy.setDeserializationDelimiter("_ "); + this.strategy.setSerializationDelimiter("_"); + + // can parse the old way + setSessionCookie("0 a 1 b"); + + Map sessionIds = this.strategy.getSessionIds(this.request); + assertThat(sessionIds.size()).isEqualTo(2); + assertThat(sessionIds.get("0")).isEqualTo("a"); + assertThat(sessionIds.get("1")).isEqualTo("b"); + + // can parse the new way + this.request = new MockHttpServletRequest(); + this.response = new MockHttpServletResponse(); + setSessionCookie("0_a_1_b"); + + sessionIds = this.strategy.getSessionIds(this.request); + assertThat(sessionIds.size()).isEqualTo(2); + assertThat(sessionIds.get("0")).isEqualTo("a"); + assertThat(sessionIds.get("1")).isEqualTo("b"); + + // writes the new way + this.request = new MockHttpServletRequest(); + this.response = new MockHttpServletResponse(); + Session existing = new MapSession(); + setSessionCookie(existing.getId()); + this.request.setParameter( + CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new"); + this.strategy.onNewSession(this.session, this.request, this.response); + assertThat(getSessionId()) + .isEqualTo("0_" + existing.getId() + "_new_" + this.session.getId()); + + } + @Test public void getSessionIdsDangling() { setSessionCookie("0 a 1 b noValue");