DelegatingFilterProxy 在没有 Spring Security 的 Spring MVC 应用程序上调用了两次
Posted
技术标签:
【中文标题】DelegatingFilterProxy 在没有 Spring Security 的 Spring MVC 应用程序上调用了两次【英文标题】:DelegatingFilterProxy called twice on Spring MVC application without Spring Security 【发布时间】:2012-06-15 14:45:48 【问题描述】:我在我的 Spring MVC 应用程序中有一个作为 DelegatingFilterProxy 实现的过滤器,它被调用了两次。我不知道发生了什么事。我确信 Spring 上下文没有被创建两次,因为所有配置都在 root-context.xml 中,而我的 servlet-context.xml 是空的。
我的 web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>cheapig</display-name>
<!--Definição do Contexto Global do Container do Spring com recursos (beans) que são compartilhados com
TODOS os Servlets e Filtros -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Cria o container do Spring compartilhado com todos os Servlets e Filtros -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<display-name>sessionListener</display-name>
<listener-class>br.com.cheapig.util.SessionListener</listener-class>
</listener>
<!-- Filtro para controlar acesso -->
<filter>
<filter-name>cheapigFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>cheapigFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Definição do Servlet que processa todos os requests da aplicação. Como se está utilizando
o Framework Spring, o servlet é o DispatcherServlet. -->
<servlet>
<servlet-name>cheapig</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/cheapig/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Mapeamento dos Servlets e URLs -->
<servlet-mapping>
<servlet-name>cheapig</servlet-name>
<url-pattern>/cheapig/*</url-pattern>
</servlet-mapping>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/view/util/layout_404.jsp</location>
</error-page>
<error-page>
<error-code>405</error-code>
<location>/WEB-INF/view/util/layout_405.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/view/util/layout_500.jsp</location>
</error-page>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
我的根上下文(记住 servlet-context.xml 已被注释):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd
">
<mvc:resources mapping="/resources/**" location="/resources/" />
<!-- Imports user-defined @Controller beans that process client requests -->
<import resource="cheapig/controllers.xml" />
<import resource="cheapig/hibernatemysql5.xml"/>
<import resource="cheapig/integracaoTiles2.xml"/>
<import resource="cheapig/servicoEmail.xml"/>
<mvc:annotation-driven/>
<context:component-scan base-package="br.com.cheapig" />
<task:annotation-driven/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- Configurações de Internacionalização -->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="defaultEncoding" value="latin1" />
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="pt" />
</bean>
<!-- Mapeamento da view "rssViewer" para o bean "rssViewer" -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean id="rssViewer" class="br.com.cheapig.servico.CustomRssViewer" />
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="3145728" />
<property name="maxInMemorySize" value="3145728"></property>
</bean>
<bean id="velocityEngine"
class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="velocityProperties">
<value>
resource.loader=file
file.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
</value>
</property>
</bean>
<!-- <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="velocityProperties"> <value> resource.loader=class class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
</value> </property> <property name="resourceLoaderPath"> <value>/WEB-INF/velocity</value>
</property> </bean> -->
<!-- <bean id="controleAcesso" class="br.com.cheapig.seguranca.ControleAcesso">
<bean id="cheapigFilter" class="br.com.cheapig.seguranca.SegurancaFilter"
> <property name="controleAcesso" ref="controleAcesso" /> <property name="localeResolver"
ref="localeResolver" /> </bean> -->
</beans>
我的过滤器:
package br.com.cheapig.seguranca;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.LocaleResolver;
import br.com.cheapig.controlador.HistoricoVisitasController;
import br.com.cheapig.dominio.HistoricoVisitas;
import br.com.cheapig.dominio.Usuario;
import br.com.cheapig.exception.ControleAcessoException;
import br.com.cheapig.util.CheapigUtil;
import br.com.cheapig.util.ConstantesAmbiente;
import br.com.cheapig.util.ConstantesGenericas;
import br.com.cheapig.util.ConstantesHistoricoVisitas;
import br.com.cheapig.util.ConstantesSessao;
import br.com.cheapig.util.WorkflowUtil;
import br.com.cheapig.workflow.Request;
/**
* Classe responsável por fazer a filtragem do controle de acesso aos recursos do site
*
* @author Guilherme Macedo
* @since Apr 30, 2012
*/
@Component(value = "cheapigFilter")
public class SegurancaFilter extends OncePerRequestFilter
@Autowired
private ConstantesAmbiente constantesAmbiente;
@Autowired
private LocaleResolver localeResolver;
@Autowired
private ControleAcesso controleAcesso;
@Autowired
private HistoricoVisitasController historicoVisitasController;
/**
* Cria um novo objeto SegurancaFilter
*/
public SegurancaFilter()
super();
/**
* @see org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
*/
@SuppressWarnings("unchecked")
@Override
protected void doFilterInternal(HttpServletRequest pRequest, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
Locale locale = new Locale("pt", "br");
this.localeResolver.setLocale(pRequest, response, locale);
LocaleContextHolder.setLocale(locale);
String path = pRequest.getRequestURI().substring(pRequest.getContextPath().length());
String uri = pRequest.getRequestURI();
HttpSession vSession = pRequest.getSession();
String appId = (String) vSession.getAttribute("facebookAppId");
if(appId == null || appId.isEmpty())
vSession.setAttribute("facebookAppId", this.constantesAmbiente.getFacebookAppId());
if (path.startsWith("/resources") || path.startsWith("/css"))
filterChain.doFilter(pRequest, response); // Goes to default servlet.
System.out.println("resources");
else
System.out.println("uri: "+uri);
this.salvaVisita(pRequest);
if (uri.equals("/") || uri.equals("/cheapig/"))
pRequest.getSession().setAttribute("loadCidadeList", true);
else
pRequest.getSession().setAttribute("loadCidadeList", false);
String vUrl = uri.substring("/cheapig".length());
String vLogado = (String) vSession.getAttribute(ConstantesSessao.SESSION_LOGADO);
if (vLogado == null)
vLogado = ConstantesGenericas.SIGLA_NAO;
vSession.setAttribute(ConstantesSessao.SESSION_LOGADO, ConstantesGenericas.SIGLA_NAO);
if (vLogado.equals(ConstantesGenericas.SIGLA_SIM) && vUrl.contains("login"))
String vRedirectURL = "/cheapig/";
response.sendRedirect(vRedirectURL);
else
Usuario vUsuario = (Usuario) vSession.getAttribute(ConstantesSessao.SESSION_USUARIO);
try
if (vLogado.equalsIgnoreCase(ConstantesGenericas.SIGLA_NAO))
this.controleAcesso.verificaPermissoesAnonimas(vUrl);
else
this.controleAcesso.verificaAcesso(vUsuario, vUrl);
pRequest.getRequestDispatcher(uri).forward(pRequest, response);
catch (ControleAcessoException e)
Request vRequest = new Request();
vRequest.setRequestURI(uri);
vRequest.setRequestURL(vUrl);
vRequest.setRequestParameters(WorkflowUtil.montaParametrosURLParameterMap(pRequest.getParameterMap()));
vRequest.setRequestMethod(pRequest.getMethod());
vSession.setAttribute("COMING_REQUEST", vRequest);
response.sendRedirect("/cheapig/loginTela");
return;
public ControleAcesso getControleAcesso()
return this.controleAcesso;
public LocaleResolver getLocaleResolver()
return this.localeResolver;
@Override
protected void initFilterBean() throws ServletException
/**
* Coleta as informações do usuário e persiste no histórico de visitas
*
* @param request
* HttpServletRequest
*/
private void salvaVisita(HttpServletRequest request)
String locale = request.getLocale().getLanguage();
String browser = CheapigUtil.identificaBrowser(request.getHeader(ConstantesHistoricoVisitas.HTTP_BROWSER));
String metodo = request.getMethod();
String ip = request.getRemoteAddr();
String pagAcessada = request.getRequestURI();
String host = request.getRemoteHost();
HistoricoVisitas historico = new HistoricoVisitas();
historico.setLinguagemNavegador(locale);
historico.setBrowser(browser);
historico.setDataAcesso(new Date());
historico.setHost(host);
historico.setIpVisitante(ip);
historico.setPagAcessada(pagAcessada);
historico.setRequestMethod(metodo);
this.historicoVisitasController.cadastrarHistoricoVisitas(historico);
public void setControleAcesso(ControleAcesso controleAcesso)
this.controleAcesso = controleAcesso;
public void setLocaleResolver(LocaleResolver localeResolver)
this.localeResolver = localeResolver;
任何人都可以帮助我或给我一个提示?
提前致谢! :D
【问题讨论】:
您找到此问题的根本原因了吗?你能分享解决方案吗?谢谢 不,我还没有找到解决方案。 【参考方案1】:这个问题的唯一逻辑原因是有多个请求,因为您的过滤器正在实现OncePerRequestFilter
。
可能的原因是重定向和事件冒泡(例如,单击一个元素,该元素后面有其他元素也具有 onclick 功能)。
【讨论】:
【参考方案2】:为什么你认为过滤器被调用了两次?您是否在调试器上有断点或在日志中看到它?
当我最近遇到类似问题时(过滤器中的调试器断点命中两次),我发现我的浏览器每次点击发出 2 个请求 - 一个用于实际页面,另一个用于 favicon.ico
文件(书签中显示的小图标/favorites 或在 google chrome 的 url 栏中)。
【讨论】:
【参考方案3】:重定向可能有问题?
根据您的doFilterInternal
实施,您在特定条件下进行重定向。重定向会创建一个新请求,因此会第二次调用过滤器。
【讨论】:
【参考方案4】:我的问题是我的过滤器类上有一个@WebFilter 注释。
【讨论】:
以上是关于DelegatingFilterProxy 在没有 Spring Security 的 Spring MVC 应用程序上调用了两次的主要内容,如果未能解决你的问题,请参考以下文章
DelegatingFilterProxy 的独立 Spring 上下文
DelegatingFilterProxy 中的 NoSuchBeanDefinitionException
spring DelegatingFilterProxy 过滤器 的原理及运用