/*
|
* 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.
|
*
|
* <p>{@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.
|
*
|
* <p>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.
|
*
|
* <p><b>NOTE:</b> The lifecycle methods defined by the Servlet Filter interface
|
* will by default <i>not</i> 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.
|
*
|
* <p>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.
|
*
|
* <p>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.
|
* <p>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}).
|
* <p>For use in Servlet 3.0+ environments where instance-based registration of
|
* filters is supported.
|
* <p>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}.
|
* <p>For use in Servlet 3.0+ environments where instance-based registration of
|
* filters is supported.
|
* <p>The target bean must implement the standard Servlet Filter interface.
|
* <p>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.
|
* <p>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.
|
* <p>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.
|
* <p>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(request.getScheme()).append("://").append(request.getServerName());
|
if(referer != null && !referer.equals("") ){
|
if(referer.lastIndexOf(String.valueOf(stringBuffer)) != 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).
|
* <p>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.
|
* <p>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();
|
}
|
}
|
|
}
|