OpenSubsystems

Business Components for Java Applications

Open Core

Tutorial

Documentation
Last modified

$Author: bastafidli $
$Date: 2007/03/11 06:30:45 $
$Revision: 1.13 $
$RCSfile: tutorial_security.html,v $

Securing the web application

The application is almost complete. All the code required to browse, create, modify and delete data is in place. The last remaining item on our list is to secure the application. The requirement is that only the authorized users can modify the data while all the others can still read it.

Security of web applications is critical business requirement, because the application can be easily accessed by anybody who has a network connection to a given server. One of the common security requirements is to perform authentication of the users so that only the approved users can access certain data or execute certain operations. Open Core provides framework to implement authentication easily.

Note: OpenSubsystems provides Open Security, subsystem that addresses all aspects of application security including authentication, authorization, session tracking, user and role management and domain partitioning. By integrating Open Security into your application you can avoid the design and coding effort we will demonstrate in this section of the tutorial and you will achieve much more robust and reliable solution. Since this step of the tutorial focuses on Open Core only, we will demonstrate basic authentication that can be easily achieved just using Open Core.

Our tasks

We will design and implement user interface, that will allow user to login and logout. We will also implement the web tier support for this user interface. At last, we will override and implement the handlers that Open Core provides to authenticate user. We will also decide how to handle situation when the application doesn't allow access to users that are not logged in.

OpenChronicle authentication

The user interface that allows user to login and logout is implemented the same way as we have discussed earlier. It consists of only two pages.

  • login.jsp View source - page allowing user to enter his or her user name and password and submit request to login to the server.
  • logout.jsp View source - page confirming that user really wants to logout before the request is submitted to the server.

Access to these two pages is provided using link generated by the layout template blog.jsp View source. The code snippet in the template displays the correct link depending on, if the user is already logged in or not

   <bean:define id="loggedin" name="loggedin" scope="request"  type="java.lang.Boolean"/>
   ...               
   <logic:equal name="loggedin" value="false">
      <a href="<%=contextpath%>/<%=BlogNavigator.LOGIN_WEB_PAGE%>">Login</a>
   </logic:equal>
   <%-- If user is logged in, he can modify the blog data --%>
   <logic:equal name="loggedin" value="true">
      <a href="<%=contextpath%>/<%=BlogNavigator.LOGOUT_WEB_PAGE%>">Logout</a>
   </logic:equal>
               

The user can this way login or logout from any location in the application and the individual pages don't have to be concerned how does the user access the login and logout page.

The doGet method of BlogEditServlet View source is responsible for displaying these two new pages to user.

   protected void doGet(
      HttpServletRequest  hsrqRequest,
      HttpServletResponse hsrpResponse
   ) throws ServletException, 
            IOException
   {
      if (isLoginPage(hsrqRequest))
      {
         processNewLoginForm(hsrqRequest, hsrpResponse);
      }
      else if (isLogoutPage(hsrqRequest))
      {
         processNewLogoutForm(hsrqRequest, hsrpResponse);
      }
      else
      {
         // Now let the browser servlet process the request
         super.doGet(hsrqRequest, hsrpResponse);
      }
   }
               

