SHIRO-361 Added property to support disabling of JSESSIONID in URL

This can be turned off by setting: `sessionManager.sessionIdUrlRewritingEnabled` to false (recommended)
in the shiro.ini (or equivalent configuration)

This is a stop gap on the way to SHIRO-360
diff --git a/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletRequest.java b/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletRequest.java
index 74f5da5..6b71250 100644
--- a/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletRequest.java
+++ b/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletRequest.java
@@ -51,6 +51,7 @@
     public static final String REFERENCED_SESSION_IS_NEW = ShiroHttpServletRequest.class.getName() + "_REFERENCED_SESSION_IS_NEW";
     public static final String REFERENCED_SESSION_ID_SOURCE = ShiroHttpServletRequest.class.getName() + "REFERENCED_SESSION_ID_SOURCE";
     public static final String IDENTITY_REMOVED_KEY = ShiroHttpServletRequest.class.getName() + "_IDENTITY_REMOVED_KEY";
+    public static final String SESSION_ID_URL_REWRITING_ENABLED = ShiroHttpServletRequest.class.getName() + "_SESSION_ID_URL_REWRITING_ENABLED";
 
     protected ServletContext servletContext = null;
 
diff --git a/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletResponse.java b/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletResponse.java
index 438b3e6..744a736 100644
--- a/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletResponse.java
+++ b/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletResponse.java
@@ -136,6 +136,10 @@
      */
     protected boolean isEncodeable(final String location) {
 
+        // First check if URL rewriting is disabled globally
+        if (Boolean.FALSE.equals(request.getAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED)))
+            return (false);
+
         if (location == null)
             return (false);
 
diff --git a/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java b/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java
index f8baf11..eb7eda1 100644
--- a/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java
+++ b/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java
@@ -51,12 +51,14 @@
 
     private Cookie sessionIdCookie;
     private boolean sessionIdCookieEnabled;
+    private boolean sessionIdUrlRewritingEnabled;
 
     public DefaultWebSessionManager() {
         Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
         cookie.setHttpOnly(true); //more secure, protects against XSS attacks
         this.sessionIdCookie = cookie;
         this.sessionIdCookieEnabled = true;
+        this.sessionIdUrlRewritingEnabled = true;
     }
 
     public Cookie getSessionIdCookie() {
@@ -77,6 +79,15 @@
         this.sessionIdCookieEnabled = sessionIdCookieEnabled;
     }
 
+    public boolean isSessionIdUrlRewritingEnabled() {
+        return sessionIdUrlRewritingEnabled;
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public void setSessionIdUrlRewritingEnabled(boolean sessionIdUrlRewritingEnabled) {
+        this.sessionIdUrlRewritingEnabled = sessionIdUrlRewritingEnabled;
+    }
+
     private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
         if (currentId == null) {
             String msg = "sessionId cannot be null when persisting for subsequent requests.";
@@ -139,6 +150,10 @@
             //onUnknownSession method below will be invoked and we'll remove the attribute at that time.
             request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
         }
+
+        // always set rewrite flag - SHIRO-361
+        request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, isSessionIdUrlRewritingEnabled());
+
         return id;
     }
 
