In the last example we saw how to forward to login page on session expiration. The problem with that approach is that the session might get expired on server while user is still actively working on the client. The client will only know about session timeout on making next server request.
Following example shows how to keep extending session timeout while user is still active on the client. We are going to use JQuery on the client to monitor user activity and to send Ajax requests to the server to extend the session. If user becomes idle for a certain time period (say 2 mins) then send logout request to the server and redirect to login page.
Example
Servlets
@WebServlet(name = "securedServlet", urlPatterns = {"/"})
public class MySecuredServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
Principal principal = req.getUserPrincipal();
if (principal == null || !req.isUserInRole("employee")) {
LoginHandlerServlet.forwardToLogin(req, resp, null);
return;
}
req.setAttribute("user", req.getRemoteUser());
req.getRequestDispatcher("/employee-page.jsp")
.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet(name = "loginServlet", urlPatterns = {"/loginHandler"})
public class LoginHandlerServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String theUser = req.getParameter("theUser");
String thePassword = req.getParameter("thePassword");
try {
req.login(theUser, thePassword);
} catch (ServletException e) {
System.out.println(e.getMessage());
forwardToLogin(req, resp, "Error: " + e.getMessage());
return;
}
boolean loggedIn = req.getUserPrincipal() != null && req.isUserInRole("employee");
if (loggedIn) {
resp.sendRedirect("/");
} else {
forwardToLogin(req, resp, "Login failed.");
}
}
public static void forwardToLogin(HttpServletRequest req, HttpServletResponse resp,
String errorMessage)
throws ServletException, IOException {
req.setAttribute("errorMsg", errorMessage);
req.getRequestDispatcher("/login.jsp")
.forward(req, resp);
}
}
@WebServlet(name = "logoutServlet", urlPatterns = {"/logout"})
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("logging out");
req.logout();
Principal principal = req.getUserPrincipal();
if (principal != null) {
throw new RuntimeException("Cannot log out the user");
}
HttpSession session = req.getSession(false);
if (session != null) {
//invalidate session specially if we have added some user specific info
session.invalidate();
}
Object msg = req.getParameter("msg");
if ("onTimeOut".equals(msg)) {
LoginHandlerServlet.forwardToLogin(req, resp, "Session timeout.");
return;
}
resp.sendRedirect("/");
}
}
@WebServlet(name = "refreshServlet", urlPatterns = {"/refreshSession"})
public class RefreshSessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp) {
System.out.println("refreshing session");
if(!req.isUserInRole("employee")){
System.err.println("User logged out already, "
+ "set higher setMaxInactiveInterval() ");
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
HttpSession session = req.getSession();
if (session == null) {
System.err.println("cannot renew session");
}
}
}
Implementing HttpSessionListener
We are implementing HttpSessionListener to set a custom session timeout.
@WebListener
public class MySessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("-- HttpSessionListener#sessionCreated invoked --");
HttpSession session = se.getSession();
System.out.println("session id: " + session.getId());
session.setMaxInactiveInterval(120);//in seconds
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("-- HttpSessionListener#sessionDestroyed invoked --");
}
}
JSP pages
src/main/webapp/login.jsp<html>
<body>
<div style="color:red">
<%
Object msg = request.getAttribute("errorMsg");
if(msg != null ){
out.println(msg);
}
%>
</div>
<form action="/loginHandler" method="post">
<table>
<tr>
<td> User:</td>
<td><input type="text" name="theUser"/></td>
</tr>
<tr>
<td> Password:</td>
<td><input type="password" name="thePassword"/></td>
</tr>
</table>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
src/main/webapp/employee-page.jsp<html>
<body>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
var lastActiveTime = new Date().getTime();
$(document).ready(function() {
$('body').bind('click mousemove keypress scroll resize', function() {
lastActiveTime = new Date().getTime();
});
setInterval(checkIdleTime, 30000); // 30 sec
});
function checkIdleTime(){
var diff = new Date().getTime() - lastActiveTime;
if( diff > 120000){//2 min of inactivity
window.location.href ="/logout?msg=onTimeOut"
}else{
$.ajax({url: '/refreshSession', error: function(data, status, xhr){
alert("cannot refresh session on server: "+xhr);
window.location.reload();}
});
}
}
</script>
</head>
<h2>Employee page</h2>
<div>User: <%= request.getAttribute("user")%> </div>
<br/>
<a href='/logout'>Logout</a>
</body>
</html>
Adding tomcat-users.xml
Since we are using embedded tomcat for our demo, we are going to add tomcat-users.xml with authentication info:
src/main/webapp/config/tomcat-users.xml<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users>
<role rolename="employee"/>
<user username="joe" password="123" roles="employee"/>
</tomcat-users>
Specifying tomcat-user.xml location in pom.xml:
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
<tomcatUsers>src/main/webapp/config/tomcat-users.xml</tomcatUsers>
</configuration>
</plugin>
Running
To try examples, run embedded tomcat (configured in pom.xml of example project below):
mvn tomcat7:run-war
Output
Accessing http://localhost:8080/ and entering valid user name and password:
On submitting the form:
Session will be extending on the server side at 30 seconds rate given that user is active on the page. If user does not do anything (i.e. use mouse, key press, scrolling etc) for 2 mins then user will be logged out automatically and the page will be redirected to login page:
Example ProjectDependencies and Technologies Used: - javax.servlet-api 3.1.0 Java Servlet API
- JDK 10
- Maven 3.5.4
|