The requests submitted from these pages are handled by doPost method as described earlier. The implementation detects what action the user wants to execute and dispatches the call to a correct method.

   protected void doPost(
      HttpServletRequest  hsrqRequest,
      HttpServletResponse hsrpResponse
   ) throws ServletException, IOException
   {
      switch(getFormToProcess(hsrqRequest))
      {
         case FORM_LOGIN_ID :
         {
            processLoginForm(hsrqRequest, hsrpResponse);
            break;
         }

         case FORM_LOGOUT_ID :
         {
            processLogoutForm(hsrqRequest, hsrpResponse);
            break;
         }
         ...
   }
               

Verifying identity of the user

Before we take a look at the implementation of these methods, lets discuss the framework that Open Core provides to handle authentication. Every request sent to the server is passed to verifyLogin method defined in WebSessionServlet. Its purpose is to verify identity of the user that submitted the request. We will override this method and in our case the implementation will detect two situations: if the user is already logged in or if the user is just trying to login. If neither one of these two situations is detected, it just simply returns null because it doesn't know the user's identity.

   protected Principal verifyLogin(
      HttpSession         hsSession,
      HttpServletRequest  hsrqRequest,
      HttpServletResponse hsrpResponse
   ) throws ServletException,
            IOException
   {
      Principal currentUser = null;
      
      if (getFormToProcess(hsrqRequest) == FORM_LOGIN_ID)
      {
         // User is trying to log in, so try to log him/her in
         final String strLogin;
         String strPassword;
         
         strLogin = hsrqRequest.getParameter("LOGIN");
         strPassword = hsrqRequest.getParameter("PASSWORD");

         // Verify the user name and password and if it is valid, let the 
         // user proceed otherwise return an error message
         if ((m_strLogin.equals(strLogin))
            && (m_strPassword.equals(strPassword)))
         {
            currentUser = new Principal()
            {
               public String getName()
               {
                  return strLogin;
               }         
            };
         }
         else
         {
            CallContext.getInstance().getMessages().addErrorMessage(
               "The user name or password is not valid.");
            // User have tried to login again so reset the info from previous
            // login since it is no longer valid
            WebSessionUtils.resetSessionAndUserInfo(hsSession);
         }
      }
      else
      {
         // User is not trying to log in so see if he is already logged in
         if (WebSessionUtils.isLoggedIn(hsSession))
         {
            currentUser = WebSessionUtils.getLoggedInUserInfo(hsSession);
         }
      }
      
      return currentUser;
   }
               

Once we know that before each request is forwarded for processing, the identity of a user has been already verified, it is easy to implement login a logout. During login we just create an internal session identifier for this user and set it up so that we can detect it during subsequent requests. As a last step, we forward the user either to a URL he tried to access before login or to the main page.

   protected void processLoginForm(
      HttpServletRequest  hsrqRequest,
      HttpServletResponse hsrpResponse
   ) throws IOException,
            ServletException
   {
      s_logger.entering(this.getClass().getName(), "processLoginForm");

      try
      {
         HttpSession hsSession; 
         Principal   currentUser; 
         String      strSessionId;
         String      strURL;
            
         // We do not want to create new session if it doesn't exist since that
         // is the responsibility of the parent classes. We should have session
         // at this point
         hsSession = hsrqRequest.getSession(false);
         currentUser = CallContext.getInstance().getCurrentUser();
         if (GlobalConstants.ERROR_CHECKING)
         {
            assert hsSession != null : "Session must exist at this point";
         }

         if (currentUser != null)
         {
            // If users tries to login again using the same session (by directly 
            // typing login URL) while it is already logged in,
            // the session tracking object in the application server wouldn't be
            // destroyed, just for that case go throught the session object and
            // remove all values in it, this will cause logout if it was previously 
            // logged in
            WebSessionUtils.resetSessionAndUserInfo(hsSession);
   
            // This needs to by synchronized so that we generate the unique id for
            // each session
            synchronized (FORM_LOGIN_NAME)
            {
               strSessionId = Integer.toString(++s_iSessionCounter);
            }
            
            WebSessionUtils.setSessionAndUserInfo(hsSession, strSessionId, currentUser);
            // Check if there is any URL specified in the session object
            strURL = getLoginRedirect(hsSession, hsrqRequest);
            // We are doing the redirect so reset the stored value
            resetLoginRedirect(hsSession);
            if ((strURL == null) || (strURL.length() == 0))
            {
               strURL = getNavigator(hsrqRequest).getRootURL();
            }
            hsrpResponse.sendRedirect(strURL);
         }
         else
         {
            // The login was incorrect so display the login page again
            hsrqRequest.setAttribute("blognavigator", getNavigator(hsrqRequest));
            displayUI(BLOGEDIT_LOGIN_PAGE, hsrqRequest, hsrpResponse);
         }
      }
      catch (Exception eExc)
      {
         s_logger.log(Level.WARNING, 
                      "An error has occured while processing login page.", eExc);
         messageBoxPage(hsrqRequest, hsrpResponse, "Error", eExc.getMessage(),
                        getNavigator(hsrqRequest).getRootURL(),
                        eExc.getCause());
      }
      finally
      {
         s_logger.exiting(this.getClass().getName(), "processLoginForm");
      }
   }
               

Notice how we have retrieved the identity of the user on behalf of who we are processing the request using call

   Principal currentUser = CallContext.getInstance().getCurrentUser()
               

This call can be used anywhere in the code whenever the application needs to know who is executing that portion of the code.

Logout from the application

The verifyLogin method is using

   WebSessionUtils.getLoggedInUserInfo(hsSession);
               

to detect whether the user is already logged in. Knowing this, we just need to ensure that during logout we reset the session information for example in a following way

   protected void processLogoutForm(
      HttpServletRequest  hsrqRequest,
      HttpServletResponse hsrpResponse
   ) throws IOException,
            ServletException
   {
      s_logger.entering(this.getClass().getName(), "processLogoutForm");

      try
      {
         HttpSession hsSession; 
            
         // We do not want to create new session if it doesn't exist since that
         // is the responsibility of the parent claees. We should have session
         // at this point
         hsSession = hsrqRequest.getSession(false);
         if (GlobalConstants.ERROR_CHECKING)
         {
            assert hsSession != null : "Session must exist at this point";
         }
         
         WebSessionUtils.resetSessionAndUserInfo(hsSession);
         
         // Redirect user to the page displaying all blogs
         hsrpResponse.sendRedirect(getNavigator(hsrqRequest).getRootURL());
      }
      catch (Exception eExc)
      {
         s_logger.log(Level.WARNING, 
                      "An error has occured while processing logout page.", eExc);
         messageBoxPage(hsrqRequest, hsrpResponse, "Error", eExc.getMessage(),
                        getNavigator(hsrqRequest).getRootURL(),
                        eExc.getCause());
      }
      finally
      {
         s_logger.exiting(this.getClass().getName(), "processLogoutForm");
      }
   }
               

Mandatory authentication

From our discussion you can see that Open Core makes easy to ensure that every request is authenticated and find out anywhere in the code who the current user is. In addition, by changing a single configuration setting Open Core allows to secure the entire application and let only the authenticated users to access any portion of the application. To do this, Open Core needs to know if there is any specific handling required to display the login page. In case the application needs to do anything special, it can do so by overriding the redirectToLogin method. In our case we just ensure that if the user already requested the login page, we display it without any further delays.

   protected void redirectToLogin(
      HttpServletRequest  hsrqRequest,
      HttpServletResponse hsrpResponse
   ) throws ServletException,
             IOException
   {
      BlogNavigator navigator = getNavigator(hsrqRequest);
      
      if (navigator.isLoginPage())
      {
         // User is already trying to display login page, just show it
         processNewLoginForm(hsrqRequest, hsrpResponse);
      }
      else
      {
         // We were asked to display login page so just let client go to the 
         // the login page to get correct URL to the address bar and we will
         // most likely come back through the if above
         super.redirectToLogin(hsrqRequest, hsrpResponse);
      }
   }
               

This concludes the coding effort. Next step is to correctly configure the application so that it can be easily deployed.

Next: Configuration, packaging and deployment
Previous: Constructing the web application