Apache Forrest WikiRenderer
 
   

AuthWithTomcat

PDF
PDF

Authentication and Authorisation by the servlet container

This document describes how to let the servlet container handle webapp security. There are two parts to security that we consider here:

  • authentication: this is finding out who the user really is. Usually done by asking a username and password but could also be using e.g. a PKI infrastructure.
  • authorisation: once you know who the user is, decide whether he or she is allowed to access certain resources

You can handle this manually by creating a login form and then using Actions in the sitemap to check if the user is logged in. Better yet, there's a framework to do this in Cocoon described here.

Doing the authorisation in the sitemap gives more flexibility regarding what parts of the pipeline should be protected compared to using the security features of the servlet container. However, in many cases what the servlet container offers may be more then enough and is easier and quicker to set up (in my opinion, of course).

In this document we will describe how to set up servlet security for Tomcat 4.

First of all, you need to configure a realm in Tomcat's server.xml. How this can be done is described here.

Basically this involves three things:

  • make a user- and roles database
  • place the JDBC driver in server/lib
  • add a Realm-element in the conf/server.xml (do not forget to disable the default one!)

But all this is described in the document referenced before.

The next step is to edit Cocoon's web.xml (usually found in $TOMCAT_HOME/webapps/cocoon/WEB-INF/web.xml), and add something like shown below near the bottom of the file (before the closing </webapp> tag -- consult the web.xml DTD if unsure).

<security-constraint>
  <web-resource-collection>
    <web-resource-name>MySecretResource</web-resource-name>
    <url-pattern>/topsecret/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>*</role-name>
  </auth-constraint>
</security-constraint>


<login-config>
  <auth-method>FORM</auth-method>
  <realm-name>My protected area</realm-name>
  <form-login-config>
    <form-login-page>/login</form-login-page>
    <form-error-page>/loginfailed</form-error-page>
  </form-login-config>
</login-config>

The url-pattern element describes all the resources to be protected. This should start with a slash and end with '/*'. The path is relative to the context path on which the webapp is mounted. You can notuse more complex expressions like in Cocoon's matchers. You can however use multiple url-pattern elements. It could also specify a single resource, by not ending the path on '/*'.

The login-config element describes how the login should happen. In the example above we have used form-based login, which requires us to create login pages. An alternative is basic authentication, in which case the browser will show a popup window asking for your credentials.

To use basic authentication, replace the login-config element from the above example with this:

<login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>My protected area</realm-name>
</login-config>

Note also that form-based login requires (creates) a session, and will thus always be a little heavier on the server. It will also require cookies to be enabled in users' browsers to keep track of the session.

In case you have chosen to use basic authentication, your work ends here. Restart tomcat and try to access a protected URL. It should ask your user name and password.

In case you have have chosen to use form-based login, you need to create the login pages. These will be generated by Cocoon pipelines. To keep it simple, here we'll use static HTML files. Create two files in $TOMCAT_HOME/webapps/cocoon as follows:

login.html

<html>
  <head>
    <title>Login</title>
  </head>

  <body>
    <form method="POST"  action="j_security_check">
      <table border="0">
        <tr><td>Username:</td><td><input type="text" name="j_username"></td></tr>
        <tr><td>Password:</td><td><input type="password" name="j_password"></td></tr>
        <tr><td></td><td><input type="submit"/></td></tr>
      </table>
    </form>
  </body>
</html>

loginfailed.html

<html>
  <head>
    <title>Login</title>
  </head>

  <body>
    <b>Login failed!</b>
    <form method="POST"  action="j_security_check">
      <table border="0">
        <tr><td>Username:</td><td><input type="text" name="j_username"></td></tr>
        <tr><td>Password:</td><td><input type="password" name="j_password"></td></tr>
        <tr><td></td><td><input type="submit"/></td></tr>
      </table>
    </form>
  </body>
</html>

The special resource "j_security_check" used in the action attribute of the form is a special resource recognized by Tomcat. It is not needed to create a matcher for that one in the sitemap.

Now, we need to add matchers to the sitemap for the login pages. It is recommended to place the login pages themselves outside the protected area, because if the page references images which are also in the protected area, you will get into troubles. This is because (1) the images themselves cannot be accessed if they are in the protected area and (2) after login, Tomcat will redirect you to the last requested protected URL which will in that case be the image.

Here is an example sitemap snippet:

<map:match pattern="login">
  <map:read src="login.html" mime-type="text/html"/>
</map:match>

<map:match pattern="loginfailed">
  <map:read src="loginfailed.html" mime-type="text/html"/>
</map:match>

The patterns of the matchers should of course correspond to what you configured in the web.xml in the elements form-login-page and form-error-page.

Now you are ready to try it out. Restart Tomcat and try accessing a protected resource, and you will get the login form.

A last problem now remains: how to log out?

In case of basic authentication, the user needs to close its browser window.

In case of form-based authentication, you can invalidate the session to log out. This can be done with a pipeline like this.

<map:match pattern="logout">
  <map:act type="session-invalidate"/>
  <map:act type="request">
    <map:parameter name="parameters" value="true"/>
    <map:redirect-to uri="{goto}"/>
  </map:act>
</map:match>

In this case, you would place a link to "/logout?goto=somewhere" which would logout the user and redirect him or her to page "somewhere".

Note that the "session-invalidate" action used above ships with Cocoon (2.03) but is not declared by default in the sitemap. Therefore, add this in the map:actions section of the sitemap:

<map:action
    name="session-invalidate"
    src="org.apache.cocoon.acting.SessionInvalidatorAction"/>

If you get the error "Invalid direct reference to form login page"

If you are getting this error, it usually means that either:

  • you don't have cookies enabled
  • you surfed directly to the login page. You should instead request a protected resource, which will then redirect you to the login page.