diff --git a/web/src/test/groovy/org/apache/shiro/web/servlet/ShiroHttpServletResponseTest.groovy b/web/src/test/groovy/org/apache/shiro/web/servlet/ShiroHttpServletResponseTest.groovy
new file mode 100644
index 0000000..a26dca7
--- /dev/null
+++ b/web/src/test/groovy/org/apache/shiro/web/servlet/ShiroHttpServletResponseTest.groovy
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.shiro.web.servlet
+
+import org.junit.Test
+
+import javax.servlet.ServletContext
+import javax.servlet.http.HttpServletResponse
+import javax.servlet.http.HttpSession
+
+import static org.easymock.EasyMock.*
+import static org.junit.Assert.*
+
+/**
+ * Unit tests for {@link ShiroHttpServletResponse}.
+ */
+class ShiroHttpServletResponseTest {
+
+    private static String URL_SESSION_ID = "url_session_id"
+
+    @Test
+    void testEncodeURLNoSessionId() {
+
+        def servletContext = createStrictMock(ServletContext)
+        def httpServletResponse = createStrictMock(HttpServletResponse)
+        def shiroHttpServletRequest = setupRequestMock()
+        expect(shiroHttpServletRequest.getSession(false)).andReturn(null)
+        expect(shiroHttpServletRequest.getAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED)).andReturn(true)
+        replay shiroHttpServletRequest, servletContext, httpServletResponse
+
+        def shiroHttpServletResponse = new ShiroHttpServletResponse(httpServletResponse, servletContext, shiroHttpServletRequest)
+
+        assertEquals "/foobar", shiroHttpServletResponse.encodeURL("/foobar")
+        verify shiroHttpServletRequest, servletContext, httpServletResponse
+    }
+
+    @Test
+    void testEncodeURLSessionIdInURL() {
+
+        def servletContext = createStrictMock(ServletContext)
+        def httpServletResponse = createStrictMock(HttpServletResponse)
+        def session = createMock(HttpSession)
+        def shiroHttpServletRequest = setupRequestMock()
+        expect(session.getId()).andReturn(URL_SESSION_ID).anyTimes()
+        expect(shiroHttpServletRequest.getSession(false)).andReturn(session)
+        expect(shiroHttpServletRequest.getSession()).andReturn(session)
+        expect(shiroHttpServletRequest.isRequestedSessionIdFromCookie()).andReturn(false)
+        expect(shiroHttpServletRequest.getAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED)).andReturn(true)
+        replay shiroHttpServletRequest, servletContext, httpServletResponse, session
+
+        def shiroHttpServletResponse = new ShiroHttpServletResponse(httpServletResponse, servletContext, shiroHttpServletRequest)
+
+        assertEquals "/foobar;JSESSIONID=" + URL_SESSION_ID, shiroHttpServletResponse.encodeURL("/foobar")
+        verify shiroHttpServletRequest, servletContext, httpServletResponse, session
+    }
+
+    @Test
+    void testEncodeURLSessionIdInCookie() {
+
+        def servletContext = createStrictMock(ServletContext)
+        def httpServletResponse = createStrictMock(HttpServletResponse)
+        def session = createMock(HttpSession)
+        def shiroHttpServletRequest = setupRequestMock()
+        expect(shiroHttpServletRequest.getAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED)).andReturn(false)
+        replay shiroHttpServletRequest, servletContext, httpServletResponse, session
+
+        def shiroHttpServletResponse = new ShiroHttpServletResponse(httpServletResponse, servletContext, shiroHttpServletRequest)
+
+        assertEquals "/foobar", shiroHttpServletResponse.encodeURL("/foobar")
+        verify shiroHttpServletRequest, servletContext, httpServletResponse, session
+    }
+
+    @Test
+    void testEncodeURLSessionIdInWhenRewriteDisabled() {
+
+        def servletContext = createStrictMock(ServletContext)
+        def httpServletResponse = createStrictMock(HttpServletResponse)
+        def session = createMock(HttpSession)
+        def shiroHttpServletRequest = setupRequestMock()
+        expect(shiroHttpServletRequest.getAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED)).andReturn(false)
+        replay shiroHttpServletRequest, servletContext, httpServletResponse, session
+
+        def shiroHttpServletResponse = new ShiroHttpServletResponse(httpServletResponse, servletContext, shiroHttpServletRequest)
+
+        assertEquals "/foobar", shiroHttpServletResponse.encodeURL("/foobar")
+        verify shiroHttpServletRequest, servletContext, httpServletResponse, session
+    }
+
+    /**
+     * Tests if the request attribute {@link ShiroHttpServletRequest}.SESSION_ID_URL_REWRITING_ENABLED is not false
+     * boolean, the default behavior is to encode the URL.
+     */
+    @Test
+    void testEncodeURLSessionIdInWhenRewriteInvalid() {
+
+        def servletContext = createStrictMock(ServletContext)
+        def httpServletResponse = createStrictMock(HttpServletResponse)
+        def session = createMock(HttpSession)
+        def shiroHttpServletRequest = setupRequestMock()
+        expect(session.getId()).andReturn(URL_SESSION_ID).anyTimes()
+        expect(shiroHttpServletRequest.getSession(false)).andReturn(session)
+        expect(shiroHttpServletRequest.getSession()).andReturn(session)
+        expect(shiroHttpServletRequest.isRequestedSessionIdFromCookie()).andReturn(false)
+        expect(shiroHttpServletRequest.getAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED)).andReturn("something-else")
+        replay shiroHttpServletRequest, servletContext, httpServletResponse, session
+
+        def shiroHttpServletResponse = new ShiroHttpServletResponse(httpServletResponse, servletContext, shiroHttpServletRequest)
+
+        assertEquals "/foobar;JSESSIONID=" + URL_SESSION_ID, shiroHttpServletResponse.encodeURL("/foobar")
+        verify shiroHttpServletRequest, servletContext, httpServletResponse, session
+    }
+
+    /**
+     * Tests if the request attribute {@link ShiroHttpServletRequest}.SESSION_ID_URL_REWRITING_ENABLED is null,
+     * the default behavior is NOT to encode the URL.
+     */
+    @Test
+    void testEncodeURLSessionIdInWhenRewriteInvalidAndNull() {
+
+        def servletContext = createStrictMock(ServletContext)
+        def httpServletResponse = createStrictMock(HttpServletResponse)
+        def session = createMock(HttpSession)
+        def shiroHttpServletRequest = setupRequestMock()
+        expect(session.getId()).andReturn(URL_SESSION_ID).anyTimes()
+        expect(shiroHttpServletRequest.getSession(false)).andReturn(session)
+        expect(shiroHttpServletRequest.getSession()).andReturn(session)
+        expect(shiroHttpServletRequest.isRequestedSessionIdFromCookie()).andReturn(false)
+        expect(shiroHttpServletRequest.getAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED)).andReturn(null)
+        replay shiroHttpServletRequest, servletContext, httpServletResponse, session
+
+        def shiroHttpServletResponse = new ShiroHttpServletResponse(httpServletResponse, servletContext, shiroHttpServletRequest)
+
+        assertEquals "/foobar;JSESSIONID=" + URL_SESSION_ID, shiroHttpServletResponse.encodeURL("/foobar")
+        verify shiroHttpServletRequest, servletContext, httpServletResponse, session
+    }
+
+
+    private static ShiroHttpServletRequest setupRequestMock() {
+        def shiroHttpServletRequest = createMock(ShiroHttpServletRequest)
+
+        expect(shiroHttpServletRequest.getScheme()).andReturn("http").anyTimes()
+        expect(shiroHttpServletRequest.getServerName()).andReturn("localhost").anyTimes()
+        expect(shiroHttpServletRequest.getServerPort()).andReturn(8080).anyTimes()
+        expect(shiroHttpServletRequest.getContextPath()).andReturn("/").anyTimes()
+
+
+        return shiroHttpServletRequest
+    }
+
+}
diff --git a/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy b/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy
index f06762b..526636c 100644
--- a/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy
+++ b/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy
@@ -126,6 +126,7 @@
                 ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
+        request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, Boolean.TRUE);
 
         replay(cookie);
         replay(request);
@@ -160,6 +161,7 @@
                 ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
+        request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, Boolean.TRUE);
 
         replay(cookie);
         replay(request);
@@ -195,6 +197,7 @@
                 ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
+        request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, Boolean.TRUE);
 
         replay(cookie);
         replay(request);
@@ -225,6 +228,7 @@
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
         request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
+        request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, Boolean.TRUE);
 
         replay(request);
         replay(response);