Custom realm

In previous example (Form based authentication) a user enters web page first and then he is asked to authenticate himself on web page. In this example we will authenticate user before entering the web page.


The scenario in this example is the same as in previous example. Create new web project named HelloWorld and make single jsp file index.jsp. This is the entry point to your application and we will protect it with our own implementation of realm.

index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<title>Hello</title>
</head>
<body>
<h3>Hello World</h3>

</body>
</html>

Add the following lines to web.xml. It is almost the same as in previous example except <auth-method> element which declares the BASIC type of authentication.

<security-constraint>
<web-resource-collection>
<web-resource-name>MyRealm</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>MyRealm</realm-name>
</login-config>
<security-role>
<role-name>manager</role-name>
</security-role>

Now we need to implement MyRealm class. Implementation of realm should be built separatly of HelloWorld web application and it must be added to Tomcat classpath (ie. put jar inside $CATALINA_HOME/lib).

Start new Java project and import all jars from $CATALINA_HOME/lib. Create new class MyRealm and extend RealmBase class. The methods that must be overridden from parent class are: authenticate(..), getPrincipal(..), getName(..) and getPassword(..).

package si.matjaz.myrealm;

import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.realm.RealmBase;

public class MyRealm extends RealmBase {

private String username;

private String password;

@Override
public Principal authenticate(String username, String credentials) {

this.username = username;
this.password = credentials;

/* dummy authentication */
if (this.username.equals(this.password)) {
return getPrincipal(username);
} else {
return null;
}

}

@Override
protected Principal getPrincipal(String username) {
List<String> roles = new ArrayList<String>();
roles.add("manager");
return new GenericPrincipal(this, username, password, roles);
}

@Override
protected String getPassword(String string) {
return password;
}

@Override
protected String getName() {
return username;
}

/* Custom variables, see <Realm> element */
private String myVariable;

public String getMyVariable() {
return myVariable;
}

public void setMyVariable(String myVariable) {
this.myVariable = myVariable;
}

}

Our implementation of authenticate(..) method is pretty dummy, since it it only checks if username equals to password, so you will have to be more inventive in your code.

If authenticate(..) method returns null, the user is not authenticated and cannot proceed. The Principal object should return a list of roles. Tomcat is based on roles: for every user who successfully authenticates himself you should assign a role. Tomcat treats all users with specified role in the same way and also should your application.

Last thing to do is declare realm implementation in server.xml file. Don’t forget to comment existing UserDatabase realm, since only one Realm can exist per engine/host/context.

server.xml

<!-- <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/> -->

<Realm className="si.matjaz.myrealm.MyRealm" myVariable="test" />

Within <Realm> element we added custom attribute called myVariable. We can define as many attributes as we need in realm implementation, but we should also implement getters and setters for these attributes in MyRealm class.

Deploy HelloWorld application and see if it works:

http://localhost:8080/HelloWorld

MyRealm intercepts the request and shows browser’s login window first. If login is successful you can proceed to index.jsp page, otherwise not.