【问题标题】:Avoid back button on JSF web application避免 JSF Web 应用程序上的后退按钮
【发布时间】:2015-05-10 11:15:31
【问题描述】:

我正在显示非常敏感的数据。用户从我的服务器注销后,我不希望其他用户能够看到点击浏览器的“后退”按钮的数据。

我怎样才能做到这一点?

【问题讨论】:

    标签: jsf browser back-button


    【解决方案1】:

    默认情况下,浏览器的后退按钮根本不会向服务器发送 HTTP 请求。相反,它从浏览器缓存中检索页面。这本质上是无害的,但确实让最终用户感到困惑,因为他/她错误地认为它确实来自服务器。

    您需要做的就是指示浏览器不要缓存受限页面。您可以使用设置appropriate response headers 的简单servlet 过滤器来做到这一点:

    @WebFilter
    public class NoCacheFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            if (!request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
                response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
                response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
                response.setDateHeader("Expires", 0); // Proxies.
            }
    
            chain.doFilter(req, res);
        }
    
        // ...
    }
    

    (请注意,此过滤器会跳过 JSF 资源请求,whose caching actually needs to be configured separately

    要让它在每个 JSF 请求上运行,请在过滤器类上设置以下注释,假设您的 webapp 的 FacesServlet<servlet-name> 的值为 facesServlet

    @WebFilter(servletNames={"facesServlet"})
    

    或者,让它仅在特定的 URL 模式上运行,例如与受限页面匹配的模式,例如/app/*/private/*/secured/*左右,在过滤器类上设置如下注解:

    @WebFilter("/app/*")
    

    如果您已经有登录用户,您甚至可以在检查登录用户的过滤器中执行相同的工作。

    如果您碰巧使用 JSF 实用程序库 OmniFaces,那么您也可以直接获取它的 CacheControlFilter。这也透明地将 JSF 资源考虑在内。

    另见:

    【讨论】:

    • 我已经尝试过了——当我在调试器中运行它时,它会在页面加载时停止在 noCacheFilter: doFilter 方法中。然后,当我单击 Firefox 中的后退按钮(本地测试)时,它再次运行该功能。然而最终显示上一页。我应该注意什么?
    • 在 Firefox 中发生的另一个错误是每三次左右单击“返回”就会告诉您该页面已过期。然后,当您单击重新加载按钮(由错误页面提供)时,它仍然会加载上一页。
    • 我知道这种老问题,但是,这不适用于 Chrome。在 chrome 中单击后退按钮时,您仍然可以看到表单字段输入的值。
    • @bluelabel:您要查找的关键字是“自动填充”。另见 a.o. stackoverflow.com/q/9930900
    • @Talib:它们都引用了完全相同的对象。它是 Java,一种 OO 语言,而不是 PHP 或so。如果它对您不起作用,那么这些标头将在稍后被覆盖,或者旧页面仍在浏览器缓存中,或者您只是错误地观察了后退按钮的行为(即它确实击中了服务器,但在在服务器端代码中打开一些错误,使其看起来像缓存)。在一个完全空白的 Web 应用程序中,上述答案有效。
    【解决方案2】:

    我还找到了另一个很好的解决方案。

    在 faces-config.xml 添加

    <lifecycle>
        <phase-listener id="nocache">client.security.CacheControlPhaseListener</phase-listener>
    </lifecycle>
    

    并实现以下类:

    package client.security;
    
    import javax.faces.context.FacesContext;
    import javax.faces.event.PhaseEvent;
    import javax.faces.event.PhaseId;
    import javax.faces.event.PhaseListener;
    import javax.servlet.http.HttpServletResponse;
    
    @SuppressWarnings("serial")
    public class CacheControlPhaseListener implements PhaseListener
    {
        public PhaseId getPhaseId()
        {
            return PhaseId.RENDER_RESPONSE;
        }
    
        public void afterPhase(PhaseEvent event)        
        {
        }
    
        public void beforePhase(PhaseEvent event)
        {
           FacesContext facesContext = event.getFacesContext();
           HttpServletResponse response = (HttpServletResponse) facesContext
                    .getExternalContext().getResponse();
           response.addHeader("Pragma", "no-cache");
           response.addHeader("Cache-Control", "no-cache");
           // Stronger according to blog comment below that references HTTP spec
           response.addHeader("Cache-Control", "no-store");
           response.addHeader("Cache-Control", "must-revalidate");
           // some date in the past
           response.addHeader("Expires", "Mon, 8 Aug 2006 10:00:00 GMT");
        }
    } 
    

    【讨论】:

    • 虽然这可能有效,但使用 JSF 相位侦听器,同时使用 servlet 过滤器也可以实现相同的功能,就像使用锤子而不是螺丝刀来拧螺丝一样。
    • 什么意思? “虽然这可能有效,但使用 JSF 相位侦听器,同时使用 servlet 过滤器也可以实现相同的功能,就像使用锤子而不是螺丝刀来拧螺丝一样。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多