3

I am handling session expiration in JSF 2.0 using filter . Here is the code

  @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws IOException, ServletException {

    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;

    HttpSession session = httpServletRequest.getSession(false);

    if (session == null) {

        //session timeout check.
        if (httpServletRequest.getRequestedSessionId() != null && !httpServletRequest.isRequestedSessionIdValid()) {

            System.out.println("Session has expired");
            session = httpServletRequest.getSession(true);
            session.setAttribute("logedin", "0");    // public user               
            httpServletResponse.sendRedirect(timeoutPage);

        } else {

            session = httpServletRequest.getSession(true);
            session.setAttribute("logedin", "0");
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    } 
} //end of doFilter()

But the problem is, when session expires and if user click on the back button, then he gets the page with all styling out. Is there is anyway that when session expires, and if user click the browser back button, then he directs to the timeoutPage.

One thing more, that i am also using Prime Faces component on my page, like datatable. I am using pagination. If session time out, and i click on pagination then the session expiration message do not appear. It seems that ajax request don't call filter? How can i connect my ajax events, or you can say datatable pagination events to session expiration?

Thanks

Basit
  • 8,426
  • 46
  • 116
  • 196

1 Answers1

5

when session expires and if user click on the back button, then he gets the page with all styling out

You need to tell the browser to not cache the pages in browser cache. The browser shoud instead be sending a full request to the server.

Add the following lines right before filterChain.doFilter() call.

if (!httpServletRequest.getRequestURI().startsWith(httpServletRequest.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
    httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
    httpServletResponse.setHeader("Pragma", "no-cache"); // HTTP 1.0.
    httpServletResponse.setDateHeader("Expires", 0); // Proxies.
}

If session time out, and i click on pagination then the session expiration message do not appear. It seems that ajax request don't call filter?

JSF ajax requests expect XML responses with HTTP status 200. If you send a synchronous redirect, then a HTTP status 302 response will be sent which will be completely ignored by JSF ajax engine. You should instead be sending a normal HTTP 200 response with a specific piece of XML which tells the JSF ajax engine to perform a redirect. Do this instead of httpServletResponse.sendRedirect() then:

if ("partial/ajax".equals(httpServletRequest.getHeader("Faces-Request"))) {
    httpServletResponse.setContentType("text/xml");
    httpServletResponse.getWriter()
        .append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
        .printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>", timeoutPage);
}
else {
    httpServletResponse.sendRedirect(timeoutPage);
}

Note that when you're already inside JSF context (e.g. by PhaseListener or SystemEventListener or maybe a @ManagedBean), then you could just use ExternalContext#redirect() method. It will transparently handle synchronous/asynchronous requests accordingly.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • HHmm so when we set `..httpServletResponse.setHeader("Pragma", "no-cache");..`It means that now if we press browser back button, then our constructor will be call. And if we don't set `no-cache`, then when we press the browser back button, then our constructor do not call, instead browser uses its cache to show the page. Am i right? – Basit Mar 19 '12 at 14:21
  • That's correct if the bean is request/view scoped. The browser is instead sending a fullworthy HTTP request to the server (where you can perform all necessary checks in the filter) instead of displaying the old page from the browser cache. – BalusC Mar 19 '12 at 14:22
  • One thing more, as you defined the ajax part. You mean to say when i was clicking on the pagination, the the filter is invoking on session expiration but it is neglecting the redirect request because of HTTP status 302 response. Is it? – Basit Mar 19 '12 at 14:23
  • No, the filter is not neglecting the redirect request. The JSF ajax engine is doing that becauses it expects and supports specific XML responses with a status of 200 only. Even exceptions (status 500) are by default ignored. See further also this related question http://stackoverflow.com/questions/9305144/using-jsf-2-0-facelets-is-there-a-way-to-attach-a-global-listener-to-all-ajax – BalusC Mar 19 '12 at 14:24
  • hhmm means filter is invoking, generating the redirect request, but the JSF Ajax engine is neglecting the redirect request because of the 302 response. And your code means, that when we generate any ajax event, then the partial/ajax request header sets. So you are checking that if session has expired and user generate the ajax request, then do the things in the way that JSF ajax engine can understand for redirect, otherwise do the normal redirect. Am i right? – Basit Mar 19 '12 at 14:31
  • Awsome. Thanks. You are a JSF hero :). One thing more. It is out of topic but as you said when we set no-cache, then on browser back button click, full request generate and our constructor invoke. The same is also true for browser refresh? Like if we set no-cache and refresh the page, then the page loads from scratch and the constructor will invoke? Actually i just face a issue, i have no `no-cache` set, but when i refresh the page then instead of calling the constructor, my method is getting call... – Basit Mar 19 '12 at 14:42
  • That's correct. This is also what you want for dynamic pages, right? – BalusC Mar 19 '12 at 14:44
  • No it is not working. I set `no-cache`. But now when i refresh the page, It shows message that **To display this page, Firefox must send information that will repeat any action (such as a search or order confirmation) that was performed earlier.** Earlier i performed a request in which i clicked on button, and on the basis of button click i get message dialog. This is not what i want. I want that it didn't repeat any earlier action. I want to ask whenever we do page refresh, then always our previous action occurs? – Basit Mar 19 '12 at 14:59
  • That will happen when the previous request is performed by a POST instead of GET. That's exactly why the POST-Redirect-GET pattern exists and also why simple page-to-page navigation by POST is a bad idea. – BalusC Mar 19 '12 at 15:00
  • Hhmm when we click on buttons that are inside JSF form, then the POST request generate by default. That's why my last action is calling, when i refresh the page. If this is GET request, then the last action didn't call, instead everything from the start get call. Am i right? – Basit Mar 19 '12 at 15:12
  • 1
    In a properly designed JSF web app, the action methods should all return `void` or `null`. Page-to-page navigation needs to be performed by GET with `` or ``. See also http://stackoverflow.com/questions/4317684/when-should-i-use-houtputlink-instead-of-hcommandlink – BalusC Mar 19 '12 at 15:21
  • So the reason of calling the last action is POST. I am using button. h:commandButton. So it generates onclick script which submits a (hidden) POST form and can invoke a managed bean action method. It means it is ok if i am getting the message dialog. It is simply my last action that is calling. OK fine. But is there any way to bypass the last action in the same situation, that is in case of POST? – Basit Mar 19 '12 at 15:54