By Danijel


2013-01-29 10:05:01 8 Comments

I am using JSF2. I have implemented a custom faces servlet like so:

public class MyFacesServletWrapper extends MyFacesServlet {
    // ...
}

wherein I'm doing some authorization checks and sending a redirect when the user is not logged in:

public void service(ServletRequest request, ServletResponse response) {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse res = (HttpServletResponse) response;

    if (...) {
        String loginURL = req.getContextPath() + "/LoginPage.faces";
        res.sendRedirect(loginURL);
    }
}

This works when the user tries to navigate to another page. However, this does not work when a JSF form is submitted by a JSF command link/button. The line sendRedirect() line is hit and executed, no exception is been thrown, but the user stays at the same page. Basically, there's no visual change at all.

Why does this work on page navigation, but not on form submit?

2 comments

@BalusC 2013-01-29 11:39:07

Your concrete problem is most likely caused because your JSF command link/button is actually sending an ajax request which in turn expects a special XML response. If you're sending a redirect as response to an ajax request, then it would just re-send the ajax request to that URL. This in turn fails without feedback because the redirect URL returns a whole HTML page instead of a special XML response. You should actually be returning a special XML response wherein the JSF ajax engine is been instructed to change the current window.location.

But you've actually bigger problems: using the wrong tool for the job. You should use a servlet filter for the job, not a homegrown servlet and for sure not one which supplants the FacesServlet who is the responsible for all the JSF works.

Assuming that you're performing the login in a request/view scoped JSF backing bean as follows (if you're using container managed authentication, see also 2nd example of Performing user authentication in Java EE / JSF using j_security_check):

externalContext.getSessionMap().put("user", user);

Then this kickoff example of a filter should do:

@WebFilter("/*") // Or @WebFilter(servletNames={"facesServlet"})
public class AuthorizationFilter implements Filter {

    private static final String AJAX_REDIRECT_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        + "<partial-response><redirect url=\"%s\"></redirect></partial-response>";

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {    
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        HttpSession session = request.getSession(false);
        String loginURL = request.getContextPath() + "/login.xhtml";

        boolean loggedIn = (session != null) && (session.getAttribute("user") != null);
        boolean loginRequest = request.getRequestURI().equals(loginURL);
        boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER + "/");
        boolean ajaxRequest = "partial/ajax".equals(request.getHeader("Faces-Request"));

        if (loggedIn || loginRequest || resourceRequest)) {
            if (!resourceRequest) { // Prevent browser from caching restricted resources. See also https://stackoverflow.com/q/4194207/157882
                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(request, response); // So, just continue request.
        }
        else if (ajaxRequest) {
            response.setContentType("text/xml");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().printf(AJAX_REDIRECT_XML, loginURL); // So, return special XML response instructing JSF ajax to send a redirect.
        }
        else {
            response.sendRedirect(loginURL); // So, just perform standard synchronous redirect.
        }
    }

    // ...
}

See also:

@Danijel 2013-01-29 14:18:07

OK, I've implemented it via Filter. However, I still don't understand the else if("partial/ajax"...) part. Could you explain in more detail or provide some links?

@BalusC 2013-01-29 14:20:11

A JSF ajax request requires a special XML response. A HTTP 302 response is not interpreted as a window redirect by JSF ajax engine, but as a redirect to a new XML response. However if that redirect actually returns a HTML page, it fails with "no feedback". You need to return a special XML response which tells JSF ajax engine to send a redirect on the given URL. You can recognize JSF ajax requests by checking the mentioned response header value. In JSF side, this is normally "under the covers" handled by ExternalContext#redirect(). But outside JSF context, you have to deal with it manually.

@Danijel 2013-03-20 10:16:25

Should this line request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER); handle css files? Seams like it doesn't.

@BalusC 2013-03-20 10:41:20

Only if they're referenced via <h:outputStylesheet>.

@Tarun Gupta 2013-01-29 10:11:45

FacesContext.getCurrentInstance().getExternalContext().redirect("newpage.xhtml"); try this.... in place of res.sendredirect(cpath).

@Danijel 2013-01-29 11:06:34

Thanks. It does not work: FacesContext.getCurrentInstance() returns null. What to do?

@BalusC 2013-01-29 11:34:19

Surely it doesn't work. The FacesContext is only available in a FacesServlet, not a homegrown servlet.

Related Questions

Sponsored Content

7 Answered Questions

1 Answered Questions

[SOLVED] How to deal with JSF messages between page navigations (POST-REDIRECT-GET)

  • 2013-01-09 17:12:20
  • Xtreme Biker
  • 3904 View
  • 7 Score
  • 1 Answer
  • Tags:   jsf-2

1 Answered Questions

[SOLVED] How to get pathInfo with SecurityWrapperRequest?

  • 2014-10-08 06:20:51
  • prashanth
  • 449 View
  • 0 Score
  • 1 Answer
  • Tags:   java esapi

4 Answered Questions

[SOLVED] Can a JSF Project run without using Servlets?

3 Answered Questions

problem using UserService of google appengine

2 Answered Questions

[SOLVED] does process still continue after client disconnects

Sponsored Content