In modern web applications, transitioning to AJAX for data interaction often exposes a flaw in session management: when a user's session expires, the server might return a HTML login page or a redirect script. Because AJAX requests are handled by the XMLHttpRequest object rather than the browser's navigation engine, this HTML response is simply treated as data, failing to trigger a browser-level redirect.
Identifying AJAX Requests
To solve this, we must distinguish between standard navigation and AJAX calls. Most JavaScript libraries (like jQuery) include a specific HTTP header in AJAX requests: X-Requested-With: XMLHttpRequest. By checking for this header on the server, we can decide whether to send a standard redirect or a structured signal that the client-side JavaScript can interpret.
Server-Side Interceptor Logic
In a Struts2 or similar framework, you can implement an interceptor to verify session integrity. If the session is invalid, the interceptor checks the X-Requested-With header.
public String intercept(ActionInvocation invocation) throws Exception {
HttpServletRequest request = (HttpServletRequest) invocation.getInvocationContext().get(StrutsStatics.HTTP_REQUEST);
HttpServletResponse response = (HttpServletResponse) invocation.getInvocationContext().get(StrutsStatics.HTTP_RESPONSE);
if (isSessionValid(request)) {
return invocation.invoke();
}
String requestedWith = request.getHeader("X-Requested-With");
if ("XMLHttpRequest".equalsIgnoreCase(requestedWith)) {
// Send a custom header and status code for the client to intercept
response.setHeader("session-status", "expired");
response.sendError(401, "Session has expired.");
return null;
} else {
// Standard redirect for non-AJAX requests
response.sendRedirect(request.getContextPath() + "/login.do");
return null;
}
}
Client-Side Handling
Once the server notifies the client of the expiratino, the frontend must handle the redirection. Using jQuery's global ajaxSetup, we can intercept all outgoing AJAX calls to check for the custom header returned by the server.
$.ajaxSetup({
complete: function(xhr) {
// Check for the custom header set by our server interceptor
if (xhr.getResponseHeader('session-status') === 'expired') {
alert('Your session has expired. Please sign in again.');
// Navigate the top-most window to the login page
var topWindow = window;
while (topWindow !== topWindow.parent) {
topWindow = topWindow.parent;
}
topWindow.location.href = '/login.html';
}
}
});
This approach ensures a consistent user experience regardless of whether the user interacts with standard links or asynchronous components. By centralizing the logic in an interceptor and a global AJAX configuration, you avoid repeating session validation code across multiple modules.