Solución:
De forma predeterminada, el botón de retroceso del navegador no envía una solicitud HTTP al servidor en absoluto. En cambio, recupera la página del caché del navegador. Esto es esencialmente inofensivo, pero de hecho confunde al usuario final, porque piensa incorrectamente que realmente proviene del servidor.
Todo lo que necesita hacer es indicar al navegador que no almacene en caché las páginas restringidas. Puede hacer esto con un filtro de servlet simple que establece los encabezados de respuesta apropiados:
@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);
}
// ...
}
(tenga en cuenta que este filtro omite las solicitudes de recursos JSF, cuyo almacenamiento en caché debe configurarse por separado)
Para que se ejecute en cada solicitud JSF, establezca la siguiente anotación en la clase de filtro, asumiendo que el valor de la <servlet-name>
de El FacesServlet
en tu aplicación web web.xml
es facesServlet
:
@WebFilter(servletNames={"facesServlet"})
O, para que se ejecute solo en un patrón de URL específico, como el que coincide con las páginas restringidas, p. Ej. /app/*
, /private/*
, /secured/*
, o así, establezca la siguiente anotación en la clase de filtro:
@WebFilter("/app/*")
Incluso podría hacer el mismo trabajo en un filtro que verifica al usuario que inició sesión, si ya tiene uno.
Si usa la biblioteca de utilidades JSF OmniFaces, entonces también puede simplemente tomar su CacheControlFilter
. Esto también tiene en cuenta de forma transparente los recursos JSF.
Ver también:
- Evitar que el usuario vea una página segura visitada anteriormente después de cerrar la sesión
- La redirección de autorización al vencimiento de la sesión no funciona al enviar un formulario JSF, la página permanece igual
- ¿Es seguro el botón de retroceso de JSF 2.0 View Scope?
También encontré otra buena solución.
En faces-config.xml agregar
<lifecycle>
<phase-listener id="nocache">client.security.CacheControlPhaseListener</phase-listener>
</lifecycle>
E implementar la siguiente clase:
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");
}
}