001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.filter; 018 019import java.io.IOException; 020 021import javax.servlet.DispatcherType; 022import javax.servlet.FilterChain; 023import javax.servlet.ServletException; 024import javax.servlet.ServletRequest; 025import javax.servlet.ServletResponse; 026import javax.servlet.http.HttpServletRequest; 027import javax.servlet.http.HttpServletResponse; 028 029import org.springframework.web.context.request.async.WebAsyncManager; 030import org.springframework.web.context.request.async.WebAsyncUtils; 031import org.springframework.web.util.WebUtils; 032 033/** 034 * Filter base class that aims to guarantee a single execution per request 035 * dispatch, on any servlet container. It provides a {@link #doFilterInternal} 036 * method with HttpServletRequest and HttpServletResponse arguments. 037 * 038 * <p>As of Servlet 3.0, a filter may be invoked as part of a 039 * {@link javax.servlet.DispatcherType#REQUEST REQUEST} or 040 * {@link javax.servlet.DispatcherType#ASYNC ASYNC} dispatches that occur in 041 * separate threads. A filter can be configured in {@code web.xml} whether it 042 * should be involved in async dispatches. However, in some cases servlet 043 * containers assume different default configuration. Therefore sub-classes can 044 * override the method {@link #shouldNotFilterAsyncDispatch()} to declare 045 * statically if they should indeed be invoked, <em>once</em>, during both types 046 * of dispatches in order to provide thread initialization, logging, security, 047 * and so on. This mechanism complements and does not replace the need to 048 * configure a filter in {@code web.xml} with dispatcher types. 049 * 050 * <p>Subclasses may use {@link #isAsyncDispatch(HttpServletRequest)} to 051 * determine when a filter is invoked as part of an async dispatch, and use 052 * {@link #isAsyncStarted(HttpServletRequest)} to determine when the request 053 * has been placed in async mode and therefore the current dispatch won't be 054 * the last one for the given request. 055 * 056 * <p>Yet another dispatch type that also occurs in its own thread is 057 * {@link javax.servlet.DispatcherType#ERROR ERROR}. Subclasses can override 058 * {@link #shouldNotFilterErrorDispatch()} if they wish to declare statically 059 * if they should be invoked <em>once</em> during error dispatches. 060 * 061 * <p>The {@link #getAlreadyFilteredAttributeName} method determines how to 062 * identify that a request is already filtered. The default implementation is 063 * based on the configured name of the concrete filter instance. 064 * 065 * @author Juergen Hoeller 066 * @author Rossen Stoyanchev 067 * @since 06.12.2003 068 */ 069public abstract class OncePerRequestFilter extends GenericFilterBean { 070 071 /** 072 * Suffix that gets appended to the filter name for the 073 * "already filtered" request attribute. 074 * @see #getAlreadyFilteredAttributeName 075 */ 076 public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED"; 077 078 079 /** 080 * This {@code doFilter} implementation stores a request attribute for 081 * "already filtered", proceeding without filtering again if the 082 * attribute is already there. 083 * @see #getAlreadyFilteredAttributeName 084 * @see #shouldNotFilter 085 * @see #doFilterInternal 086 */ 087 @Override 088 public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 089 throws ServletException, IOException { 090 091 if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { 092 throw new ServletException("OncePerRequestFilter just supports HTTP requests"); 093 } 094 HttpServletRequest httpRequest = (HttpServletRequest) request; 095 HttpServletResponse httpResponse = (HttpServletResponse) response; 096 097 String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); 098 boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null; 099 100 if (skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) { 101 102 // Proceed without invoking this filter... 103 filterChain.doFilter(request, response); 104 } 105 else if (hasAlreadyFilteredAttribute) { 106 107 if (DispatcherType.ERROR.equals(request.getDispatcherType())) { 108 doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain); 109 return; 110 } 111 112 // Proceed without invoking this filter... 113 filterChain.doFilter(request, response); 114 } 115 else { 116 // Do invoke this filter... 117 request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); 118 try { 119 doFilterInternal(httpRequest, httpResponse, filterChain); 120 } 121 finally { 122 // Remove the "already filtered" request attribute for this request. 123 request.removeAttribute(alreadyFilteredAttributeName); 124 } 125 } 126 } 127 128 private boolean skipDispatch(HttpServletRequest request) { 129 if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) { 130 return true; 131 } 132 if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) { 133 return true; 134 } 135 return false; 136 } 137 138 /** 139 * The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced 140 * in Servlet 3.0 means a filter can be invoked in more than one thread over 141 * the course of a single request. This method returns {@code true} if the 142 * filter is currently executing within an asynchronous dispatch. 143 * @param request the current request 144 * @since 3.2 145 * @see WebAsyncManager#hasConcurrentResult() 146 */ 147 protected boolean isAsyncDispatch(HttpServletRequest request) { 148 return WebAsyncUtils.getAsyncManager(request).hasConcurrentResult(); 149 } 150 151 /** 152 * Whether request processing is in asynchronous mode meaning that the 153 * response will not be committed after the current thread is exited. 154 * @param request the current request 155 * @since 3.2 156 * @see WebAsyncManager#isConcurrentHandlingStarted() 157 */ 158 protected boolean isAsyncStarted(HttpServletRequest request) { 159 return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted(); 160 } 161 162 /** 163 * Return the name of the request attribute that identifies that a request 164 * is already filtered. 165 * <p>The default implementation takes the configured name of the concrete filter 166 * instance and appends ".FILTERED". If the filter is not fully initialized, 167 * it falls back to its class name. 168 * @see #getFilterName 169 * @see #ALREADY_FILTERED_SUFFIX 170 */ 171 protected String getAlreadyFilteredAttributeName() { 172 String name = getFilterName(); 173 if (name == null) { 174 name = getClass().getName(); 175 } 176 return name + ALREADY_FILTERED_SUFFIX; 177 } 178 179 /** 180 * Can be overridden in subclasses for custom filtering control, 181 * returning {@code true} to avoid filtering of the given request. 182 * <p>The default implementation always returns {@code false}. 183 * @param request current HTTP request 184 * @return whether the given request should <i>not</i> be filtered 185 * @throws ServletException in case of errors 186 */ 187 protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { 188 return false; 189 } 190 191 /** 192 * The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced 193 * in Servlet 3.0 means a filter can be invoked in more than one thread 194 * over the course of a single request. Some filters only need to filter 195 * the initial thread (e.g. request wrapping) while others may need 196 * to be invoked at least once in each additional thread for example for 197 * setting up thread locals or to perform final processing at the very end. 198 * <p>Note that although a filter can be mapped to handle specific dispatcher 199 * types via {@code web.xml} or in Java through the {@code ServletContext}, 200 * servlet containers may enforce different defaults with regards to 201 * dispatcher types. This flag enforces the design intent of the filter. 202 * <p>The default return value is "true", which means the filter will not be 203 * invoked during subsequent async dispatches. If "false", the filter will 204 * be invoked during async dispatches with the same guarantees of being 205 * invoked only once during a request within a single thread. 206 * @since 3.2 207 */ 208 protected boolean shouldNotFilterAsyncDispatch() { 209 return true; 210 } 211 212 /** 213 * Whether to filter error dispatches such as when the servlet container 214 * processes and error mapped in {@code web.xml}. The default return value 215 * is "true", which means the filter will not be invoked in case of an error 216 * dispatch. 217 * @since 3.2 218 */ 219 protected boolean shouldNotFilterErrorDispatch() { 220 return true; 221 } 222 223 224 /** 225 * Same contract as for {@code doFilter}, but guaranteed to be 226 * just invoked once per request within a single request thread. 227 * See {@link #shouldNotFilterAsyncDispatch()} for details. 228 * <p>Provides HttpServletRequest and HttpServletResponse arguments instead of the 229 * default ServletRequest and ServletResponse ones. 230 */ 231 protected abstract void doFilterInternal( 232 HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 233 throws ServletException, IOException; 234 235 /** 236 * Typically an ERROR dispatch happens after the REQUEST dispatch completes, 237 * and the filter chain starts anew. On some servers however the ERROR 238 * dispatch may be nested within the REQUEST dispatch, e.g. as a result of 239 * calling {@code sendError} on the response. In that case we are still in 240 * the filter chain, on the same thread, but the request and response have 241 * been switched to the original, unwrapped ones. 242 * <p>Sub-classes may use this method to filter such nested ERROR dispatches 243 * and re-apply wrapping on the request or response. {@code ThreadLocal} 244 * context, if any, should still be active as we are still nested within 245 * the filter chain. 246 * @since 5.1.9 247 */ 248 protected void doFilterNestedErrorDispatch(HttpServletRequest request, HttpServletResponse response, 249 FilterChain filterChain) throws ServletException, IOException { 250 251 filterChain.doFilter(request, response); 252 } 253 254}