目次

Version 4, last updated by Christoph Knabe at Nov 28 01:17 UTC

How to use Container Managed Security

Web servers like Tomcat or Jetty offer Container Managed Security with Single Sign On. This means, that a user is required to login only once at the web container, and the container will pass its username and rolenames to all web applications running on this container. In my organization we use this to run JSPWiki and a self-written Java web application on the same server, sharing the authentication and user roles.

While the Lift user management by MetaMegaProtoUser is comfortable and flexible for self-registering users, this way was not suitable for us, because we get all authentication information from our computing center. Users are not allowed to register.

So how to get the information from HttpServletRequest.getRemoteUser and HttpServletRequest.isUserInRole(String)?

How to get access to the HttpServletRequest?

You get access to the HttpServletRequest of the Servlet API by the following steps:

  • Get S.request, which has type Box[Req]
  • From req: Req take req.request, which has the Lift type HTTPRequest
  • Cast it to HTTPRequestServlet (also a Lift type)
  • Ask it for the HttpServletRequest from the Servlet API
  • Ask the latter for the current username and check each of the relevant roles.

And here is the code for doing this:

def tryToStoreCurrentUserLoginInSession(){
  // If the Box is Full(req), we will work with its content req:
  S.request.foreach{
    _tryToStoreCurrentUserLoginInSession(_)
  }
}

private def _tryToStoreCurrentUserLoginInSession(req: Req){
  val httpRequest: HTTPRequest = req.request
  if(httpRequest==null){return ()}
  val hrs = httpRequest.asInstanceOf[HTTPRequestServlet]
  val hsr: HttpServletRequest = hrs.req
  val username: String = hsr.getRemoteUser
  if(username!=null){
    val currentRoles = rolesToCheck.filter(hsr.isUserInRole(_))
    _log.debug("Storing: User=" + username + ", Roles=" + currentRoles)
    User.storeActualLogin(new User.Login(username, currentRoles))
  }
}

val rolesToCheck = List(
  "Admin", "Secretary", "Teacher", "Authenticated"
)

The List rolesToCheck has to be iterated over, as there is no method in the HTTPRequestServlet API to get all roles, the current user has.

The method User.storeActualLogin is not described here. It will store the user information in a SessionVar, so that the info will be accessible when processing further requests. This showed to be necessary, as on some Comet/AJAX requests, there was no associated HttpServletRequest.

How to enable Single Sign On on Tomcat

This is described under Tomcat Single Sign On.

How to simply define usernames with password and roles in Jetty for test purposes?

We have done it in the Maven POM, although there is an alternative possibility for this in the Jetty configuration file (see Jetty Realms). For details see Jetty HashUserRealm.

Here is our Maven Jetty configuration:

<build> ...
  <plugins> ...
    <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <configuration>
          <stopPort>9966</stopPort>
          <stopKey>foo</stopKey>
          <contextPath>/</contextPath>
          <scanIntervalSeconds>6</scanIntervalSeconds>
          <userRealms>
            <userRealm implementation="org.mortbay.jetty.security.HashUserRealm">
              <name>ComputingCenterAccount</name>
              <!-- Searchs the file with the login informations in the same directory as the pom.xml:
                In this file you can define users with a password and their roles in the format:
                username: password [,rolename ...]
                Siehe http://jetty.codehaus.org/jetty/jetty-6/apidocs/org/mortbay/jetty/security/HashUserRealm.html
              -->
              <config>jetty-login.properties</config>
            </userRealm>
          </userRealms>
        </configuration>
    </plugin>
  </plugins>
<build>

How to force Container Authentication

This is done by securing pages in the file WEB-INF/web.xml, e.g. the login success page or all pages. When the client requests a secured page, the container will prompt the user for his name and password. If succeeding, the container will serve the requested page and have the user information stored in the session. Then further requests can ask getRemoteUser and isUserInRole.

Here is the security part of our file web.xml under the webapp element.

    <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>ComputingCenterAccount</realm-name>
    </login-config>

    <!-- Security roles referenced by this web application -->
    <security-role>
      <description>An authenticated user according to the computing center</description>
      <role-name>Authenticated</role-name>
    </security-role>
    <security-role>
	<description>A teacher of the university according to the computing center</description>
	<role-name>Teacher</role-name>
    </security-role>
    <security-role>
	<description>A secretary of the dean of the faculty</description>
	<role-name>Secretary </role-name>
    </security-role>
    <security-role>
	<description>An administrator of the web server</description>
	<role-name>Admin</role-name>
    </security-role>

  <!-- Secured resources -->
    <security-constraint>
      <web-resource-collection>
        <web-resource-name>ForceLogin</web-resource-name>
        <description>Secured page for forcing the container to request login</description>
        <url-pattern>/login</url-pattern>
      </web-resource-collection>
      <auth-constraint>
        <role-name>Authenticated</role-name>
      </auth-constraint>
    </security-constraint>

    <security-constraint>
      <web-resource-collection>
        <web-resource-name>AdminPages</web-resource-name>
        <description>Administrator-only pages</description>
        <url-pattern>/admin/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
        <!-- As Lift issues better error messages, the container will give access to all authenticated users, 
          but Lift will check that the admin pages are served only to administrators.
        -->
        <role-name>Authenticated</role-name>
      </auth-constraint>
    </security-constraint>