Je montre des données TRÈS sensibles. Une fois que l'utilisateur s'est déconnecté de mon serveur, je ne veux pas qu'un autre utilisateur puisse voir les données sur le bouton Retour du navigateur.
Comment puis-je atteindre cet objectif?
Par défaut, le bouton de retour du navigateur n'envoie aucune requête HTTP au serveur. Au lieu de cela, il récupère la page dans le cache du navigateur. C'est essentiellement inoffensif, mais déroutant pour l'utilisateur final, car il pense à tort que cela vient vraiment du serveur.
Il vous suffit de demander au navigateur de ne pas mettre en cache les pages restreintes. Vous pouvez le faire avec un simple filtre de servlet qui définit les en-têtes de réponse appropriés :
@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);
}
// ...
}
(notez que ce filtre ignore les demandes de ressources JSF, dont la mise en cache doit en fait être configurée séparément )
Pour l'exécuter sur chaque demande JSF, définissez l'annotation suivante sur la classe de filtre, en supposant que la valeur de <servlet-name>
du FacesServlet
dans le web.xml
est facesServlet
:
@WebFilter(servletNames={"facesServlet"})
Ou, pour le faire fonctionner sur un modèle d'URL spécifique uniquement, tel que celui correspondant aux pages restreintes, par exemple /app/*
, /private/*
, /secured/*
, ou ainsi, définissez l'annotation suivante sur la classe de filtre:
@WebFilter("/app/*")
Vous pouvez même faire le même travail dans un filtre qui vérifie l'utilisateur connecté, si vous en avez déjà un.
S'il vous arrive d'utiliser la bibliothèque d'utilitaires JSF OmniFaces , alors vous pouvez également récupérer son CacheControlFilter
. Cela prend également en compte de manière transparente les ressources JSF.
J'ai également trouvé une autre bonne solution.
Dans faces-config.xml, ajoutez
<lifecycle>
<phase-listener id="nocache">client.security.CacheControlPhaseListener</phase-listener>
</lifecycle>
Et implémentez la classe suivante:
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");
}
}