1 /* 2 * Copyright 2002-2015 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.web.filter; 18 19 import java.io.IOException; 20 import javax.servlet.FilterChain; 21 import javax.servlet.ServletException; 22 import javax.servlet.ServletRequest; 23 import javax.servlet.ServletResponse; 24 import javax.servlet.http.HttpServletRequest; 25 import javax.servlet.http.HttpServletResponse; 26 27 import org.springframework.web.context.request.async.WebAsyncManager; 28 import org.springframework.web.context.request.async.WebAsyncUtils; 29 import org.springframework.web.util.WebUtils; 30 31 /** 32 * Filter base class that aims to guarantee a single execution per request 33 * dispatch, on any servlet container. It provides a {@link #doFilterInternal} 34 * method with HttpServletRequest and HttpServletResponse arguments. 35 * 36 * <p>As of Servlet 3.0, a filter may be invoked as part of a 37 * {@link javax.servlet.DispatcherType#REQUEST REQUEST} or 38 * {@link javax.servlet.DispatcherType#ASYNC ASYNC} dispatches that occur in 39 * separate threads. A filter can be configured in {@code web.xml} whether it 40 * should be involved in async dispatches. However, in some cases servlet 41 * containers assume different default configuration. Therefore sub-classes can 42 * override the method {@link #shouldNotFilterAsyncDispatch()} to declare 43 * statically if they should indeed be invoked, <em>once</em>, during both types 44 * of dispatches in order to provide thread initialization, logging, security, 45 * and so on. This mechanism complements and does not replace the need to 46 * configure a filter in {@code web.xml} with dispatcher types. 47 * 48 * <p>Subclasses may use {@link #isAsyncDispatch(HttpServletRequest)} to 49 * determine when a filter is invoked as part of an async dispatch, and use 50 * {@link #isAsyncStarted(HttpServletRequest)} to determine when the request 51 * has been placed in async mode and therefore the current dispatch won't be 52 * the last one for the given request. 53 * 54 * <p>Yet another dispatch type that also occurs in its own thread is 55 * {@link javax.servlet.DispatcherType#ERROR ERROR}. Subclasses can override 56 * {@link #shouldNotFilterErrorDispatch()} if they wish to declare statically 57 * if they should be invoked <em>once</em> during error dispatches. 58 * 59 * <p>The {@link #getAlreadyFilteredAttributeName} method determines how to 60 * identify that a request is already filtered. The default implementation is 61 * based on the configured name of the concrete filter instance. 62 * 63 * @author Juergen Hoeller 64 * @author Rossen Stoyanchev 65 * @since 06.12.2003 66 */ 67 public abstract class OncePerRequestFilter extends GenericFilterBean { 68 69 /** 70 * Suffix that gets appended to the filter name for the 71 * "already filtered" request attribute. 72 * @see #getAlreadyFilteredAttributeName 73 */ 74 public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED"; 75 76 77 /** 78 * This {@code doFilter} implementation stores a request attribute for 79 * "already filtered", proceeding without filtering again if the 80 * attribute is already there. 81 * @see #getAlreadyFilteredAttributeName 82 * @see #shouldNotFilter 83 * @see #doFilterInternal 84 */ 85 @Override 86 public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 87 throws ServletException, IOException { 88 89 if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { 90 throw new ServletException("OncePerRequestFilter just supports HTTP requests"); 91 } 92 HttpServletRequest httpRequest = (HttpServletRequest) request; 93 HttpServletResponse httpResponse = (HttpServletResponse) response; 94 95 String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); 96 boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null; 97 98 if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) { 99 100 // Proceed without invoking this filter... 101 filterChain.doFilter(request, response); 102 } 103 else { 104 // Do invoke this filter... 105 request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); 106 try { 107 doFilterInternal(httpRequest, httpResponse, filterChain); 108 } 109 finally { 110 // Remove the "already filtered" request attribute for this request. 111 request.removeAttribute(alreadyFilteredAttributeName); 112 } 113 } 114 } 115 116 117 private boolean skipDispatch(HttpServletRequest request) { 118 if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) { 119 return true; 120 } 121 if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) { 122 return true; 123 } 124 return false; 125 } 126 127 /** 128 * The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced 129 * in Servlet 3.0 means a filter can be invoked in more than one thread over 130 * the course of a single request. This method returns {@code true} if the 131 * filter is currently executing within an asynchronous dispatch. 132 * @param request the current request 133 * @since 3.2 134 * @see WebAsyncManager#hasConcurrentResult() 135 */ 136 protected boolean isAsyncDispatch(HttpServletRequest request) { 137 return WebAsyncUtils.getAsyncManager(request).hasConcurrentResult(); 138 } 139 140 /** 141 * Whether request processing is in asynchronous mode meaning that the 142 * response will not be committed after the current thread is exited. 143 * @param request the current request 144 * @since 3.2 145 * @see WebAsyncManager#isConcurrentHandlingStarted() 146 */ 147 protected boolean isAsyncStarted(HttpServletRequest request) { 148 return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted(); 149 } 150 151 /** 152 * Return the name of the request attribute that identifies that a request 153 * is already filtered. 154 * <p>The default implementation takes the configured name of the concrete filter 155 * instance and appends ".FILTERED". If the filter is not fully initialized, 156 * it falls back to its class name. 157 * @see #getFilterName 158 * @see #ALREADY_FILTERED_SUFFIX 159 */ 160 protected String getAlreadyFilteredAttributeName() { 161 String name = getFilterName(); 162 if (name == null) { 163 name = getClass().getName(); 164 } 165 return name + ALREADY_FILTERED_SUFFIX; 166 } 167 168 /** 169 * Can be overridden in subclasses for custom filtering control, 170 * returning {@code true} to avoid filtering of the given request. 171 * <p>The default implementation always returns {@code false}. 172 * @param request current HTTP request 173 * @return whether the given request should <i>not</i> be filtered 174 * @throws ServletException in case of errors 175 */ 176 protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { 177 return false; 178 } 179 180 /** 181 * The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced 182 * in Servlet 3.0 means a filter can be invoked in more than one thread 183 * over the course of a single request. Some filters only need to filter 184 * the initial thread (e.g. request wrapping) while others may need 185 * to be invoked at least once in each additional thread for example for 186 * setting up thread locals or to perform final processing at the very end. 187 * <p>Note that although a filter can be mapped to handle specific dispatcher 188 * types via {@code web.xml} or in Java through the {@code ServletContext}, 189 * servlet containers may enforce different defaults with regards to 190 * dispatcher types. This flag enforces the design intent of the filter. 191 * <p>The default return value is "true", which means the filter will not be 192 * invoked during subsequent async dispatches. If "false", the filter will 193 * be invoked during async dispatches with the same guarantees of being 194 * invoked only once during a request within a single thread. 195 * @since 3.2 196 */ 197 protected boolean shouldNotFilterAsyncDispatch() { 198 return true; 199 } 200 201 /** 202 * Whether to filter error dispatches such as when the servlet container 203 * processes and error mapped in {@code web.xml}. The default return value 204 * is "true", which means the filter will not be invoked in case of an error 205 * dispatch. 206 * @since 3.2 207 */ 208 protected boolean shouldNotFilterErrorDispatch() { 209 return true; 210 } 211 212 213 /** 214 * Same contract as for {@code doFilter}, but guaranteed to be 215 * just invoked once per request within a single request thread. 216 * See {@link #shouldNotFilterAsyncDispatch()} for details. 217 * <p>Provides HttpServletRequest and HttpServletResponse arguments instead of the 218 * default ServletRequest and ServletResponse ones. 219 */ 220 protected abstract void doFilterInternal( 221 HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 222 throws ServletException, IOException; 223 224 }