/* * Copyright 2002-2017 the original author or authors. * * 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 * * https://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.springframework.web.filter; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Proxy for a standard Servlet Filter, delegating to a Spring-managed bean that * implements the Filter interface. Supports a "targetBeanName" filter init-param * in {@code web.xml}, specifying the name of the target bean in the Spring * application context. * *

{@code web.xml} will usually contain a {@code DelegatingFilterProxy} definition, * with the specified {@code filter-name} corresponding to a bean name in * Spring's root application context. All calls to the filter proxy will then * be delegated to that bean in the Spring context, which is required to implement * the standard Servlet Filter interface. * *

This approach is particularly useful for Filter implementation with complex * setup needs, allowing to apply the full Spring bean definition machinery to * Filter instances. Alternatively, consider standard Filter setup in combination * with looking up service beans from the Spring root application context. * *

NOTE: The lifecycle methods defined by the Servlet Filter interface * will by default not be delegated to the target bean, relying on the * Spring application context to manage the lifecycle of that bean. Specifying * the "targetFilterLifecycle" filter init-param as "true" will enforce invocation * of the {@code Filter.init} and {@code Filter.destroy} lifecycle methods * on the target bean, letting the servlet container manage the filter lifecycle. * *

As of Spring 3.1, {@code DelegatingFilterProxy} has been updated to optionally accept * constructor parameters when using Servlet 3.0's instance-based filter registration * methods, usually in conjunction with Spring 3.1's * {@link org.springframework.web.WebApplicationInitializer} SPI. These constructors allow * for providing the delegate Filter bean directly, or providing the application context * and bean name to fetch, avoiding the need to look up the application context from the * ServletContext. * *

This class was originally inspired by Spring Security's {@code FilterToBeanProxy} * class, written by Ben Alex. * * @author Juergen Hoeller * @author Sam Brannen * @author Chris Beams * @since 1.2 * @see #setTargetBeanName * @see #setTargetFilterLifecycle * @see Filter#doFilter * @see Filter#init * @see Filter#destroy * @see #DelegatingFilterProxy(Filter) * @see #DelegatingFilterProxy(String) * @see #DelegatingFilterProxy(String, WebApplicationContext) * @see org.springframework.web.WebApplicationInitializer */ public class DelegatingFilterProxy extends GenericFilterBean { private String contextAttribute; private WebApplicationContext webApplicationContext; private String targetBeanName; private boolean targetFilterLifecycle = false; private volatile Filter delegate; private final Object delegateMonitor = new Object(); /** * Create a new {@code DelegatingFilterProxy}. For traditional (pre-Servlet 3.0) use * in {@code web.xml}. * @see #setTargetBeanName(String) */ public DelegatingFilterProxy() { } /** * Create a new {@code DelegatingFilterProxy} with the given {@link Filter} delegate. * Bypasses entirely the need for interacting with a Spring application context, * specifying the {@linkplain #setTargetBeanName target bean name}, etc. *

For use in Servlet 3.0+ environments where instance-based registration of * filters is supported. * @param delegate the {@code Filter} instance that this proxy will delegate to and * manage the lifecycle for (must not be {@code null}). * @see #doFilter(ServletRequest, ServletResponse, FilterChain) * @see #invokeDelegate(Filter, ServletRequest, ServletResponse, FilterChain) * @see #destroy() * @see #setEnvironment(org.springframework.core.env.Environment) */ public DelegatingFilterProxy(Filter delegate) { Assert.notNull(delegate, "Delegate Filter must not be null"); this.delegate = delegate; } /** * Create a new {@code DelegatingFilterProxy} that will retrieve the named target * bean from the Spring {@code WebApplicationContext} found in the {@code ServletContext} * (either the 'root' application context or the context named by * {@link #setContextAttribute}). *

For use in Servlet 3.0+ environments where instance-based registration of * filters is supported. *

The target bean must implement the standard Servlet Filter. * @param targetBeanName name of the target filter bean to look up in the Spring * application context (must not be {@code null}). * @see #findWebApplicationContext() * @see #setEnvironment(org.springframework.core.env.Environment) */ public DelegatingFilterProxy(String targetBeanName) { this(targetBeanName, null); } /** * Create a new {@code DelegatingFilterProxy} that will retrieve the named target * bean from the given Spring {@code WebApplicationContext}. *

For use in Servlet 3.0+ environments where instance-based registration of * filters is supported. *

The target bean must implement the standard Servlet Filter interface. *

The given {@code WebApplicationContext} may or may not be refreshed when passed * in. If it has not, and if the context implements {@link ConfigurableApplicationContext}, * a {@link ConfigurableApplicationContext#refresh() refresh()} will be attempted before * retrieving the named target bean. *

This proxy's {@code Environment} will be inherited from the given * {@code WebApplicationContext}. * @param targetBeanName name of the target filter bean in the Spring application * context (must not be {@code null}). * @param wac the application context from which the target filter will be retrieved; * if {@code null}, an application context will be looked up from {@code ServletContext} * as a fallback. * @see #findWebApplicationContext() * @see #setEnvironment(org.springframework.core.env.Environment) */ public DelegatingFilterProxy(String targetBeanName, WebApplicationContext wac) { Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty"); this.setTargetBeanName(targetBeanName); this.webApplicationContext = wac; if (wac != null) { this.setEnvironment(wac.getEnvironment()); } } /** * Set the name of the ServletContext attribute which should be used to retrieve the * {@link WebApplicationContext} from which to load the delegate {@link Filter} bean. */ public void setContextAttribute(String contextAttribute) { this.contextAttribute = contextAttribute; } /** * Return the name of the ServletContext attribute which should be used to retrieve the * {@link WebApplicationContext} from which to load the delegate {@link Filter} bean. */ public String getContextAttribute() { return this.contextAttribute; } /** * Set the name of the target bean in the Spring application context. * The target bean must implement the standard Servlet Filter interface. *

By default, the {@code filter-name} as specified for the * DelegatingFilterProxy in {@code web.xml} will be used. */ public void setTargetBeanName(String targetBeanName) { this.targetBeanName = targetBeanName; } /** * Return the name of the target bean in the Spring application context. */ protected String getTargetBeanName() { return this.targetBeanName; } /** * Set whether to invoke the {@code Filter.init} and * {@code Filter.destroy} lifecycle methods on the target bean. *

Default is "false"; target beans usually rely on the Spring application * context for managing their lifecycle. Setting this flag to "true" means * that the servlet container will control the lifecycle of the target * Filter, with this proxy delegating the corresponding calls. */ public void setTargetFilterLifecycle(boolean targetFilterLifecycle) { this.targetFilterLifecycle = targetFilterLifecycle; } /** * Return whether to invoke the {@code Filter.init} and * {@code Filter.destroy} lifecycle methods on the target bean. */ protected boolean isTargetFilterLifecycle() { return this.targetFilterLifecycle; } @Override protected void initFilterBean() throws ServletException { synchronized (this.delegateMonitor) { if (this.delegate == null) { // If no target bean name specified, use filter name. if (this.targetBeanName == null) { this.targetBeanName = getFilterName(); } // Fetch Spring root application context and initialize the delegate early, // if possible. If the root application context will be started after this // filter proxy, we'll have to resort to lazy initialization. WebApplicationContext wac = findWebApplicationContext(); if (wac != null) { this.delegate = initDelegate(wac); } } } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { String referer = ((HttpServletRequest)request).getHeader("Referer"); StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("http://").append(request.getServerName()); StringBuffer stringBuffer1 = new StringBuffer(); stringBuffer1.append("https://").append(request.getServerName()); if(referer != null && !referer.equals("") ){ if(referer.lastIndexOf(String.valueOf(stringBuffer)) == 0 && referer.lastIndexOf(String.valueOf(stringBuffer1)) == 0){ HttpServletResponse httpResp1 = (HttpServletResponse)response; httpResp1.setStatus(500); httpResp1.setContentType("text/html; charset=utf-8"); httpResp1.getWriter().write("系统不支持当前域名的访问!"); return; } } String method = ((HttpServletRequest)request).getMethod(); if (method.toUpperCase().equals("OPTIONS")){ HttpServletResponse httpResp1 = (HttpServletResponse)response; httpResp1.setStatus(500); httpResp1.setContentType("text/html; charset=utf-8"); httpResp1.getWriter().write("系统不支持访问!"); return; } String contentType = ((HttpServletRequest)request).getHeader("Content-Type"); if ((method.toUpperCase().equals("GET") && !StringUtils.isEmpty(contentType) && contentType.contains("form"))|| (StringUtils.isEmpty(contentType) && method.toUpperCase().equals("POST"))){ HttpServletResponse httpResp1 = (HttpServletResponse)response; httpResp1.setStatus(500); httpResp1.setContentType("text/html; charset=utf-8"); httpResp1.getWriter().write("系统不支持访问!"); return; } HttpServletRequest httpReq = (HttpServletRequest) request; HttpServletResponse httpRes = (HttpServletResponse) response; /**禁止缓存**/ httpRes.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); httpRes.setHeader("Pragma", "no-cache"); httpRes.setHeader("X-Content-Type-Options", "nosniff"); httpRes.setHeader("X-XSS-Protection", "1"); httpRes.setHeader("strict-transport-security", "max-age=31536000"); httpRes.setHeader("X-Frame-Options", "DENY"); //安全测评需要,如影响使用请注释掉 if(httpReq.getRequestURI().contains("javax.faces.resource")){ httpRes.setHeader("Content-Security-Policy", "default-src 'self';script-src 'self';frame-ancestors 'self';"); } // Lazily initialize the delegate if necessary. Filter delegateToUse = this.delegate; if (delegateToUse == null) { synchronized (this.delegateMonitor) { delegateToUse = this.delegate; if (delegateToUse == null) { WebApplicationContext wac = findWebApplicationContext(); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: " + "no ContextLoaderListener or DispatcherServlet registered?"); } delegateToUse = initDelegate(wac); } this.delegate = delegateToUse; } } // Let the delegate perform the actual doFilter operation. invokeDelegate(delegateToUse, request, response, filterChain); } @Override public void destroy() { Filter delegateToUse = this.delegate; if (delegateToUse != null) { destroyDelegate(delegateToUse); } } /** * Return the {@code WebApplicationContext} passed in at construction time, if available. * Otherwise, attempt to retrieve a {@code WebApplicationContext} from the * {@code ServletContext} attribute with the {@linkplain #setContextAttribute * configured name} if set. Otherwise look up a {@code WebApplicationContext} under * the well-known "root" application context attribute. The * {@code WebApplicationContext} must have already been loaded and stored in the * {@code ServletContext} before this filter gets initialized (or invoked). *

Subclasses may override this method to provide a different * {@code WebApplicationContext} retrieval strategy. * @return the {@code WebApplicationContext} for this proxy, or {@code null} if not found * @see #DelegatingFilterProxy(String, WebApplicationContext) * @see #getContextAttribute() * @see WebApplicationContextUtils#getWebApplicationContext(ServletContext) * @see WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE */ protected WebApplicationContext findWebApplicationContext() { if (this.webApplicationContext != null) { // The user has injected a context at construction time -> use it... if (this.webApplicationContext instanceof ConfigurableApplicationContext) { ConfigurableApplicationContext cac = (ConfigurableApplicationContext) this.webApplicationContext; if (!cac.isActive()) { // The context has not yet been refreshed -> do so before returning it... cac.refresh(); } } return this.webApplicationContext; } String attrName = getContextAttribute(); if (attrName != null) { return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); } else { return WebApplicationContextUtils.findWebApplicationContext(getServletContext()); } } /** * Initialize the Filter delegate, defined as bean the given Spring * application context. *

The default implementation fetches the bean from the application context * and calls the standard {@code Filter.init} method on it, passing * in the FilterConfig of this Filter proxy. * @param wac the root application context * @return the initialized delegate Filter * @throws ServletException if thrown by the Filter * @see #getTargetBeanName() * @see #isTargetFilterLifecycle() * @see #getFilterConfig() * @see Filter#init(FilterConfig) */ protected Filter initDelegate(WebApplicationContext wac) throws ServletException { Filter delegate = wac.getBean(getTargetBeanName(), Filter.class); if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); } return delegate; } /** * Actually invoke the delegate Filter with the given request and response. * @param delegate the delegate Filter * @param request the current HTTP request * @param response the current HTTP response * @param filterChain the current FilterChain * @throws ServletException if thrown by the Filter * @throws IOException if thrown by the Filter */ protected void invokeDelegate( Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { delegate.doFilter(request, response, filterChain); } /** * Destroy the Filter delegate. * Default implementation simply calls {@code Filter.destroy} on it. * @param delegate the Filter delegate (never {@code null}) * @see #isTargetFilterLifecycle() * @see Filter#destroy() */ protected void destroyDelegate(Filter delegate) { if (isTargetFilterLifecycle()) { delegate.destroy(); } } }