Persistent authentication filter logging in; not propagating to JSP

210 views Asked by At

I've been working on a "Remember Me?"-style persistent authentication system, but after successfully authenticating and logging in the user, my JSPs do not reflect that the user is logged in until the next page request or refresh is processed. The cookie used for this is being written, and the database is being updated. Checking the login state of the user object within the Filter method indicates that the user object is being properly loaded. After refreshing or navigating to a different page, the page shows that the user is logged in.

Why does the successful login not show up on the first page?

Is there something obvious that I'm not doing here, that would prevent changes to the session data from within a Filter from being propagated to the JSP? Is this a limitation that needs to be overcome by a forced refresh?

This is written for a Java EE 5 and Struts 1.x site rendering to JSPs, using a custom Java EE Filter class as the entry point for automatic login. The filter is installed first in the chain, and mapped to "/*" in my web.xml.

My filter method implementation looks like this:

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
    if (!isPersistentAuthEnabled()) {
        chain.doFilter(req, resp);
        return;
    }
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) resp;
    HttpSession session = request.getSession(false);

    SessionStore store = null;
    if (session != null) {
        store = (SessionStore)session.getAttribute("sessionStore");
    }

    User user = null;
    if (store != null) {
        user = store.getUser();
    }

    if (user == null || user.isLoggedIn()) {
        // Skip authentication if no user object in session, or if already logged in.
        chain.doFilter(req, resp);
        return;
    }

    PersistentAuthenticationManager manager = null;
    try {
        manager = new PersistentAuthenticationManager();
    } catch (Exception e) {
        LOG.warn("skipping");
        chain.doFilter(req, resp);
        return;
    }

    try {
        manager.authenticateUser(user, request, response); // Authenticates based on cookie and DB store
        if (user.isLoggedIn()) {
            LOG.debug("success"); // this is being logged correctly
        }
    } catch (Exception e) {
        LOG.warn("failure");
    }
    chain.doFilter(req, resp);
}

The relevant code in the JSP is as follows (mapping the taglib jstl-core.tld to the c prefix):

<c:choose>
    <c:when test="${sessionStore.user.loggedIn}">
        <!-- Not shown: Display welcome message -->
    </c:when>
    <c:otherwise>
        <!-- Not shown: Click here to log in -->
    </c:otherwise>
</c:choose>

I've tested this on Chrome and IE so far, and the same thing happens on both. My idea for a fix would be to force a refresh by redirecting the user to the same page, but I fear that might have some ill effects, such as if that were to hit the Struts action handlers for certain requests (ie. posting things) before submitting a redirect, among other doubts. Is there some other way to handle this scenario?

EDIT: Since I believe I can safely assume users won't be submitting any forms or processing delicate actions on their first access of the site, I'm going to go with an automatic refresh via AJAX verification from the JSP which sets up the login prompt. I've discovered that the filter in question doesn't run when a plain JSP (such as the index page) is requested; it only appears to run when a Struts action is invoked. I was confused because an AJAX action on the homepage of this particular site was authenticating the user, and NOT the index.jsp request; I determined this by logging the URL of the request from within the filter. I'll describe the process in an answer.

1

There are 1 answers

0
Shotgun Ninja On BEST ANSWER

As it turns out, the filter that I've set up for persistent authentication appears to only run when a Struts action is invoked; as such, it wasn't running on the index page of my site (among other pages), but in my case was instead authenticating when an AJAX call to a Struts action from the index page was processed. I've tested this for other non-Struts pages, and it appears that none of them pass through the filter I've implemented.

The way I've solved this was by placing code into my login check JSP which invokes a Struts action via AJAX (thus triggering the filter in the background); the action returns JSON which indicates whether or not the user is logged in. If logged in, the AJAX response handler then shows the user an overlaid notification for a brief time, then refreshes the page, showing the correct contents as if the user were logged in.

The relevant portion of the JSP which displays the login prompt (updated):

<c:choose>
    <c:when test="${sessionStore.user.loggedIn}">
        <!-- Not shown: Display welcome message -->
    </c:when>
    <c:otherwise>
        <script src="/path/to/persistentAuth.js"></script>
        <script type="text/javascript">
            checkPersistentAuthAndRefresh();
        </script>
        <!-- Not shown: Click here to log in -->
    </c:otherwise>
</c:choose>

Inside persistentAuth.js:

function checkPersistentAuthAndRefresh() {
    var refreshPage = function () {
        window.location.reload(true);
    };
    $.ajax({
        url: "http://example.com/path/checkPersistentAuth.do",
        dataType: "json",
        success: function (data) {
            if (data.isLoggedIn) {
                showArbitraryDropDownMessage();
                setTimeout(refreshPage, 100);
            }
        }
    });
}