001/* 002 * Copyright 2002-2015 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; 020import javax.servlet.FilterChain; 021import javax.servlet.ServletException; 022import javax.servlet.ServletRequest; 023import javax.servlet.ServletResponse; 024import javax.servlet.http.HttpServletRequest; 025import javax.servlet.http.HttpServletResponse; 026 027import org.springframework.web.context.request.async.WebAsyncManager; 028import org.springframework.web.context.request.async.WebAsyncUtils; 029import org.springframework.web.util.WebUtils; 030 031/** 032 * Filter base class that aims to guarantee a single execution per request 033 * dispatch, on any servlet container. It provides a {@link #doFilterInternal} 034 * method with HttpServletRequest and HttpServletResponse arguments. 035 * 036 * <p>As of Servlet 3.0, a filter may be invoked as part of a 037 * {@link javax.servlet.DispatcherType#REQUEST REQUEST} or 038 * {@link javax.servlet.DispatcherType#ASYNC ASYNC} dispatches that occur in 039 * separate threads. A filter can be configured in {@code web.xml} whether it 040 * should be involved in async dispatches. However, in some cases servlet 041 * containers assume different default configuration. Therefore sub-classes can 042 * override the method {@link #shouldNotFilterAsyncDispatch()} to declare 043 * statically if they should indeed be invoked, <em>once</em>, during both types 044 * of dispatches in order to provide thread initialization, logging, security, 045 * and so on. This mechanism complements and does not replace the need to 046 * configure a filter in {@code web.xml} with dispatcher types. 047 * 048 * <p>Subclasses may use {@link #isAsyncDispatch(HttpServletRequest)} to 049 * determine when a filter is invoked as part of an async dispatch, and use 050 * {@link #isAsyncStarted(HttpServletRequest)} to determine when the request 051 * has been placed in async mode and therefore the current dispatch won't be 052 * the last one for the given request. 053 * 054 * <p>Yet another dispatch type that also occurs in its own thread is 055 * {@link javax.servlet.DispatcherType#ERROR ERROR}. Subclasses can override 056 * {@link #shouldNotFilterErrorDispatch()} if they wish to declare statically 057 * if they should be invoked <em>once</em> during error dispatches. 058 * 059 * <p>The {@link #getAlreadyFilteredAttributeName} method determines how to 060 * identify that a request is already filtered. The default implementation is 061 * based on the configured name of the concrete filter instance. 062 * 063 * @author Juergen Hoeller 064 * @author Rossen Stoyanchev 065 * @since 06.12.2003 066 */ 067public abstract class OncePerRequestFilter extends GenericFilterBean { 068 069 /** 070 * Suffix that gets appended to the filter name for the 071 * "already filtered" request attribute. 072 * @see #getAlreadyFilteredAttributeName 073 */ 074 public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED"; 075 076 077 /** 078 * This {@code doFilter} implementation stores a request attribute for 079 * "already filtered", proceeding without filtering again if the 080 * attribute is already there. 081 * @see #getAlreadyFilteredAttributeName 082 * @see #shouldNotFilter 083 * @see #doFilterInternal 084 */ 085 @Override 086 public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 087 throws ServletException, IOException { 088 089 if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { 090 throw new ServletException("OncePerRequestFilter just supports HTTP requests"); 091 } 092 HttpServletRequest httpRequest = (HttpServletRequest) request; 093 HttpServletResponse httpResponse = (HttpServletResponse) response; 094 095 String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); 096 boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null; 097 098 if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) { 099 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}