Issue
I have created a default form-login authentication and below is my configuration.
<!-- Empty filter chain for the login page -->
<http pattern="/rest/login" security="none" />
<http pattern="/install/license/**" security="none" />
<http pattern="/resources/**" security="none" />
<http auto-config="true" use-expressions="true">
<request-cache ref="authenticationRequestCache" />
<access-denied-handler error-page="/rest/login?error=denied" />
<form-login login-page="/rest/login"
authentication-success-handler-ref="successHandler"
authentication-failure-url="/rest/login?error" />
<intercept-url pattern="/rest/devices" access="denyAll" />
<intercept-url pattern="/rest/devices/**" access="denyAll" />
<intercept-url pattern="/rest/super/**" access="hasRole('ROLE_SUPER')" />
<intercept-url pattern="/rest/**" access="isAuthenticated()" />
<intercept-url pattern="/cavirinRest/*" />
<session-management invalid-session-url="/rest/login?error=sessionExpired"
session-authentication-strategy-ref="sas" />
<logout invalidate-session="true" logout-success-url="/rest/login" delete-cookies="JSESSIONID"/>
</http>
Authentication works fine as expected, but recently i added session-timeout to the application. My application polls server for every 5 seconds, hence the default time out does not work. I googled and found below solution with filter and implemented it.
from web.xml:
<filter>
<filter-name>XhrSessionTimeoutFilter</filter-name>
<filter-class>com.cavirin.security.filter.XhrSessionTimeoutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XhrSessionTimeoutFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
<session-config>
<!-- Disables URL-based sessions (no more 'jsessionid' in the URL using Tomcat) -->
<tracking-mode>COOKIE</tracking-mode>
<session-timeout>2</session-timeout>
</session-config>
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpSession session = request.getSession(false);
if (session != null) {
logger.trace("doFilterInternal(): session object is not null.");
session.setMaxInactiveInterval(120); //2 mins for testing //set max inactive interval to 30 mins
// if requestURI is not null
if (request.getRequestURI() != null) {
logger.trace("doFilterInternal(): request.getRequestURI() : {} ", request.getRequestURI());
String ajaxHeader = request.getHeader("X-Requested-With");
logger.trace("doFilterInternal(): ajaxHeader : {} ", ajaxHeader);
//if it is an AJAX call
if ("XMLHttpRequest".equals(ajaxHeader)) {
logger.trace("doFilterInternal(): An AJAX call, set the last access time, if not already set.");
Long lastAccess = (Long) session.getAttribute(AJAX_DATA_LAST_ACCESS_TIME);
if (lastAccess == null) {
logger.trace("doFilterInternal(): Last access time is null, set current time as lastAccess time.");
lastAccess = System.currentTimeMillis();
session.setAttribute(AJAX_DATA_LAST_ACCESS_TIME, lastAccess);
} else {
logger.trace("doFilterInternal(): max interval: {} -- lastAccess: {} -- currentTimeMillis: {} ",
+ session.getMaxInactiveInterval(), lastAccess, System.currentTimeMillis());
if (((session.getMaxInactiveInterval() * 1000) - (System.currentTimeMillis() - lastAccess)) < 0) {
logger.debug("doFilterInternal(): session should be invalidated as inative time execeeded.");
session.invalidate();
}
}
} else {
logger.trace("doFilterInternal(): Not an AJAX call.");
session.removeAttribute(AJAX_DATA_LAST_ACCESS_TIME);
}
}
}
filterChain.doFilter(request, response);
}
Timeout also works fine, but once time out occurs, my ajax request (i see it on chrome - developer tools, network tab) gets 302 status as it is redirected to another page. i.e my log-in page with session timeout error message. I don't click on anything and I leave it for another 2mins (i set 2mins timeout for testing), again I see a log message saying that "session expired" and redirected to log-in. I could see there a new jsessionid is created after session expiration
RequestMapping(method = RequestMethod.GET)
public ModelAndView showLogin(Model model,
@RequestParam(value = "error", required = false) String errorStr,
HttpServletRequest request) {
logger.trace("Returning login page view.");
String pageToBeLoaded = "rest/login" ;
System.out.println("showLogin(): errorStr: " + errorStr);
if (errorStr != null) {
if (errorStr.equalsIgnoreCase("sessionExpired")) {
logger.debug("showLogin(): error: sessionExpired ");
System.out.println("showLogin(): error: sessionExpired");
model.addAttribute("error", "Session expired. Please log in again.");
} else if (errorStr.equalsIgnoreCase("denied")) {
logger.debug("showLogin(): error: denied ");
System.out.println("showLogin(): error: denied");
model.addAttribute("error", "Access is denined. This page is for SUPER user only.");
} else {
model.addAttribute("error", getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));
System.out.println("showLogin(): error: " + getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));
}
}
return new ModelAndView(pageToBeLoaded);
}
I know that I did not stop polling server after timeout, so it polls again and again, But 1) how the session is created without logging-in to the application?
Also, 2) I'm not getting session timeout error message, as it is redirected to this page once and again it goes to log-in page. I'm not able to hold this error message until user is logging in again.
Any pointers ?
Solution
After much reading about spring security and its related stuff, came to know that 1) session is created by default (http config, if-required) and once session timed-out, it will automatically redirect to a given url on session-management of invalid-session-url and creates another session. I observed this behavior in the spring-security debug log, which states that, session invalidated, creating another session if required. 2) Once the timed-out session is invalidated, it redirects to invalid-session-url; but my ajax keeps polling from the same page, the another session is created and looking for authentication to access my secure web page, hence redirection happens to my log-in page, but this time no session time out error.
Answered By - Coder