📄 httpsessioncontextintegrationfilter.java
字号:
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * Licensed 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.acegisecurity.context;import java.io.IOException;import java.lang.reflect.Method;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;import javax.servlet.http.HttpSession;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.InitializingBean;import org.springframework.util.Assert;import org.springframework.util.ReflectionUtils;/** * Populates the {@link SecurityContextHolder} with information obtained from * the <code>HttpSession</code>. * <p/> * <p/> * The <code>HttpSession</code> will be queried to retrieve the * <code>SecurityContext</code> that should be stored against the * <code>SecurityContextHolder</code> for the duration of the web request. At * the end of the web request, any updates made to the * <code>SecurityContextHolder</code> will be persisted back to the * <code>HttpSession</code> by this filter. * </p> * <p/> * If a valid <code>SecurityContext</code> cannot be obtained from the * <code>HttpSession</code> for whatever reason, a fresh * <code>SecurityContext</code> will be created and used instead. The created * object will be of the instance defined by the {@link #setContext(Class)} * method (which defaults to {@link org.acegisecurity.context.SecurityContextImpl}. * </p> * <p/> * No <code>HttpSession</code> will be created by this filter if one does not * already exist. If at the end of the web request the <code>HttpSession</code> * does not exist, a <code>HttpSession</code> will <b>only</b> be created if * the current contents of the <code>SecurityContextHolder</code> are not * {@link java.lang.Object#equals(java.lang.Object)} to a <code>new</code> * instance of {@link #setContext(Class)}. This avoids needless * <code>HttpSession</code> creation, but automates the storage of changes * made to the <code>SecurityContextHolder</code>. There is one exception to * this rule, that is if the {@link #forceEagerSessionCreation} property is * <code>true</code>, in which case sessions will always be created * irrespective of normal session-minimisation logic (the default is * <code>false</code>, as this is resource intensive and not recommended). * </p> * <p/> * This filter will only execute once per request, to resolve servlet container * (specifically Weblogic) incompatibilities. * </p> * <p/> * If for whatever reason no <code>HttpSession</code> should <b>ever</b> be * created (eg this filter is only being used with Basic authentication or * similar clients that will never present the same <code>jsessionid</code> * etc), the {@link #setAllowSessionCreation(boolean)} should be set to * <code>false</code>. Only do this if you really need to conserve server * memory and ensure all classes using the <code>SecurityContextHolder</code> * are designed to have no persistence of the <code>SecurityContext</code> * between web requests. Please note that if {@link #forceEagerSessionCreation} * is <code>true</code>, the <code>allowSessionCreation</code> must also be * <code>true</code> (setting it to <code>false</code> will cause a startup * time error). * </p> * <p/> * This filter MUST be executed BEFORE any authentication processing mechanisms. * Authentication processing mechanisms (eg BASIC, CAS processing filters etc) * expect the <code>SecurityContextHolder</code> to contain a valid * <code>SecurityContext</code> by the time they execute. * </p> * * @author Ben Alex * @author Patrick Burleson * @author Luke Taylor * @author Martin Algesten * * @version $Id: HttpSessionContextIntegrationFilter.java 2004 2007-09-01 14:43:09Z raykrueger $ */public class HttpSessionContextIntegrationFilter implements InitializingBean, Filter { //~ Static fields/initializers ===================================================================================== protected static final Log logger = LogFactory.getLog(HttpSessionContextIntegrationFilter.class); static final String FILTER_APPLIED = "__acegi_session_integration_filter_applied"; public static final String ACEGI_SECURITY_CONTEXT_KEY = "ACEGI_SECURITY_CONTEXT"; //~ Instance fields ================================================================================================ private Class context = SecurityContextImpl.class; private Object contextObject; /** * Indicates if this filter can create a <code>HttpSession</code> if * needed (sessions are always created sparingly, but setting this value to * <code>false</code> will prohibit sessions from ever being created). * Defaults to <code>true</code>. Do not set to <code>false</code> if * you are have set {@link #forceEagerSessionCreation} to <code>true</code>, * as the properties would be in conflict. */ private boolean allowSessionCreation = true; /** * Indicates if this filter is required to create a <code>HttpSession</code> * for every request before proceeding through the filter chain, even if the * <code>HttpSession</code> would not ordinarily have been created. By * default this is <code>false</code>, which is entirely appropriate for * most circumstances as you do not want a <code>HttpSession</code> * created unless the filter actually needs one. It is envisaged the main * situation in which this property would be set to <code>true</code> is * if using other filters that depend on a <code>HttpSession</code> * already existing, such as those which need to obtain a session ID. This * is only required in specialised cases, so leave it set to * <code>false</code> unless you have an actual requirement and are * conscious of the session creation overhead. */ private boolean forceEagerSessionCreation = false; /** * Indicates whether the <code>SecurityContext</code> will be cloned from * the <code>HttpSession</code>. The default is to simply reference (ie * the default is <code>false</code>). The default may cause issues if * concurrent threads need to have a different security identity from other * threads being concurrently processed that share the same * <code>HttpSession</code>. In most normal environments this does not * represent an issue, as changes to the security identity in one thread is * allowed to affect the security identitiy in other threads associated with * the same <code>HttpSession</code>. For unusual cases where this is not * permitted, change this value to <code>true</code> and ensure the * {@link #context} is set to a <code>SecurityContext</code> that * implements {@link Cloneable} and overrides the <code>clone()</code> * method. */ private boolean cloneFromHttpSession = false; public boolean isCloneFromHttpSession() { return cloneFromHttpSession; } public void setCloneFromHttpSession(boolean cloneFromHttpSession) { this.cloneFromHttpSession = cloneFromHttpSession; } public HttpSessionContextIntegrationFilter() throws ServletException { this.contextObject = generateNewContext(); } //~ Methods ======================================================================================================== public void afterPropertiesSet() throws Exception { if ((this.context == null) || (!SecurityContext.class.isAssignableFrom(this.context))) { throw new IllegalArgumentException("context must be defined and implement SecurityContext " + "(typically use org.acegisecurity.context.SecurityContextImpl; existing class is " + this.context + ")"); } if (forceEagerSessionCreation && !allowSessionCreation) { throw new IllegalArgumentException( "If using forceEagerSessionCreation, you must set allowSessionCreation to also be true"); } } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { Assert.isInstanceOf(HttpServletRequest.class, req, "ServletRequest must be an instance of HttpServletRequest"); Assert.isInstanceOf(HttpServletResponse.class, res, "ServletResponse must be an instance of HttpServletResponse"); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (request.getAttribute(FILTER_APPLIED) != null) { // ensure that filter is only applied once per request chain.doFilter(request, response); return; } HttpSession httpSession = null; try { httpSession = request.getSession(forceEagerSessionCreation); } catch (IllegalStateException ignored) { } 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 ACEGI_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/>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -