Explorar el Código

SEC-1039: Deprecated HttpSessionContextIntegrationFilter and made it extend SecurityContextPersistenceFilter.

Luke Taylor hace 17 años
padre
commit
1918c50fd7

+ 31 - 347
core/src/main/java/org/springframework/security/context/HttpSessionContextIntegrationFilter.java

@@ -95,20 +95,22 @@ import org.springframework.security.ui.FilterChainOrder;
  * @author Luke Taylor
  * @author Martin Algesten
  *
+ * @deprecated Use SecurityContextPersistenceFilter instead.
+ *
  * @version $Id$
  */
-public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter implements InitializingBean {
+public class HttpSessionContextIntegrationFilter extends SecurityContextPersistenceFilter implements InitializingBean {
     //~ Static fields/initializers =====================================================================================
 
-    static final String FILTER_APPLIED = "__spring_security_session_integration_filter_applied";
-
+//    static final String FILTER_APPLIED = "__spring_security_session_integration_filter_applied";
+//
     public static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
 
     //~ Instance fields ================================================================================================
 
     private Class<? extends SecurityContext> contextClass = SecurityContextImpl.class;
 
-    private Object contextObject;
+//    private Object contextObject;
 
     /**
      * Indicates if this filter can create a <code>HttpSession</code> if
@@ -153,382 +155,64 @@ public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter im
      */
     private boolean cloneFromHttpSession = false;
 
-    private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
+ //   private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
 
-    public boolean isCloneFromHttpSession() {
-        return cloneFromHttpSession;
-    }
-
-    public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
-        this.cloneFromHttpSession = cloneFromHttpSession;
-    }
+    private HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
 
     public HttpSessionContextIntegrationFilter() throws ServletException {
-        this.contextObject = generateNewContext();
+//        this.contextObject = generateNewContext();
+        super.setSecurityContextRepository(repo);
     }
 
-    //~ Methods ========================================================================================================
-
-    public void afterPropertiesSet() throws Exception {
-        if ((this.contextClass == null) || (!SecurityContext.class.isAssignableFrom(this.contextClass))) {
-            throw new IllegalArgumentException("context must be defined and implement SecurityContext "
-                    + "(typically use org.springframework.security.context.SecurityContextImpl; existing class is "
-                    + this.contextClass + ")");
-        }
-
-        if (forceEagerSessionCreation && !allowSessionCreation) {
-            throw new IllegalArgumentException(
-                    "If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
-        }
-
-        contextObject = generateNewContext();
-    }
-
-    public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
-            throws IOException, ServletException {
-
-        if (request.getAttribute(FILTER_APPLIED) != null) {
-            // ensure that filter is only applied once per request
-            chain.doFilter(request, response);
-
-            return;
-        }
-
-        HttpSession httpSession = safeGetSession(request, forceEagerSessionCreation);
-        boolean httpSessionExistedAtStartOfRequest = httpSession != null;
-        SecurityContext contextBeforeChainExecution = readSecurityContextFromSession(httpSession);
-
-        // Make the HttpSession null, as we don't want to keep a reference to it lying
-        // around in case chain.doFilter() invalidates it.
-        httpSession = null;
-
-        if (contextBeforeChainExecution == null) {
-            contextBeforeChainExecution = generateNewContext();
-
-            if (logger.isDebugEnabled()) {
-                logger.debug("New SecurityContext instance will be associated with SecurityContextHolder");
-            }
-        } else {
-            if (logger.isDebugEnabled()) {
-                logger.debug("Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT to "
-                        + "associate with SecurityContextHolder: '" + contextBeforeChainExecution + "'");
-            }
-        }
-
-        int contextHashBeforeChainExecution = contextBeforeChainExecution.hashCode();
-        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
-
-        // Create a wrapper that will eagerly update the session with the security context
-        // if anything in the chain does a sendError() or sendRedirect().
-        // See SEC-398
-
-        OnRedirectUpdateSessionResponseWrapper responseWrapper =
-                new OnRedirectUpdateSessionResponseWrapper( response, request,
-                    httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution );
-
-        // Proceed with chain
-
-        try {
-            // This is the only place in this class where SecurityContextHolder.setContext() is called
-            SecurityContextHolder.setContext(contextBeforeChainExecution);
-
-            chain.doFilter(request, responseWrapper);
-        }
-        finally {
-            // This is the only place in this class where SecurityContextHolder.getContext() is called
-            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
-
-            // Crucial removal of SecurityContextHolder contents - do this before anything else.
-            SecurityContextHolder.clearContext();
-
-            request.removeAttribute(FILTER_APPLIED);
-
-            // storeSecurityContextInSession() might already be called by the response wrapper
-            // if something in the chain called sendError() or sendRedirect(). This ensures we only call it
-            // once per request.
-            if ( !responseWrapper.isSessionUpdateDone() ) {
-                storeSecurityContextInSession(contextAfterChainExecution, request,
-                      httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution);
-            }
-
-            if (logger.isDebugEnabled()) {
-                logger.debug("SecurityContextHolder now cleared, as request processing completed");
-            }
-        }
-    }
-
-    /**
-     * Gets the security context from the session (if available) and returns it.
-     * <p/>
-     * If the session is null, the context object is null or the context object stored in the session
-     * is not an instance of SecurityContext it will return null.
-     * <p/>
-     * If <tt>cloneFromHttpSession</tt> is set to true, it will attempt to clone the context object
-     * and return the cloned instance.
-     *
-     * @param httpSession the session obtained from the request.
-     */
-    private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
-        if (httpSession == null) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("No HttpSession currently exists");
-            }
-
-            return null;
-        }
-
-        // Session exists, so try to obtain a context from it.
-
-        Object contextFromSessionObject = httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY);
-
-        if (contextFromSessionObject == null) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("HttpSession returned null object for SPRING_SECURITY_CONTEXT");
-            }
-
-            return null;
-        }
-
-        // We now have the security context object from the session.
-
-        // Clone if required (see SEC-356)
-        if (cloneFromHttpSession) {
-            Assert.isInstanceOf(Cloneable.class, contextFromSessionObject,
-                    "Context must implement Clonable and provide a Object.clone() method");
-            try {
-                Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[]{});
-                if (!m.isAccessible()) {
-                    m.setAccessible(true);
-                }
-                contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[]{});
-            }
-            catch (Exception ex) {
-                ReflectionUtils.handleReflectionException(ex);
-            }
-        }
-
-        if (!(contextFromSessionObject instanceof SecurityContext)) {
-            if (logger.isWarnEnabled()) {
-                logger.warn("SPRING_SECURITY_CONTEXT did not contain a SecurityContext but contained: '"
-                        + contextFromSessionObject
-                        + "'; are you improperly modifying the HttpSession directly "
-                        + "(you should always use SecurityContextHolder) or using the HttpSession attribute "
-                        + "reserved for this class?");
-            }
-
-            return null;
-        }
-
-        // Everything OK. The only non-null return from this method.
-
-        return (SecurityContext) contextFromSessionObject;
-    }
-
-    /**
-     * Stores the supplied security context in the session (if available) and if it has changed since it was
-     * set at the start of the request. If the AuthenticationTrustResolver identifies the current user as
-     * anonymous, then the context will not be stored.
-     *
-     * @param securityContext the context object obtained from the SecurityContextHolder after the request has
-     *        been processed by the filter chain. SecurityContextHolder.getContext() cannot be used to obtain
-     *        the context as it has already been cleared by the time this method is called.
-     * @param request the request object (used to obtain the session, if one exists).
-     * @param httpSessionExistedAtStartOfRequest indicates whether there was a session in place before the
-     *        filter chain executed. If this is true, and the session is found to be null, this indicates that it was
-     *        invalidated during the request and a new session will now be created.
-     * @param contextHashBeforeChainExecution the hashcode of the context before the filter chain executed.
-     *        The context will only be stored if it has a different hashcode, indicating that the context changed
-     *        during the request.
-     *
-     */
-    private void storeSecurityContextInSession(SecurityContext securityContext,
-                                               HttpServletRequest request,
-                                               boolean httpSessionExistedAtStartOfRequest,
-                                               int contextHashBeforeChainExecution) {
-        HttpSession httpSession = safeGetSession(request, false);
-
-        if (httpSession == null) {
-            if (httpSessionExistedAtStartOfRequest) {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("HttpSession is now null, but was not null at start of request; "
-                            + "session was invalidated, so do not create a new session");
-                }
-            } else {
-                // Generate a HttpSession only if we need to
-
-                if (!allowSessionCreation) {
-                    if (logger.isDebugEnabled()) {
-                        logger.debug("The HttpSession is currently null, and the "
-                                        + "HttpSessionContextIntegrationFilter is prohibited from creating an HttpSession "
-                                        + "(because the allowSessionCreation property is false) - SecurityContext thus not "
-                                        + "stored for next request");
-                    }
-                } else if (!contextObject.equals(securityContext)) {
-                    if (logger.isDebugEnabled()) {
-                        logger.debug("HttpSession being created as SecurityContextHolder contents are non-default");
-                    }
-
-                    httpSession = safeGetSession(request, true);
-
-                } else {
-                    if (logger.isDebugEnabled()) {
-                        logger.debug("HttpSession is null, but SecurityContextHolder has not changed from default: ' "
-                                + securityContext
-                                + "'; not creating HttpSession or storing SecurityContextHolder contents");
-                    }
-                }
-            }
-        }
-
-        // If HttpSession exists, store current SecurityContextHolder contents but only if
-        // the SecurityContext has actually changed (see JIRA SEC-37)
-        if (httpSession != null && securityContext.hashCode() != contextHashBeforeChainExecution) {
-            // See SEC-766
-            if (authenticationTrustResolver.isAnonymous(securityContext.getAuthentication())) {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("SecurityContext contents are anonymous - context will not be stored in HttpSession. ");
-                }
-            } else {
-                httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext);
-
-                if (logger.isDebugEnabled()) {
-                    logger.debug("SecurityContext stored to HttpSession: '" + securityContext + "'");
-                }
-            }
-        }
-    }
-
-    private HttpSession safeGetSession(HttpServletRequest request, boolean allowCreate) {
-        try {
-            return request.getSession(allowCreate);
-        }
-        catch (IllegalStateException ignored) {
-            return null;
-        }
+    public boolean isCloneFromHttpSession() {
+        return cloneFromHttpSession;
     }
 
-    public SecurityContext generateNewContext() throws ServletException {
-        try {
-            return (SecurityContext) this.contextClass.newInstance();
-        }
-        catch (InstantiationException ie) {
-            throw new ServletException(ie);
-        }
-        catch (IllegalAccessException iae) {
-            throw new ServletException(iae);
-        }
+    public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
+        this.cloneFromHttpSession = cloneFromHttpSession;
+        repo.setCloneFromHttpSession(cloneFromHttpSession);
     }
 
     public boolean isAllowSessionCreation() {
-        return allowSessionCreation;
+      return allowSessionCreation;
     }
 
     public void setAllowSessionCreation(boolean allowSessionCreation) {
-        this.allowSessionCreation = allowSessionCreation;
+      this.allowSessionCreation = allowSessionCreation;
+      repo.setAllowSessionCreation(allowSessionCreation);
     }
 
     protected Class<? extends SecurityContext> getContextClass() {
-        return contextClass;
+      return contextClass;
     }
 
     @SuppressWarnings("unchecked")
     public void setContextClass(Class secureContext) {
-        this.contextClass = secureContext;
+      this.contextClass = secureContext;
+      repo.setSecurityContextClass(secureContext);
     }
 
     public boolean isForceEagerSessionCreation() {
-        return forceEagerSessionCreation;
+      return forceEagerSessionCreation;
     }
 
     public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
-        this.forceEagerSessionCreation = forceEagerSessionCreation;
+      this.forceEagerSessionCreation = forceEagerSessionCreation;
+      super.setForceEagerSessionCreation(forceEagerSessionCreation);
     }
 
     public int getOrder() {
-        return FilterChainOrder.HTTP_SESSION_CONTEXT_FILTER;
+      return FilterChainOrder.HTTP_SESSION_CONTEXT_FILTER;
     }
 
