JSF 3.0应用登出时Servlet Filter拦截异常请求问题咨询
问题分析与解决方案
核心问题
点击登出链接后,服务器执行会话失效并尝试转发到login.jsp,但Filter却捕获到第三次请求为原页面accountSelection.jsf,最终错误触发会话过期处理流程。
根本原因
- GET请求+服务器转发的上下文残留:你用普通
<a>标签(GET请求)触发登出,执行会话失效后使用dispatch()做服务器内部转发。这种方式下,浏览器地址栏仍停留在logOut.jsf,且原页面accountSelection.jsf的请求上下文未被彻底切断——如果浏览器存在未完成的资源请求、或者JSF视图状态失效触发的自动重试,就会再次发起对原页面的请求,此时会话已失效,直接触发Filter的过期逻辑。 - 转发的请求未被Filter拦截(可选):若Filter未配置
<dispatcher>FORWARD</dispatcher>,服务器内部转发到login.jsp的请求不会被Filter捕获,导致你看不到预期的login.jsp日志。
解决方案
1. 用重定向替代服务器转发
将登出Bean中的转发代码改为重定向,彻底切断与原请求上下文的关联:
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext(); // 先失效会话,再执行重定向 ec.invalidateSession(); ec.redirect(ec.getRequestContextPath() + "/login.jsp"); // 标记响应完成,避免后续JSF处理 FacesContext.getCurrentInstance().responseComplete();
重定向会让浏览器主动请求login.jsp,地址栏更新为登录页,原页面的所有后续请求都会被终止。
2. 规范登出请求方式(推荐)
改用POST请求处理登出,避免GET请求带来的缓存、重复提交风险:
<h:form id="logoutForm"> <h:commandLink value="登出" action="#{logoutBean.logout}"/> </h:form>
POST请求不会被浏览器缓存,且能确保登出操作的幂等性。
3. 调整Filter配置(可选)
如果需要Filter拦截服务器内部转发的请求,在web.xml中补充<dispatcher>配置:
<filter-mapping> <filter-name>SessionExpiredFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
此配置让Filter同时拦截浏览器直接发起的请求和服务器内部转发的请求,但这不是解决当前问题的核心步骤,核心还是用重定向替代转发。
内容的提问来源于stack exchange,提问作者HockChai Lim
相关产品推荐
相关产品推荐

