blob: 761b8369c1545b2ddc937dbddeb1d60a5c679241 [file] [log] [blame]
/*
* 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.jsecurity.web.session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jsecurity.authz.AuthorizationException;
import org.jsecurity.authz.HostUnauthorizedException;
import org.jsecurity.session.InvalidSessionException;
import org.jsecurity.session.Session;
import org.jsecurity.session.mgt.DefaultSessionManager;
import org.jsecurity.web.WebUtils;
import org.jsecurity.web.attr.CookieAttribute;
import org.jsecurity.web.attr.RequestParamAttribute;
import org.jsecurity.web.attr.WebAttribute;
import org.jsecurity.web.servlet.JSecurityHttpServletRequest;
import org.jsecurity.web.servlet.JSecurityHttpSession;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
import java.net.InetAddress;
/**
* Web-application capable <tt>SessionManager</tt> implementation.
*
* @author Les Hazlewood
* @since 0.9
*/
public class DefaultWebSessionManager extends DefaultSessionManager implements WebSessionManager {
//TODO - complete JavaDoc
private static final Log log = LogFactory.getLog(DefaultWebSessionManager.class);
/**
* Property specifying if, after a session object is acquired from the request, if that session should be
* validated to ensure the starting origin of the session is the same as the incoming request.
*/
private boolean validateRequestOrigin = false; //default
protected CookieAttribute<Serializable> sessionIdCookieAttribute = null;
protected RequestParamAttribute<Serializable> sessionIdRequestParamAttribute = null;
public DefaultWebSessionManager() {
ensureCookieSessionIdStore();
ensureRequestParamSessionIdStore();
}
public CookieAttribute<Serializable> getSessionIdCookieAttribute() {
return sessionIdCookieAttribute;
}
public void setSessionIdCookieAttribute(CookieAttribute<Serializable> sessionIdCookieAttribute) {
this.sessionIdCookieAttribute = sessionIdCookieAttribute;
}
public RequestParamAttribute<Serializable> getSessionIdRequestParamAttribute() {
return sessionIdRequestParamAttribute;
}
public void setSessionIdRequestParamAttribute(RequestParamAttribute<Serializable> sessionIdRequestParamAttribute) {
this.sessionIdRequestParamAttribute = sessionIdRequestParamAttribute;
}
/**
* If set to <tt>true</tt>, this implementation will ensure that any
* <tt>HttpRequest</tt> attempting
* to join a session (i.e. via {@link #getSession getSession} must have the same
* IP Address of the <tt>HttpRequest</tt> that started the session.
*
* <p> If set to <tt>false</tt>, any <tt>HttpRequest</tt> with a reference to a valid
* session id may acquire that <tt>Session</tt>.
*
* <p>Although convenient, this should only be enabled in environments where the
* system can <em>guarantee</em> that each IP address represents one and only one
* machine accessing the system.
*
* <p>Public websites are not good candidates for enabling this
* feature since many browser clients often sit behind NAT routers (in
* which case many machines are viewed to come from the same IP, thereby making this
* validation check useless). Also, some internet service providers (e.g. AOL) may change a
* client's IP in mid-session, making subsequent requests appear to come from a different
* location. Again, this feature should only be enabled where IP Addresses can be guaranteed a
* 1-to-1 relationship with a user's session.
*
* <p>For the reasons specified above, this property is <tt>false</tt> by default.
*
* @return true if this factory will verify each HttpRequest joining a session
*/
public boolean isValidateRequestOrigin() {
return validateRequestOrigin;
}
/**
* Sets whether or not a request's origin will be validated when accessing a session. See
* the {@link #isValidateRequestOrigin} JavaDoc for an in-depth explanation of this property.
*
* @param validateRequestOrigin whether or not to validate the request's origin when accessing
* a session.
* @see #isValidateRequestOrigin
*/
public void setValidateRequestOrigin(boolean validateRequestOrigin) {
this.validateRequestOrigin = validateRequestOrigin;
}
public void setSessionIdCookieName(String name) {
getSessionIdCookieAttribute().setName(name);
}
public void setSessionIdCookiePath(String path) {
getSessionIdCookieAttribute().setPath(path);
}
public void setSessionIdCookieMaxAge(int maxAge) {
getSessionIdCookieAttribute().setMaxAge(maxAge);
}
public void setSessionIdCookieSecure(boolean secure) {
getSessionIdCookieAttribute().setSecure(secure);
}
protected void ensureCookieSessionIdStore() {
CookieAttribute<Serializable> cookieStore = getSessionIdCookieAttribute();
if (cookieStore == null) {
cookieStore = new CookieAttribute<Serializable>(JSecurityHttpSession.DEFAULT_SESSION_ID_NAME);
cookieStore.setCheckRequestParams(false);
setSessionIdCookieAttribute(cookieStore);
}
}
protected void ensureRequestParamSessionIdStore() {
RequestParamAttribute<Serializable> reqParamStore = getSessionIdRequestParamAttribute();
if (reqParamStore == null) {
reqParamStore = new RequestParamAttribute<Serializable>(JSecurityHttpSession.DEFAULT_SESSION_ID_NAME);
setSessionIdRequestParamAttribute(reqParamStore);
}
}
protected void validateSessionOrigin(ServletRequest request, Session session)
throws HostUnauthorizedException {
InetAddress requestIp = WebUtils.getInetAddress(request);
InetAddress originIp = session.getHostAddress();
Serializable sessionId = session.getId();
if (originIp == null) {
if (requestIp != null) {
String msg = "No IP Address was specified when creating session with id [" +
sessionId + "]. Attempting to access session from " +
"IP [" + requestIp + "]. Origin IP and request IP must match.";
throw new HostUnauthorizedException(msg);
}
} else {
if (requestIp != null) {
if (!requestIp.equals(originIp)) {
String msg = "Session with id [" + sessionId + "] originated from [" +
originIp + "], but the current HttpServletRequest originated " +
"from [" + requestIp + "]. Disallowing session access: " +
"session origin and request origin must match to allow access.";
throw new HostUnauthorizedException(msg);
}
} else {
String msg = "No IP Address associated with the current HttpServletRequest. " +
"Session with id [" + sessionId + "] originated from " +
"[" + originIp + "]. Request IP must match the session's origin " +
"IP in order to gain access to that session.";
throw new HostUnauthorizedException(msg);
}
}
}
protected void storeSessionId(Serializable currentId, ServletRequest request, ServletResponse response) {
if (currentId == null) {
String msg = "sessionId cannot be null when persisting for subsequent requests.";
throw new IllegalArgumentException(msg);
}
//ensure that the id has been set in the idStore, or if it already has, that it is not different than the
//'real' session value:
Serializable existingId = retrieveSessionId(request, response);
if (existingId == null || !currentId.equals(existingId)) {
getSessionIdCookieAttribute().storeValue(currentId, request, response);
}
}
protected Serializable retrieveSessionId(ServletRequest request, ServletResponse response) {
WebAttribute<Serializable> cookieSessionIdAttribute = getSessionIdCookieAttribute();
Serializable id = cookieSessionIdAttribute.retrieveValue(request, response);
if (id != null) {
request.setAttribute(JSecurityHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
JSecurityHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
} else {
id = getSessionIdRequestParamAttribute().retrieveValue(request, response);
if (id != null) {
request.setAttribute(JSecurityHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
JSecurityHttpServletRequest.URL_SESSION_ID_SOURCE);
}
}
return id;
}
public Serializable start(InetAddress hostAddress) throws HostUnauthorizedException, IllegalArgumentException {
ServletRequest request = WebUtils.getRequiredServletRequest();
ServletResponse response = WebUtils.getRequiredServletResponse();
return start(request, response, hostAddress);
}
protected Serializable start(ServletRequest request, ServletResponse response, InetAddress inetAddress) {
Serializable sessionId = super.start(inetAddress);
storeSessionId(sessionId, request, response);
request.removeAttribute(JSecurityHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(JSecurityHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
return sessionId;
}
public Session retrieveSession(Serializable sessionId) throws InvalidSessionException, AuthorizationException {
if (sessionId != null) {
return super.retrieveSession(sessionId);
} else {
ServletRequest request = WebUtils.getRequiredServletRequest();
ServletResponse response = WebUtils.getRequiredServletResponse();
return getSession(request, response);
}
}
/**
* Returns the Session associated with the specified request if it is valid or <tt>null</tt> if a Session doesn't
* exist or it was invalid.
*
* @param request incoming servlet request
* @param response outgoing servlet response
* @return the Session associated with the incoming request or <tt>null</tt> if one does not exist.
* @throws org.jsecurity.session.InvalidSessionException
* if the associated Session has expired prior to invoking this method.
* @throws org.jsecurity.authz.AuthorizationException
* if the caller is not authorized to access the session associated with the request.
*/
public final Session getSession(ServletRequest request, ServletResponse response)
throws InvalidSessionException, AuthorizationException {
Session session;
try {
session = doGetSession(request, response);
} catch (InvalidSessionException ise) {
if (log.isTraceEnabled()) {
log.trace("Request Session with id [" + ise.getSessionId() + "] is invalid, message: [" +
ise.getMessage() + "]. Removing any associated session cookie...");
}
getSessionIdCookieAttribute().removeValue(request, response);
//give subclass a chance to do something additional if necessary. Otherwise returning null is just fine:
session = handleInvalidSession(request, response, ise);
}
return session;
}
protected Session doGetSession(ServletRequest request, ServletResponse response) {
Session session = null;
Serializable sessionId = retrieveSessionId(request, response);
if (sessionId != null) {
request.setAttribute(JSecurityHttpServletRequest.REFERENCED_SESSION_ID, sessionId);
session = super.retrieveSession(sessionId);
if (isValidateRequestOrigin()) {
if (log.isDebugEnabled()) {
log.debug("Validating request origin against session origin");
}
validateSessionOrigin(request, session);
}
if (session != null) {
request.setAttribute(JSecurityHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
}
} else {
if (log.isTraceEnabled()) {
log.trace("No JSecurity session id associated with the given " +
"HttpServletRequest. A Session will not be returned.");
}
}
return session;
}
protected Session handleInvalidSession(ServletRequest request,
ServletResponse response,
InvalidSessionException ise) {
if (log.isTraceEnabled()) {
log.trace("Sesssion associated with the current request is nonexistent or invalid. Returning null.");
}
return null;
}
protected void onStop(Session session) {
super.onStop(session);
ServletRequest request = WebUtils.getRequiredServletRequest();
ServletResponse response = WebUtils.getRequiredServletResponse();
getSessionIdCookieAttribute().removeValue(request, response);
}
}