Setting up HTTP auth protecting a Tomcat servlet

In my last blog post I covered a custom Java servlet running on Tomcat that gives statistics on current active users using a Java application running on the Tomcat server.

You may realize that this servlet, or any Tomcat servlet/application is world-readable, and you may want to protect it with a password.

There are several ways you can password protect it. For example, you could simply create an input form where a password is entered, and only then display the information. Or add an authentication string to the URL, or add custom HTTP headers which the servlet reads.

This blog post covers the simple old school HTTP authentication, where the browser pops up a username/password dialog box to the user.

You can adopt the code below in your Tomcat servlet class:

public class MyClass extends HttpServlet {
 private HashMap < String, String > validUsers = new HashMap < String, String > ();

 public void init(final ServletConfig config) throws ServletException {
  validUsers.put("myuser:mypassword", "authorized");
 }

 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  String auth = request.getHeader("Authorization");
  if (!authenticate(auth)) {
   response.setHeader("WWW-Authenticate", "BASIC realm=\"My Website Auth\"");
   response.sendError(HttpServletResponse.SC_UNAUTHORIZED);

  } else {
   //User authenticated, allow them in and serve protected content here
  }
 }

 private boolean authenticate(String auth) {
  if (auth == null)
   return false;

  if (!auth.toUpperCase().startsWith("BASIC "))
   return false;

  String userpassEncoded = auth.substring(6);
  String userpassDecoded = new String(DatatypeConverter.parseBase64Binary(userpassEncoded));

  if ("authorized".equals(validUsers.get(userpassDecoded)))
   return true;
  else
   return false;
 }

}

As you can see, the servlet code first looks for an “Authorization” HTTP header, if not present it sends a standard response code, which causes the browser to prompt the user. The username/password are then transmitted to the servlet, which supports the standard BASIC authentication, with the username/password base64 encoded. The servlet then matches the username/password to a HashMap of configured users we’ve defined in the init() method.

Pretty simple!

Creating a servlet to get the active number of sessions in Tomcat over the web

Last two blog posts I explored how to get the number of active sessions either directly through Tomcat (https://adeelscorner.wordpress.com/2016/03/13/getting-the-number-of-active-sessions-in-tomcat/), or through the application (particularly in Vaadin, https://adeelscorner.wordpress.com/2016/03/20/getting-the-number-of-active-sessions-in-vaadin/).

This blog post will explore putting both together and getting the session data through a servlet mapped to a URL. This way an admin can get the stats for a particular server right through a browser.

Let’s start with defining a servlet in web.xml:

  <servlet>
    <servlet-name>SessionStats</servlet-name>
    <servlet-class>com.perthera.session.SessionStats</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>SessionStats</servlet-name>
    <url-pattern>/SessionStats</url-pattern>
  </servlet-mapping>

Now you have a servlet called SessionStats defined to the /SessionStats on your Tomcat server, which will map to the com.company.session.SessionStats class.

In the SessionStats we’ll query the Tomcat level sessions, and the sessions the application is tracking, and return them as easy to read HTML:

public class SessionStats extends HttpServlet {
 private String message;

 public void init(final ServletConfig config) throws ServletException {}

 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  getSessionStats();

  response.setContentType("text/html");
  PrintWriter out = response.getWriter();
  out.println(message);
 }

 private void getSessionStats() throws Exception {
  message = "";

  final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
  ObjectName objectName = new ObjectName("Catalina:type=Manager,context=/,host=localhost");

  int activeSessions = (Integer) mBeanServer.getAttribute(objectName, "activeSessions");

  message += "Active Sessions Reported By Tomcat: " + activeSessions + ".
";
  message += "
";
  message += "Active/Idle User Sessions Reported By Application: " + ApplicationLevelTracker.browserTabUniqueIDToIdleTimeHashMap.size() + ".
";
  message += "
";

  Set keySet = ApplicationLevelTracker.browserTabUniqueIDToIdleTimeHashMap.keySet();
  for (String key: keySet) {
   Pair < Boolean, Long > pair = ApplicationLevelTracker.browserTabUniqueIDToIdleTimeHashMap.get(key);
   String userId = key.split(":")[0];
   String activeOrIdle = (pair.getElement0() ? "idle" : "active");
   Date timeStamp = new Date(pair.getElement1());
   message += "User ID " + userId + " is logged in with a tab open that was last reported as " + activeOrIdle + " at " + timeStamp + "
";
  }
  message += "
";
 }
}

So as you can see, the Tomcat session stats are queried, and displayed. And then the ApplicationLevelTracker is used to loop through a HashMap of users logged in and their idle/active as-of timestamp is displayed. This information can be useful to a system administrator in many ways, such as when determining if a server is in use and should not be taken offline. And it can be accessed through a browser by visiting: http://hostname/SessionStats

Correctly doing post-login processing in the DaftMonk AngularJS full-stack seed

So I’ve been working on a personal project using the MEAN stack (MongoDB, ExpressJS, AngularJS and NodeJS)! The nerd in me has been pretty excited to learn something completely new and different. I’ve never done full-stack development strictly in JavaScript, and so far I’m finding it to be pretty neat. I’ll be making posts about things I struggle with, or find useful, as I progress.

Anyway, I used the DaftMonk AngularJS full-stack yeoman generator (https://github.com/DaftMonk/generator-angular-fullstack) for my project seed. I definitely recommend it if you’re starting out fresh with MEAN (or even if you’re not), since it speeds up the initial setup and development immensely.

My problem arose as I was wiring up some post-processing to be done after a user logged in. In the login controller (login.controller.js), after the login function is called from the Auth service, there is a function in “.then()” that is called upon success.

Auth.login({
  email: $scope.user.email,
  password: $scope.user.password
})
.then( function() {
    //login was successful
    //...
  }

However, even though the user has logged in, the user data is not available yet! So, you cannot access the current user’s data properties (such as their role) through Auth.getCurrentUser() (for example the Auth.getCurrentUser().role field will be undefined). This is due to the fact that the data is loaded asynchronously (I’m still trying to get the hang of how that works).

The way around this is to use the Auth.isLoggedInAsync(…callback…) function. The callback sent to isLoggedInAsync is called after the user has finally been loaded, which will guarantee your post-login-processing code being executed after the data for the user is available.

This is how you’d make use of Auth.isLoggedInAsync(…):

Auth.login({
  email: $scope.user.email,
  password: $scope.user.password
})
.then( function() {
    Auth.isLoggedInAsync(function(success) {
      //Auth.getCurrentUser() will now return the user with all the properties fully loaded
    }

Enjoy!