Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Das Problem: Es wird gar keine Exception geworfen wenn ich z. B. versuche nach einem SessionTimeout einen bereiets geladenen DataTable weiter zu blättern. Folglich kann der Exceptionhandler auch keinen Redirect machen.
Geprüft habe ich das mit diversen Sysouts - Der Handler wird aufgerufen aber der ExceptionStack ist leer .hasNext() = false.
auf anhieb finde ich jetzt nichts außer das der Eintrag fehlt wohin deine App navigieren soll bei dem Kommando 'viewExpired'. Die Frage ist wird die Session jemals erstellt? Ich denke du hast testweise den session timeout auf 1 gestellt. Welche Sicherheitsimpl. verwendest du denn? JAAS? Vielleicht fängt der Filter die Exception vorher ab.
+ du mappst das FacesServlet auf *.xhtml -> besser wäre *.jsf oder /faces/*.
Testweise kannst du noch eine Session listener rauf setzen um zu sehen wann die session genau abläuft.
Java:
@WebListener
public class SessionContextListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent arg0) {
System.out.println("Web-Session created");
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
System.out.println("Web-Session destroyed");
}
}
Haha, genau das hab ich jetzt auch probiert bevor ich hier rein geschaut habe ,-)
Funktioniert aber leider nicht (ganz).
Die Lösung des Problems bestand erstmal darin, dass der Tomcat gar keine ViewExpiredException werfen kann, da es sich um Ajax-Requests aus Primefaces handelt. In meinem Szenario war die Lösung die Folgende:
Alles was nicht Ajax ist, kann ich getrost aussen vor lassen, da sich die User bei mir anmelden müssen.
Inhalte von Seiten die loggedout aufgerufen werden können sind egal. In den gesicherten Bereichen wird man bei einem HttpRequest sowieso wieder zur Login-Seite geleitet wenn die Session abgelaufen ist.
Das Problem waren nur die Ajax-Requests. Da bleibt Primefaces dann einfach stehen.
Ich habe probiert @WebListener und @WebFilter. Bei ersterem ist das redirecten sehr schwer, letzteres hört auf zu funktionieren wenn die Session abgelaufen ist.
Die Lösung war ein PhaseListener mit entsprechendem Eintrag in der faces-config-xml.
Zuerst wird überprüft ob es sich um einen Ajax-Request handelt und dann ob die Session noch valid ist.
Falls nicht wird über FacesContext.getCurr[...]redirect("/index.jsf") umgeleitet. Und das funktioniert jetzt wunderbar.
Dadurch kommt auch auf den Seiten auf denen man nicht eingeloggt ist nur ein Redirect zu stande, wenn ein Ajax-Request ausgeführt wird.
Die Lösung war ein PhaseListener mit entsprechendem Eintrag in der faces-config-xml.
Zuerst wird überprüft ob es sich um einen Ajax-Request handelt und dann ob die Session noch valid ist.
Falls nicht wird über FacesContext.getCurr[...]redirect("/index.jsf") umgeleitet. Und das funktioniert jetzt wunderbar.
Dadurch kommt auch auf den Seiten auf denen man nicht eingeloggt ist nur ein Redirect zu stande, wenn ein Ajax-Request ausgeführt wird.
Kannst du das vielleicht noch etwas genauer ausführen? Das klingt nämlich genau nach dem, was ich momentan suche ... ich weiß nur trotzdem noch nicht so wirklich, wie ich das nun umzusetzen hätte.
public class SessionTimeoutPhaseListener implements PhaseListener {
@Override
public void afterPhase(PhaseEvent pe) {
}
@Override
public void beforePhase(PhaseEvent pe) {
ExternalContext ctx = pe.getFacesContext().getExternalContext();
HttpServletRequest request = (HttpServletRequest) ctx.getRequest();
HttpSession session = request.getSession(false);
if ("partial/ajax".equals(request.getHeader("Faces-Request"))) {
if (!request.isRequestedSessionIdValid() || session == null) {
try {
ctx.redirect(ctx.getRequestContextPath() + "/session-expired.xhtml");
} catch (IOException ex) {
Logger.getLogger(SessionTimeoutPhaseListener.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
@Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
}
Soweit bin ich jetzt - der Phaselistener muss in der faces-config.xml konfiguriert werden.
Allerdings funktioniert der jetzt "nicht mehr" (hatte ich aber schon mal) auf der Startseite. In einem gesicherten Bereich funktioniert er super.
Das Problem ist, dass beim ersten Aufruf der App noch keine Session existiert.
if session == null funktioniert dann leider nicht. isRequestedSessionIdValid funktioniert dann natürlich auch nicht. Hier muss ich noch rausfinden welchen Wert die jeweiligen Vars zu den relevanten Zeitpunkten haben.
Ein Filter hat nicht funktioniert, weil der bei mir nicht mehr funktionierte sobald die Session abgelaufen war. Ein Weblistener mit Annotations hat auch nicht mehr funktioniert sobald die Session weg war.
Falls du das Problem mit dem ersten Aufruf gelöst bekommst wäre ich für einen Tip auch dankbar...
public class SecurityPhaseListener implements PhaseListener, Serializable {
@Override
public void afterPhase(PhaseEvent arg0) {
final FacesContext facescontext = arg0.getFacesContext();
// not exist yet (non-faces-request)
if (facescontext.getViewRoot() == null) {
facescontext.getApplication().getNavigationHandler().handleNavigation(facescontext, null, "index");
// ignore other phases
facescontext.renderResponse();
} else {
// current view identifier
final String viewId = facescontext.getViewRoot().getViewId();
final boolean loginPage = viewId.lastIndexOf("index") > -1;
// JAAS
final boolean login = facescontext.getExternalContext().getRemoteUser() != null;
// or (facescontext.getExternalContext().getSessionMap().get("security-token") != null);
// not login page and user is not login - redirect to login page
if (!loginPage && !login) {
facescontext.getApplication().getNavigationHandler().handleNavigation(facescontext, null, "index");
// ignore other phases
facescontext.renderResponse();
// login page and user is login - redirect to start page
} else if (loginPage && login) {
facescontext.getApplication().getNavigationHandler().handleNavigation(facescontext, null, "start");
// ignore other phases
facescontext.renderResponse();
}
}
// go on
}
/**
* @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
*/
@Override
public void beforePhase(PhaseEvent arg0) {
// nothing to do -> view will be restore or new created
}
/**
* @see javax.faces.event.PhaseListener#getPhaseId()
*/
@Override
public PhaseId getPhaseId() {
// before the view will be created
return PhaseId.RESTORE_VIEW;
}
}
faces-config.xml
[XML]
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=" Java EE : XML Schemas for Java EE Deployment Descriptors
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<lifecycle>
<phase-listener>de.my.package.SecurityPhaseListener</phase-listener>
</lifecycle>
<navigation-rule>
<from-view-id>/*</from-view-id>
<navigation-case>
<from-outcome>index</from-outcome>
<to-view-id>/index.xhtml</to-view-id>
<redirect />
</navigation-case>
<navigation-case>
<from-outcome>start</from-outcome>
<to-view-id>/public/start.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
</faces-config>
Im Prinzip macht dein Code genau das was er soll, ich war auf einem leicht anderen Weg auch schon soweit, aber das Problem ist, dass das nur funktioniert wenn man in einem gesicherten Bereich unterwegs ist.
Wenn ich die ganz normale Startseite aufrufe, und dort z. B. einen DataTable mit Ajax habe funktioniert der auch nur, so lange die Session existiert. Läuft die Session ab, funktioniert auch der DataTable nicht mehr (z. B. Blättern).
Dafür suche ich eine Lösung, habe aber noch keine gefunden.
Eine Möglichkeit wäre natürlich die Beans RequestScoped zu machen, oder? Aber ich hätte halt schon gerne, das z. B. Filtereinstellungen erhalten bleiben solange die Session läuft.
Die User können bei mir die App teilweise schon benutzen ohne sich anmelden zu müssen, müssten sich alle automatisch anmelden würde deine Lösung super funktionieren...