-    //~ Inner Classes ==================================================================================================
-
-    /**
-     * Wrapper that is applied to every request to update the <code>HttpSession<code> with
-     * the <code>SecurityContext</code> when a <code>sendError()</code> or <code>sendRedirect</code>
-     * happens. See SEC-398. The class contains the fields needed to call
-     * <code>storeSecurityContextInSession()</code>
-     */
-    private class OnRedirectUpdateSessionResponseWrapper extends HttpServletResponseWrapper {
-
-        HttpServletRequest request;
-        boolean httpSessionExistedAtStartOfRequest;
-        int contextHashBeforeChainExecution;
-
-        // Used to ensure storeSecurityContextInSession() is only
-        // called once.
-        boolean sessionUpdateDone = false;
-
-        /**
-         * Takes the parameters required to call <code>storeSecurityContextInSession()</code> in
-         * addition to the response object we are wrapping.
-         * @see HttpSessionContextIntegrationFilter#storeSecurityContextInSession(SecurityContext, HttpServletRequest, boolean, int)
-         */
-        public OnRedirectUpdateSessionResponseWrapper(HttpServletResponse response,
-                                                      HttpServletRequest request,
-                                                      boolean httpSessionExistedAtStartOfRequest,
-                                                      int contextHashBeforeChainExecution) {
-            super(response);
-            this.request = request;
-            this.httpSessionExistedAtStartOfRequest = httpSessionExistedAtStartOfRequest;
-            this.contextHashBeforeChainExecution = contextHashBeforeChainExecution;
-        }
-
-        /**
-         * Makes sure the session is updated before calling the
-         * superclass <code>sendError()</code>
-         */
-        public void sendError(int sc) throws IOException {
-            doSessionUpdate();
-            super.sendError(sc);
-        }
-
-        /**
-         * Makes sure the session is updated before calling the
-         * superclass <code>sendError()</code>
-         */
-        public void sendError(int sc, String msg) throws IOException {
-            doSessionUpdate();
-            super.sendError(sc, msg);
-        }
-
-        /**
-         * Makes sure the session is updated before calling the
-         * superclass <code>sendRedirect()</code>
-         */
-        public void sendRedirect(String location) throws IOException {
-            doSessionUpdate();
-            super.sendRedirect(location);
-        }
-
-        /**
-         * Calls <code>storeSecurityContextInSession()</code>
-         */
-        private void doSessionUpdate() {
-            if (sessionUpdateDone) {
-                return;
-            }
-            SecurityContext securityContext = SecurityContextHolder.getContext();
-            storeSecurityContextInSession(securityContext, request,
-                    httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution);
-            sessionUpdateDone = true;
-        }
+    //~ Methods ========================================================================================================
 
-        /**
-         * Tells if the response wrapper has called
-         * <code>storeSecurityContextInSession()</code>.
-         */
-        public boolean isSessionUpdateDone() {
-            return sessionUpdateDone;
+    public void afterPropertiesSet() throws Exception {
+        if (forceEagerSessionCreation && !allowSessionCreation) {
+            throw new IllegalArgumentException(
+                    "If using forceEagerSessionCreation, you must set allowSessionCreation to also be true");
         }
-
     }
 
+
 }

+ 1 - 0
core/src/main/java/org/springframework/security/context/SecurityContextPersistenceFilter.java

@@ -73,6 +73,7 @@ public class SecurityContextPersistenceFilter extends SpringSecurityFilter {
             // Crucial removal of SecurityContextHolder contents - do this before anything else.
             SecurityContextHolder.clearContext();
             repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
+            request.removeAttribute(FILTER_APPLIED);
 
             if (logger.isDebugEnabled()) {
                 logger.debug("SecurityContextHolder now cleared, as request processing completed");

+ 4 - 9
core/src/test/java/org/springframework/security/context/SecurityContextPersistenceFilterTests.java

@@ -98,23 +98,18 @@ public class SecurityContextPersistenceFilterTests {
     }
 
     @Test
-    public void filterIsOnlyAppliedOncePerRequest() throws Exception {
+    public void filterIsNotAppliedAgainIfFilterAppliedAttributeIsSet() throws Exception {
         final FilterChain chain = jmock.mock(FilterChain.class);
         final MockHttpServletRequest request = new MockHttpServletRequest();
         final MockHttpServletResponse response = new MockHttpServletResponse();
         SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();
-        final SecurityContextRepository repo = jmock.mock(SecurityContextRepository.class);
-        filter.setSecurityContextRepository(repo);
-        final SecurityContext sc = SecurityContextHolder.getContext();
+        filter.setSecurityContextRepository(jmock.mock(SecurityContextRepository.class));
 
         jmock.checking(new Expectations() {{
-            oneOf(repo).loadContext(with(aNonNull(HttpRequestResponseHolder.class))); will(returnValue(sc));
-            oneOf(repo).saveContext(sc, request, response);
-            exactly(2).of(chain).doFilter(request, response);
+            oneOf(chain).doFilter(request, response);
         }});
 
-        filter.doFilter(request, response, chain);
-        assertNotNull(request.getAttribute(SecurityContextPersistenceFilter.FILTER_APPLIED));
+        request.setAttribute(SecurityContextPersistenceFilter.FILTER_APPLIED, Boolean.TRUE);
         filter.doFilter(request, response, chain);
         jmock.assertIsSatisfied();
     }