Fix CookieHttpSessionStrategy encodeUrl

Fix to https://support.pivotal.io/tickets/30469

I do notice 2 bugs in the spring-session code, both inside the
CookieHttpSessionStrategy class.

The first, using their own sample application, after getting a new
session alias from the HttpSessionManager, it calls
HttpSessionManager.encodeURL and then stores the output into a request
attribute which is later rendered. However, in a Spring MVC
application, a Controller may want to issue a redirect to a
newly-encoded URL, like this:

HttpSessionManager sessionManager = (HttpSessionManager)
request.getAttribute(HttpSessionManager.class.getName());
String newSessionAlias = sessionManager.getNewSessionAlias(request);
String currentSessionAlias =
sessionManager.getCurrentSessionAlias(request);

return String.format("redirect:%s",
sessionManager.encodeURL("/effectiveUser", newSessionAlias));

The problem here is that Spring MVC will funnel the redirect back
through
CookieHttpSessionStrategy.MultiSessionHttpServletResponse.encodeRedirect
URL, which then looks up the current alias & re-encodes with that
alias, effectively replacing the new alias which the controller added.
The fix would be for the
CookieHttpSessionStrategy.MultiSessionHttpServletResponse to examine
the inputted url for the appropriate parameter & don't re-encode if it
finds it there.

The second issue is that inside CookieHttpSessionStrategy line 346
(return path + "?" + query;) is that if there is no query string (on
the default session alias) then the resulting URL will end in a ? with
nothing after it. In that situation the ? should be removed as well
because the query string is empty.
This commit is contained in:
Eric Deandrea
2016-06-28 11:03:01 -04:00
committed by Rob Winch
parent 2052ec8d44
commit bff0f8f845
2 changed files with 792 additions and 595 deletions

View File

@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
@@ -158,7 +159,8 @@ 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 " ".
* The delimiter between a session alias and a session id when reading a cookie value.
* The default value is " ".
*/
private String deserializationDelimiter = DEFAULT_DELIMITER;
@@ -307,22 +309,23 @@ public final class CookieHttpSessionStrategy
}
/**
* Sets the delimiter between a session alias and a session id when deserializing a cookie. The default is " "
* This is useful when using <a href="https://tools.ietf.org/html/rfc6265">RFC
* 6265</a> for writing the cookies which doesn't allow for spaces in the cookie
* values.
* Sets the delimiter between a session alias and a session id when deserializing a
* cookie. The default is " " This is useful when using
* <a href="https://tools.ietf.org/html/rfc6265">RFC 6265</a> 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 " ")
* @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 <a href="https://tools.ietf.org/html/rfc6265">RFC
* 6265</a> for writing the cookies which doesn't allow for spaces in the cookie
* values.
* Sets the delimiter between a session alias and a session id when deserializing a
* cookie. The default is " ". This is useful when using
* <a href="https://tools.ietf.org/html/rfc6265">RFC 6265</a> for writing the cookies
* which doesn't allow for spaces in the cookie values.
*
* @param delimiter the delimiter to set (i.e. "_")
*/
@@ -335,7 +338,8 @@ public final class CookieHttpSessionStrategy
String sessionCookieValue = cookieValues.isEmpty() ? ""
: cookieValues.iterator().next();
Map<String, String> result = new LinkedHashMap<String, String>();
StringTokenizer tokens = new StringTokenizer(sessionCookieValue, this.deserializationDelimiter);
StringTokenizer tokens = new StringTokenizer(sessionCookieValue,
this.deserializationDelimiter);
if (tokens.countTokens() == 1) {
result.put(DEFAULT_ALIAS, tokens.nextToken());
return result;
@@ -375,12 +379,16 @@ public final class CookieHttpSessionStrategy
String replacement = isDefaultAlias ? "" : "$1" + encodedSessionAlias;
query = query.replaceFirst("((^|&)" + this.sessionParam + "=)([^&]+)?",
replacement);
if (!isDefaultAlias && url.endsWith(query)) {
String sessionParamReplacement = String.format("%s=%s", this.sessionParam,
encodedSessionAlias);
if (!isDefaultAlias && !query.contains(sessionParamReplacement)
&& url.endsWith(query)) {
// no existing alias
if (!(query.endsWith("&") || query.length() == 0)) {
query += "&";
}
query += this.sessionParam + "=" + encodedSessionAlias;
query += sessionParamReplacement;
}
return path + "?" + query;
@@ -407,19 +415,43 @@ public final class CookieHttpSessionStrategy
this.request = request;
}
private String getCurrentSessionAliasFromUrl(String url) {
String currentSessionAliasFromUrl = null;
int queryStart = url.indexOf("?");
if (queryStart >= 0) {
String query = url.substring(queryStart + 1);
Matcher matcher = Pattern
.compile(String.format("%s=([^&]+)",
CookieHttpSessionStrategy.this.sessionParam))
.matcher(query);
if (matcher.find()) {
currentSessionAliasFromUrl = matcher.group(1);
}
}
return currentSessionAliasFromUrl;
}
@Override
public String encodeRedirectURL(String url) {
url = super.encodeRedirectURL(url);
return CookieHttpSessionStrategy.this.encodeURL(url,
getCurrentSessionAlias(this.request));
String encodedUrl = super.encodeRedirectURL(url);
String currentSessionAliasFromUrl = getCurrentSessionAliasFromUrl(encodedUrl);
String alias = (currentSessionAliasFromUrl != null)
? currentSessionAliasFromUrl : getCurrentSessionAlias(this.request);
return CookieHttpSessionStrategy.this.encodeURL(encodedUrl, alias);
}
@Override
public String encodeURL(String url) {
url = super.encodeURL(url);
String encodedUrl = super.encodeURL(url);
String currentSessionAliasFromUrl = getCurrentSessionAliasFromUrl(encodedUrl);
String alias = (currentSessionAliasFromUrl != null)
? currentSessionAliasFromUrl : getCurrentSessionAlias(this.request);
String alias = getCurrentSessionAlias(this.request);
return CookieHttpSessionStrategy.this.encodeURL(url, alias);
return CookieHttpSessionStrategy.this.encodeURL(encodedUrl, alias);
}
}