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}