DS-3542: Cleanup SessionImpl

This commit is contained in:
Tom Desair
2017-10-26 10:28:30 +02:00
parent eed50c363d
commit 1c8de472e7
7 changed files with 10 additions and 739 deletions

View File

@@ -15,8 +15,6 @@ import javax.servlet.http.HttpServletResponse;
public interface Request { public interface Request {
public String getRequestId(); public String getRequestId();
public Session getSession();
public Object getAttribute(String name); public Object getAttribute(String name);
public void setAttribute(String name, Object o); public void setAttribute(String name, Object o);

View File

@@ -7,21 +7,18 @@
*/ */
package org.dspace.services.sessions.model; package org.dspace.services.sessions.model;
import org.dspace.services.model.Request;
import org.dspace.services.model.Session;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.dspace.services.model.Request;
public final class HttpRequestImpl extends AbstractRequestImpl implements Request { public final class HttpRequestImpl extends AbstractRequestImpl implements Request {
private transient ServletRequest servletRequest = null; private transient ServletRequest servletRequest = null;
private transient ServletResponse servletResponse = null; private transient ServletResponse servletResponse = null;
private Session session = null;
public HttpRequestImpl(ServletRequest request, ServletResponse response) { public HttpRequestImpl(ServletRequest request, ServletResponse response) {
if (request == null || response == null) { if (request == null || response == null) {
throw new IllegalArgumentException("Cannot create a request without an http request or response"); throw new IllegalArgumentException("Cannot create a request without an http request or response");
@@ -29,11 +26,6 @@ public final class HttpRequestImpl extends AbstractRequestImpl implements Reques
this.servletRequest = request; this.servletRequest = request;
this.servletResponse = response; this.servletResponse = response;
if (servletRequest instanceof HttpServletRequest) {
this.session = new SessionImpl((HttpServletRequest)servletRequest);
} else {
this.session = new SessionImpl();
}
} }
public ServletRequest getServletRequest() { public ServletRequest getServletRequest() {
@@ -48,10 +40,6 @@ public final class HttpRequestImpl extends AbstractRequestImpl implements Reques
return null; return null;
} }
public Session getSession() {
return session;
}
public ServletResponse getServletResponse() { public ServletResponse getServletResponse() {
return servletResponse; return servletResponse;
} }

View File

@@ -1,198 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.services.sessions.model;
import java.util.Enumeration;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
/**
* This is a special HTTP session object that stands in for a real one
* when sessions are not initiated or associated by HTTP requests.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
@SuppressWarnings("deprecation")
public final class InternalHttpSession implements HttpSession {
private final String id;
private long lastAccessedTime = System.currentTimeMillis();
private long creationTime = System.currentTimeMillis();
private int maxInactiveInternal = 1800;
private boolean invalidated = false;
private ConcurrentHashMap<String, Object> attributes = null;
private ConcurrentHashMap<String, Object> getAttributes() {
if (this.attributes == null) {
this.attributes = new ConcurrentHashMap<String, Object>();
}
return this.attributes;
}
public InternalHttpSession() {
this.id = UUID.randomUUID().toString();
}
private void checkInvalidated() {
if (invalidated) {
throw new IllegalStateException("This session is no longer valid");
}
}
@Override
public String toString() {
return "internalSession:" + this.id + ":" + this.creationTime + ":" + this.invalidated + ":" + super.toString();
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getAttribute(java.lang.String)
*/
public Object getAttribute(String name) {
checkInvalidated();
return getAttributes().get(name);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getAttributeNames()
*/
@SuppressWarnings("unchecked")
public Enumeration getAttributeNames() {
checkInvalidated();
return getAttributes().keys();
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getCreationTime()
*/
public long getCreationTime() {
checkInvalidated();
return creationTime;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getId()
*/
public String getId() {
checkInvalidated();
return id;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getLastAccessedTime()
*/
public long getLastAccessedTime() {
checkInvalidated();
return lastAccessedTime;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getMaxInactiveInterval()
*/
public int getMaxInactiveInterval() {
checkInvalidated();
return maxInactiveInternal;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getServletContext()
*/
public ServletContext getServletContext() {
checkInvalidated();
return null;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getSessionContext()
*/
public HttpSessionContext getSessionContext() {
checkInvalidated();
return null;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getValue(java.lang.String)
*/
public Object getValue(String name) {
checkInvalidated();
return getAttributes().get(name);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getValueNames()
*/
public String[] getValueNames() {
checkInvalidated();
Set<String> names = getAttributes().keySet();
return names.toArray(new String[names.size()]);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#invalidate()
*/
public void invalidate() {
invalidated = true;
if (this.attributes != null) {
this.attributes.clear();
this.attributes = null;
}
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#isNew()
*/
public boolean isNew() {
return false;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object)
*/
public void putValue(String name, Object value) {
checkInvalidated();
getAttributes().put(name, value);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String)
*/
public void removeAttribute(String name) {
checkInvalidated();
getAttributes().remove(name);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#removeValue(java.lang.String)
*/
public void removeValue(String name) {
checkInvalidated();
getAttributes().remove(name);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object)
*/
public void setAttribute(String name, Object value) {
checkInvalidated();
getAttributes().put(name, value);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int)
*/
public void setMaxInactiveInterval(int interval) {
checkInvalidated();
this.maxInactiveInternal = interval;
}
}

View File

@@ -7,22 +7,20 @@
*/ */
package org.dspace.services.sessions.model; package org.dspace.services.sessions.model;
import org.dspace.services.model.Request; import java.util.HashMap;
import org.dspace.services.model.Session; import java.util.Map;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map; import org.dspace.services.model.Request;
public final class InternalRequestImpl extends AbstractRequestImpl implements Request { public final class InternalRequestImpl extends AbstractRequestImpl implements Request {
private Map<String, Object> attributes = new HashMap<String, Object>(); private Map<String, Object> attributes = new HashMap<String, Object>();
private Session session = new SessionImpl();
public InternalRequestImpl() { public InternalRequestImpl() {
} }
@@ -34,10 +32,6 @@ public final class InternalRequestImpl extends AbstractRequestImpl implements Re
return null; return null;
} }
public Session getSession() {
return session;
}
public ServletResponse getServletResponse() { public ServletResponse getServletResponse() {
return null; return null;
} }

View File

@@ -1,511 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.services.sessions.model;
import org.dspace.services.model.Session;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
import java.io.Serializable;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
/**
* Represents a users session (login session) in the system. Can hold
* some additional attributes as needed, but the underlying
* implementation may limit the number and size of attributes to ensure
* that session replication is not impacted negatively.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
@SuppressWarnings("deprecation")
public final class SessionImpl implements Session {
// keys for things stored in the session
public static final String SESSION_ID = "dspaceSessionId";
public static final String USER_ID = "userId";
public static final String USER_EID = "userEid";
public static final String SERVER_ID = "serverId";
public static final String HOST_IP = "originatingHostIP";
public static final String HOST_NAME = "originatingHostName";
/**
* This is the only thing that is actually replicated across the cluster.
*/
private transient HttpSession httpSession;
/**
* Make a session that is associated with the current HTTP request.
*
* @param request current request
*/
public SessionImpl(HttpServletRequest request) {
if (request == null) {
throw new IllegalArgumentException("Cannot create a session without an http request");
}
//TODO this has to be removed
// this.httpSession = request.getSession(); // establish the session
setKeyAttribute(HOST_IP, request.getRemoteAddr());
setKeyAttribute(HOST_NAME, request.getRemoteHost());
}
/**
* Make a session which is not associated with the current HTTP request.
*/
public SessionImpl() {
// creates a new internal http session that is not cached anywhere
this.httpSession = new InternalHttpSession();
try {
InetAddress i4 = Inet4Address.getLocalHost();
setKeyAttribute(HOST_IP, i4.getHostAddress()); // IP address
setKeyAttribute(HOST_NAME, i4.getHostName());
} catch (UnknownHostException e) {
// could not get address
setKeyAttribute(HOST_IP, "10.0.0.1"); // set a fake one I guess
}
}
/**
* Set the sessionId. Normally this should not probably happen much.
* @param sessionId the session ID
*/
public void setSessionId(String sessionId) {
if (! isAttributeSet(SESSION_ID)) {
if (isBlank(sessionId)) {
// just use the http session id
setKeyAttribute(SESSION_ID, this.httpSession.getId());
} else {
setKeyAttribute(SESSION_ID, sessionId);
}
}
}
/**
* Set the userId and userEid. This should only happen when
* re-binding the session or clearing the associated user.
* If userId is null then user is cleared.
*
* @param userId the user ID
* @param userEid the user EID
*/
public void setUserId(String userId, String userEid) {
if (isBlank(userId)) {
removeKeyAttribute(USER_ID);
removeKeyAttribute(USER_EID);
} else {
setKeyAttribute(USER_ID, userId);
setKeyAttribute(USER_EID, userEid);
}
}
/**
* Set the DSpace serverId which originated this session.
*
* @param serverId the serverId
*/
public void setServerId(String serverId) {
setKeyAttribute(SERVER_ID, serverId);
}
/**
* Are all required values set? Notice that the sense of the test
* is the complement of what the name of this method implies.
*
* @return true if this session already has all the required values
* needed to complete it. This means the serverId and other values
* in the session are already set.
*/
public boolean isIncomplete() {
boolean complete = false;
if (isAttributeSet(SERVER_ID)
&& isAttributeSet(SESSION_ID)
&& isAttributeSet(HOST_IP)) {
complete = true;
}
return ! complete;
}
/**
* @param key the attribute key
* @return true if the attribute is set
*/
public boolean isAttributeSet(String key) {
return getKeyAttribute(key) != null;
}
/**
* @return true if this session is invalidated
*/
public boolean isInvalidated() {
boolean invalid = true;
if (this.httpSession != null) {
try {
this.httpSession.getCreationTime();
invalid = false;
} catch (IllegalStateException e) {
invalid = true;
}
} else {
// no httpsession
invalid = false;
}
return invalid;
}
/**
* Handles the general setting of things in the session.
* Use this to build other set methods.
* Handles checking the session is still valid, and
* the checking for null values in the value and key.
*
* @param key the key to use
* @param value the value to set
* @return true if the value was set, false if cleared or failure
* @throws IllegalArgumentException if the key is null
*/
protected boolean setKeyAttribute(String key, String value) {
if (key == null) {
throw new IllegalArgumentException("session attribute key cannot be null");
}
boolean wasSet = false;
if (! isInvalidated()) {
if (isBlank(value)) {
// this.httpSession.removeAttribute(key);
} else {
// this.httpSession.setAttribute(key, value);
wasSet = true;
}
}
return wasSet;
}
/**
* Handles the general getting of things from the session.
* Use this to build other set methods.
* Checks that the session is still valid.
*
* @param key the key to use
* @return the value OR null if not found
* @throws IllegalArgumentException if the key is null
*/
protected String getKeyAttribute(String key) {
if (key == null) {
throw new IllegalArgumentException("session attribute key cannot be null");
}
String value = null;
if (! isInvalidated()) {
// value = (String) this.httpSession.getAttribute(key);
}
return value;
}
/**
* Handles removal of attributes and related checks.
*
* @param key the key to use
* @throws IllegalArgumentException if the key is null
*/
protected void removeKeyAttribute(String key) {
if (key == null) {
throw new IllegalArgumentException("session attribute key cannot be null");
}
if (! isInvalidated()) {
// this.httpSession.removeAttribute(key);
}
}
@Override
public boolean equals(Object obj) {
// sessions are equal if the ids are the same, allows comparison across reloaded items
if (null == obj) {
return false;
}
if (!(obj instanceof SessionImpl)) {
return false;
} else {
SessionImpl castObj = (SessionImpl) obj;
boolean eq;
try {
eq = this.getId().equals(castObj.getId());
} catch (IllegalStateException e) {
eq = false;
}
return eq;
}
}
@Override
public int hashCode() {
String hashStr = this.getClass().getName() + ":" + this.httpSession.toString();
return hashStr.hashCode();
}
@Override
public String toString() {
String str;
if (isInvalidated()) {
str = "invalidated:" + this.httpSession.toString() + ":" + super.toString();
} else {
str = "active:"+getId()+":user="+getUserId()+"("+getUserEID()+"):sid="+getSessionId()+":server="+getServerId()+":created="+getCreationTime()+":accessed="+getLastAccessedTime()+":maxInactiveSecs="+getMaxInactiveInterval()+":hostIP="+getOriginatingHostIP()+":hostName="+getOriginatingHostName()+":"+super.toString();
}
return "Session:" + str;
}
// INTERFACE methods
/* (non-Javadoc)
* @see org.dspace.services.model.Session#getAttribute(java.lang.String)
*/
@Override
public String getAttribute(String key) {
return getKeyAttribute(key);
}
/* (non-Javadoc)
* @see org.dspace.services.model.Session#setAttribute(java.lang.String, java.lang.String)
*/
@Override
public void setAttribute(String key, String value) {
setKeyAttribute(key, value);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String)
*/
@Override
public void removeAttribute(String name) {
removeKeyAttribute(name);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object)
*/
@Override
public void setAttribute(String name, Object value) {
if (value != null && !(value instanceof String)) {
throw new UnsupportedOperationException("Invalid session attribute ("+name+","+value+"), Only strings can be stored in the session");
}
setKeyAttribute(name, (String) value);
}
/**
* @return a copy of the attributes in this session.
* Modifying it has no effect on the session attributes.
*/
@SuppressWarnings("unchecked")
@Override
public Map<String, String> getAttributes() {
Map<String, String> map = new HashMap<String, String>();
// if (! isInvalidated()) {
// Enumeration<String> names = this.httpSession.getAttributeNames();
// while (names.hasMoreElements()) {
// String key = names.nextElement();
// String value = (String) this.httpSession.getAttribute(key);
// map.put(key, value);
// }
// }
return map;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getAttributeNames()
*/
@SuppressWarnings("unchecked")
@Override
public Enumeration getAttributeNames() {
return this.httpSession.getAttributeNames();
}
/* (non-Javadoc)
* @see org.dspace.services.model.Session#clear()
*/
@SuppressWarnings("unchecked")
@Override
public void clear() {
if (! isInvalidated()) {
Enumeration<String> names = this.httpSession.getAttributeNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
this.httpSession.removeAttribute(name);
}
}
}
@Override
public String getOriginatingHostIP() {
return getKeyAttribute(HOST_IP);
}
@Override
public String getOriginatingHostName() {
return getKeyAttribute(HOST_NAME);
}
@Override
public String getServerId() {
return getKeyAttribute(SERVER_ID);
}
@Override
public String getSessionId() {
return getKeyAttribute(SESSION_ID);
}
@Override
public String getUserEID() {
return getKeyAttribute(USER_EID);
}
@Override
public String getUserId() {
return getKeyAttribute(USER_ID);
}
@Override
public boolean isActive() {
return ! isInvalidated();
}
// HTTP SESSION passthroughs
@Override
public long getCreationTime() {
return this.httpSession.getCreationTime();
}
@Override
public String getId() {
String id = null;
if (isAttributeSet(SESSION_ID)) {
id = getKeyAttribute(SESSION_ID);
} else {
id = this.httpSession.getId();
}
return id;
}
@Override
public long getLastAccessedTime() {
return this.httpSession.getLastAccessedTime();
}
@Override
public int getMaxInactiveInterval() {
return this.httpSession.getMaxInactiveInterval();
}
@Override
public void setMaxInactiveInterval(int interval) {
this.httpSession.setMaxInactiveInterval(interval);
}
@Override
public ServletContext getServletContext() {
if (this.httpSession != null) {
return this.httpSession.getServletContext();
}
throw new UnsupportedOperationException("No http session available for this operation");
}
@Override
public void invalidate() {
if (! isInvalidated()) {
this.httpSession.invalidate();
}
// TODO nothing otherwise?
}
@Override
public boolean isNew() {
if (! isInvalidated()) {
return this.httpSession.isNew();
}
return false;
}
// DEPRECATED
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getValue(java.lang.String)
*/
@Override
public Object getValue(String name) {
return getKeyAttribute(name);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#getValueNames()
*/
@Override
public String[] getValueNames() {
Set<String> keys = getAttributes().keySet();
return keys.toArray(new String[keys.size()]);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#removeValue(java.lang.String)
*/
@Override
public void removeValue(String name) {
removeAttribute(name);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object)
*/
@Override
public void putValue(String name, Object value) {
setAttribute(name, value);
}
@Override
public HttpSessionContext getSessionContext() {
if (this.httpSession != null) {
return this.httpSession.getSessionContext();
}
throw new UnsupportedOperationException("No http session available for this operation");
}
// END DEPRECATED
/**
* Check if something is blank (null or "").
*
* @param string string to check
* @return true if is blank
*/
public static boolean isBlank(String string) {
return (string == null) || ("".equals(string));
}
/**
* Compares sessions by the last time they were accessed, with more
* recent first.
*/
public static final class SessionLastAccessedComparator implements Comparator<Session>, Serializable {
public static final long serialVersionUID = 1l;
public int compare(Session o1, Session o2) {
try {
Long lat1 = Long.valueOf(o1.getLastAccessedTime());
Long lat2 = Long.valueOf(o2.getLastAccessedTime());
return lat2.compareTo(lat1); // reverse
} catch (Exception e) {
return 0;
}
}
}
}

View File

@@ -53,10 +53,9 @@
</bean> </bean>
<!-- CACHING end beans --> <!-- CACHING end beans -->
<!-- SESSION - session and request services (implemented as a single bean) --> <!-- REQUEST - request service (implemented as a single bean) -->
<bean id="org.dspace.services.SessionService" class="org.dspace.services.sessions.StatelessRequestServiceImpl" /> <bean id="org.dspace.services.RequestService" class="org.dspace.services.sessions.StatelessRequestServiceImpl" />
<alias alias="org.dspace.services.RequestService" name="org.dspace.services.SessionService" /> <!-- REQUEST end beans -->
<!-- SESSION end beans -->
<!-- EVENTS --> <!-- EVENTS -->
<bean id="org.dspace.services.EventService" class="org.dspace.services.events.SystemEventService" /> <bean id="org.dspace.services.EventService" class="org.dspace.services.events.SystemEventService" />

View File

@@ -50,6 +50,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
//Wire up the HttpServletRequest with the current SecurityContext values //Wire up the HttpServletRequest with the current SecurityContext values
.servletApi().and() .servletApi().and()
//Disable CSRF as our API can be used by clients on an other domain //Disable CSRF as our API can be used by clients on an other domain
//TODO This might be a good reason to switch from using cookies to using HTTP headers
.csrf().disable() .csrf().disable()
//Logout configuration //Logout configuration