001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.web.session.mgt;
020
021 import org.apache.shiro.session.ExpiredSessionException;
022 import org.apache.shiro.session.InvalidSessionException;
023 import org.apache.shiro.session.Session;
024 import org.apache.shiro.session.mgt.DefaultSessionManager;
025 import org.apache.shiro.session.mgt.DelegatingSession;
026 import org.apache.shiro.session.mgt.SessionContext;
027 import org.apache.shiro.session.mgt.SessionKey;
028 import org.apache.shiro.web.servlet.Cookie;
029 import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
030 import org.apache.shiro.web.servlet.ShiroHttpSession;
031 import org.apache.shiro.web.servlet.SimpleCookie;
032 import org.apache.shiro.web.util.WebUtils;
033 import org.slf4j.Logger;
034 import org.slf4j.LoggerFactory;
035
036 import javax.servlet.ServletRequest;
037 import javax.servlet.ServletResponse;
038 import javax.servlet.http.HttpServletRequest;
039 import javax.servlet.http.HttpServletResponse;
040 import java.io.Serializable;
041
042
043 /**
044 * Web-application capable {@link org.apache.shiro.session.mgt.SessionManager SessionManager} implementation.
045 *
046 * @since 0.9
047 */
048 public class DefaultWebSessionManager extends DefaultSessionManager {
049
050 private static final Logger log = LoggerFactory.getLogger(DefaultWebSessionManager.class);
051
052 private Cookie sessionIdCookie;
053 private boolean sessionIdCookieEnabled;
054
055 public DefaultWebSessionManager() {
056 Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
057 cookie.setHttpOnly(true); //more secure, protects against XSS attacks
058 this.sessionIdCookie = cookie;
059 this.sessionIdCookieEnabled = true;
060 }
061
062 public Cookie getSessionIdCookie() {
063 return sessionIdCookie;
064 }
065
066 @SuppressWarnings({"UnusedDeclaration"})
067 public void setSessionIdCookie(Cookie sessionIdCookie) {
068 this.sessionIdCookie = sessionIdCookie;
069 }
070
071 public boolean isSessionIdCookieEnabled() {
072 return sessionIdCookieEnabled;
073 }
074
075 @SuppressWarnings({"UnusedDeclaration"})
076 public void setSessionIdCookieEnabled(boolean sessionIdCookieEnabled) {
077 this.sessionIdCookieEnabled = sessionIdCookieEnabled;
078 }
079
080 private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
081 if (currentId == null) {
082 String msg = "sessionId cannot be null when persisting for subsequent requests.";
083 throw new IllegalArgumentException(msg);
084 }
085 Cookie template = getSessionIdCookie();
086 Cookie cookie = new SimpleCookie(template);
087 String idString = currentId.toString();
088 cookie.setValue(idString);
089 cookie.saveTo(request, response);
090 log.trace("Set session ID cookie for session with id {}", idString);
091 }
092
093 private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) {
094 getSessionIdCookie().removeFrom(request, response);
095 }
096
097 private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
098 if (!isSessionIdCookieEnabled()) {
099 log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie.");
100 return null;
101 }
102 if (!(request instanceof HttpServletRequest)) {
103 log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie. Returning null.");
104 return null;
105 }
106 HttpServletRequest httpRequest = (HttpServletRequest) request;
107 return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
108 }
109
110 private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
111
112 String id = getSessionIdCookieValue(request, response);
113 if (id != null) {
114 request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
115 ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
116 } else {
117 //not in a cookie, or cookie is disabled - try the request params as a fallback (i.e. URL rewriting):
118 id = request.getParameter(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
119 if (id == null) {
120 //try lowercase:
121 id = request.getParameter(ShiroHttpSession.DEFAULT_SESSION_ID_NAME.toLowerCase());
122 }
123 if (id != null) {
124 request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
125 ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
126 }
127 }
128 if (id != null) {
129 request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
130 //automatically mark it valid here. If it is invalid, the
131 //onUnknownSession method below will be invoked and we'll remove the attribute at that time.
132 request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
133 }
134 return id;
135 }
136
137 protected Session createExposedSession(Session session, SessionContext context) {
138 if (!WebUtils.isWeb(context)) {
139 return super.createExposedSession(session, context);
140 }
141 ServletRequest request = WebUtils.getRequest(context);
142 ServletResponse response = WebUtils.getResponse(context);
143 SessionKey key = new WebSessionKey(session.getId(), request, response);
144 return new DelegatingSession(this, key);
145 }
146
147 protected Session createExposedSession(Session session, SessionKey key) {
148 if (!WebUtils.isWeb(key)) {
149 return super.createExposedSession(session, key);
150 }
151
152 ServletRequest request = WebUtils.getRequest(key);
153 ServletResponse response = WebUtils.getResponse(key);
154 SessionKey sessionKey = new WebSessionKey(session.getId(), request, response);
155 return new DelegatingSession(this, sessionKey);
156 }
157
158 /**
159 * Stores the Session's ID, usually as a Cookie, to associate with future requests.
160 *
161 * @param session the session that was just {@link #createSession created}.
162 */
163 @Override
164 protected void onStart(Session session, SessionContext context) {
165 super.onStart(session, context);
166
167 if (!WebUtils.isHttp(context)) {
168 log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request/response " +
169 "pair. No session ID cookie will be set.");
170 return;
171
172 }
173 HttpServletRequest request = WebUtils.getHttpRequest(context);
174 HttpServletResponse response = WebUtils.getHttpResponse(context);
175
176 if (isSessionIdCookieEnabled()) {
177 Serializable sessionId = session.getId();
178 storeSessionId(sessionId, request, response);
179 } else {
180 log.debug("Session ID cookie is disabled. No cookie has been set for new session with id {}", session.getId());
181 }
182
183 request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
184 request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
185 }
186
187 @Override
188 public Serializable getSessionId(SessionKey key) {
189 Serializable id = super.getSessionId(key);
190 if (id == null && WebUtils.isWeb(key)) {
191 ServletRequest request = WebUtils.getRequest(key);
192 ServletResponse response = WebUtils.getResponse(key);
193 id = getSessionId(request, response);
194 }
195 return id;
196 }
197
198 protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
199 return getReferencedSessionId(request, response);
200 }
201
202 @Override
203 protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
204 super.onExpiration(s, ese, key);
205 onInvalidation(key);
206 }
207
208 @Override
209 protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) {
210 super.onInvalidation(session, ise, key);
211 onInvalidation(key);
212 }
213
214 private void onInvalidation(SessionKey key) {
215 ServletRequest request = WebUtils.getRequest(key);
216 if (request != null) {
217 request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID);
218 }
219 if (WebUtils.isHttp(key)) {
220 log.debug("Referenced session was invalid. Removing session ID cookie.");
221 removeSessionIdCookie(WebUtils.getHttpRequest(key), WebUtils.getHttpResponse(key));
222 } else {
223 log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " +
224 "pair. Session ID cookie will not be removed due to invalidated session.");
225 }
226 }
227
228 @Override
229 protected void onStop(Session session, SessionKey key) {
230 super.onStop(session, key);
231 if (WebUtils.isHttp(key)) {
232 HttpServletRequest request = WebUtils.getHttpRequest(key);
233 HttpServletResponse response = WebUtils.getHttpResponse(key);
234 log.debug("Session has been stopped (subject logout or explicit stop). Removing session ID cookie.");
235 removeSessionIdCookie(request, response);
236 } else {
237 log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response " +
238 "pair. Session ID cookie will not be removed due to stopped session.");
239 }
240 }
241 }