Embedded Apache Tomcat servlet container with Jakarta Servlet API, HTTP connectors, and lifecycle management for Java web applications
Comprehensive authentication and authorization framework including BASIC, DIGEST, FORM, and CLIENT-CERT authenticators, realm implementations for user/role management, credential handlers for password encryption, and Jakarta Authentication (JASPIC) integration for custom authentication modules.
Core authentication and authorization interface.
public interface Realm {
// Container
Container getContainer();
void setContainer(Container container);
// Credential handler
CredentialHandler getCredentialHandler();
void setCredentialHandler(CredentialHandler credentialHandler);
// Authentication methods
Principal authenticate(String username);
Principal authenticate(String username, String credentials);
Principal authenticate(String username, String digest, String nonce, String nc,
String cnonce, String qop, String realm, String digestA2);
Principal authenticate(String username, String digest, String nonce, String nc,
String cnonce, String qop, String realm, String digestA2, String algorithm);
Principal authenticate(GSSContext gssContext, boolean storeCreds);
Principal authenticate(GSSName gssName, GSSCredential gssCredential);
Principal authenticate(X509Certificate[] certs);
// Authorization
boolean hasRole(Wrapper wrapper, Principal principal, String role);
boolean hasResourcePermission(Request request, Response response,
SecurityConstraint[] constraints, Context context) throws IOException;
boolean hasUserDataPermission(Request request, Response response,
SecurityConstraint[] constraints) throws IOException;
// Background processing
void backgroundProcess();
// Security constraints
SecurityConstraint[] findSecurityConstraints(Request request, Context context);
// Event listeners
void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangeListener(PropertyChangeListener listener);
}Tomcat provides multiple concrete realm implementations for different user storage backends.
/**
* Base class for Realm implementations providing common functionality.
*/
public abstract class RealmBase implements Realm, Lifecycle {
protected Container container;
protected CredentialHandler credentialHandler;
// Subclasses must implement these methods
protected abstract String getPassword(String username);
protected abstract Principal getPrincipal(String username);
public abstract boolean hasRole(Wrapper wrapper, Principal principal, String role);
}
/**
* Simple in-memory realm for development and testing.
* Users and roles are defined in tomcat-users.xml.
*/
public class MemoryRealm extends RealmBase {
public MemoryRealm();
/** Set path to the users configuration file */
public void setPathname(String pathname);
public String getPathname();
}
/**
* Realm implementation that reads user information from a JDBC database.
*/
public class DataSourceRealm extends RealmBase {
public DataSourceRealm();
/** Set the JNDI name of the DataSource */
public void setDataSourceName(String dataSourceName);
public String getDataSourceName();
/** Set the SQL query to retrieve user credentials */
public void setUserCredCol(String userCredCol);
public String getUserCredCol();
/** Set the SQL query to retrieve user roles */
public void setUserRoleTable(String userRoleTable);
public String getUserRoleTable();
/** Set the name column in user table */
public void setUserNameCol(String userNameCol);
public String getUserNameCol();
/** Set the name of the user table */
public void setUserTable(String userTable);
public String getUserTable();
/** Set the role name column */
public void setRoleNameCol(String roleNameCol);
public String getRoleNameCol();
}
/**
* Realm implementation that authenticates users against an LDAP directory.
*/
public class JNDIRealm extends RealmBase {
public JNDIRealm();
/** Set the connection URL for the LDAP server */
public void setConnectionURL(String connectionURL);
public String getConnectionURL();
/** Set the connection name (DN) to use for authentication */
public void setConnectionName(String connectionName);
public String getConnectionName();
/** Set the connection password */
public void setConnectionPassword(String connectionPassword);
/** Set the base DN for user searches */
public void setUserBase(String userBase);
public String getUserBase();
/** Set the search pattern for finding users */
public void setUserSearch(String userSearch);
public String getUserSearch();
/** Set the base DN for role searches */
public void setRoleBase(String roleBase);
public String getRoleBase();
/** Set the search pattern for finding roles */
public void setRoleSearch(String roleSearch);
public String getRoleSearch();
/** Set the attribute name for role names */
public void setRoleName(String roleName);
public String getRoleName();
/** Set whether to use subtree scope for searches */
public void setRoleSubtree(boolean roleSubtree);
public boolean getRoleSubtree();
/** Set whether nested groups should be searched */
public void setRoleNested(boolean roleNested);
public boolean getRoleNested();
}
/**
* Realm implementation that uses JAAS for authentication.
*/
public class JAASRealm extends RealmBase {
public JAASRealm();
/** Set the JAAS application name */
public void setAppName(String appName);
public String getAppName();
/** Set the user class name */
public void setUserClassNames(String userClassNames);
public String getUserClassNames();
/** Set the role class names */
public void setRoleClassNames(String roleClassNames);
public String getRoleClassNames();
/** Set whether to use context class loader */
public void setUseContextClassLoader(boolean useContextClassLoader);
public boolean isUseContextClassLoader();
}
/**
* Realm implementation that uses the UserDatabase JNDI resource.
*/
public class UserDatabaseRealm extends RealmBase {
public UserDatabaseRealm();
/** Set the JNDI name of the UserDatabase resource */
public void setResourceName(String resourceName);
public String getResourceName();
}
/**
* Realm that combines multiple realms for authentication.
* Attempts authentication against each realm in sequence.
*/
public class CombinedRealm extends RealmBase {
public CombinedRealm();
/** Add a realm to the combined realm */
public void addRealm(Realm realm);
/** Get all configured realms */
public Realm[] getNestedRealms();
}
/**
* Realm that provides brute force attack protection by locking out users
* after too many failed authentication attempts.
*/
public class LockOutRealm extends CombinedRealm {
public LockOutRealm();
/** Set the number of failed attempts before lockout */
public void setFailureCount(int failureCount);
public int getFailureCount();
/** Set the lockout duration in seconds */
public void setLockOutTime(int lockOutTime);
public int getLockOutTime();
/** Set the time window for counting failures in seconds */
public void setCacheSize(int cacheSize);
public int getCacheSize();
/** Set whether to cache authenticated users */
public void setCacheRemovalWarningTime(int cacheRemovalWarningTime);
public int getCacheRemovalWarningTime();
}
/**
* No-op realm that always denies authentication.
* Useful for applications that don't require authentication.
*/
public class NullRealm extends RealmBase {
public NullRealm();
}Credential handler implementations for password encryption and verification.
/**
* Base credential handler interface.
*/
public interface CredentialHandler {
boolean matches(String inputCredentials, String storedCredentials);
String mutate(String inputCredentials);
}
/**
* Credential handler using message digest algorithms (MD5, SHA-256, etc.).
*/
public class MessageDigestCredentialHandler implements CredentialHandler {
public MessageDigestCredentialHandler();
/** Set the digest algorithm (e.g., "SHA-256", "MD5") */
public void setAlgorithm(String algorithm);
public String getAlgorithm();
/** Set the digest encoding ("hex" or "base64") */
public void setEncoding(String encoding);
public String getEncoding();
/** Set the number of iterations for key derivation */
public void setIterations(int iterations);
public int getIterations();
/** Set the salt length in bytes */
public void setSaltLength(int saltLength);
public int getSaltLength();
@Override
public boolean matches(String inputCredentials, String storedCredentials);
@Override
public String mutate(String inputCredentials);
}
/**
* Credential handler using SecretKeyFactory for PBKDF2 and similar algorithms.
*/
public class SecretKeyCredentialHandler implements CredentialHandler {
public SecretKeyCredentialHandler();
/** Set the algorithm (e.g., "PBKDF2WithHmacSHA256") */
public void setAlgorithm(String algorithm);
public String getAlgorithm();
/** Set the number of iterations */
public void setIterations(int iterations);
public int getIterations();
/** Set the key length in bits */
public void setKeyLength(int keyLength);
public int getKeyLength();
/** Set the salt length in bytes */
public void setSaltLength(int saltLength);
public int getSaltLength();
@Override
public boolean matches(String inputCredentials, String storedCredentials);
@Override
public String mutate(String inputCredentials);
}
/**
* Credential handler that supports multiple nested credential handlers.
* Tries each handler in sequence for matching.
*/
public class NestedCredentialHandler implements CredentialHandler {
public NestedCredentialHandler();
/** Add a credential handler */
public void addCredentialHandler(CredentialHandler handler);
/** Get all configured credential handlers */
public CredentialHandler[] getCredentialHandlers();
@Override
public boolean matches(String inputCredentials, String storedCredentials);
@Override
public String mutate(String inputCredentials);
}Configuration Example:
import org.apache.catalina.realm.DataSourceRealm;
import org.apache.catalina.realm.MessageDigestCredentialHandler;
// Configure DataSource realm with SHA-256 password hashing
DataSourceRealm realm = new DataSourceRealm();
realm.setDataSourceName("java:comp/env/jdbc/UserDB");
realm.setUserTable("users");
realm.setUserNameCol("username");
realm.setUserCredCol("password");
realm.setUserRoleTable("user_roles");
realm.setRoleNameCol("role_name");
// Configure credential handler
MessageDigestCredentialHandler credentialHandler = new MessageDigestCredentialHandler();
credentialHandler.setAlgorithm("SHA-256");
credentialHandler.setIterations(100000);
credentialHandler.setSaltLength(32);
realm.setCredentialHandler(credentialHandler);
// Attach to context
context.setRealm(realm);Valve that performs authentication.
public interface Authenticator {
boolean authenticate(Request request, HttpServletResponse response) throws IOException;
void login(String userName, String password, Request request) throws ServletException;
void logout(Request request);
}Configuration for URL-based security constraints.
public class SecurityConstraint implements Serializable {
// Constructor
public SecurityConstraint();
// Display name
public void setDisplayName(String displayName);
public String getDisplayName();
// Auth constraint
public void setAuthConstraint(boolean authConstraint);
public boolean getAuthConstraint();
public void addAuthRole(String authRole);
public boolean findAuthRole(String role);
public String[] findAuthRoles();
public void removeAuthRole(String authRole);
// User data constraint
public void setUserConstraint(String userConstraint);
public String getUserConstraint();
// Collections
public void addCollection(SecurityCollection collection);
public SecurityCollection[] findCollections();
public void removeCollection(SecurityCollection collection);
}
public class SecurityCollection implements Serializable {
// Constructor
public SecurityCollection();
public SecurityCollection(String name);
// Name and description
public void setName(String name);
public String getName();
public void setDescription(String description);
public String getDescription();
// URL patterns
public void addPattern(String pattern);
public boolean findPattern(String pattern);
public String[] findPatterns();
public void removePattern(String pattern);
// HTTP methods
public void addMethod(String method);
public boolean findMethod(String method);
public String[] findMethods();
public void removeMethod(String method);
// Omitted HTTP methods
public void addOmittedMethod(String method);
public boolean findOmittedMethod(String method);
public String[] findOmittedMethods();
public void removeOmittedMethod(String method);
}
public class LoginConfig implements Serializable {
// Constructor
public LoginConfig();
// Authentication method
public void setAuthMethod(String authMethod);
public String getAuthMethod();
// Realm name
public void setRealmName(String realmName);
public String getRealmName();
// Login and error pages (for FORM authentication)
public void setLoginPage(String loginPage);
public String getLoginPage();
public void setErrorPage(String errorPage);
public String getErrorPage();
}Jakarta Authentication (JASPIC) interfaces.
// Server authentication module
public interface ServerAuthModule {
void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
CallbackHandler handler, Map<String,Object> options) throws AuthException;
AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
Subject serviceSubject) throws AuthException;
AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject)
throws AuthException;
void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException;
Class<?>[] getSupportedMessageTypes();
}
// Authentication status
public class AuthStatus {
public static final AuthStatus SUCCESS;
public static final AuthStatus FAILURE;
public static final AuthStatus SEND_SUCCESS;
public static final AuthStatus SEND_FAILURE;
public static final AuthStatus SEND_CONTINUE;
}
// Message info
public interface MessageInfo {
Object getRequestMessage();
Object getResponseMessage();
void setRequestMessage(Object request);
void setResponseMessage(Object response);
Map<String,Object> getMap();
}import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.Context;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
public class BasicAuthExample {
public static void main(String[] args) throws Exception {
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
Context ctx = tomcat.addWebapp("/secure", "/path/to/webapp");
// Add users
tomcat.addUser("admin", "admin123");
tomcat.addRole("admin", "manager");
// Add security constraint
SecurityConstraint constraint = new SecurityConstraint();
constraint.setAuthConstraint(true);
constraint.addAuthRole("manager");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/admin/*");
constraint.addCollection(collection);
ctx.addConstraint(constraint);
// Configure login
LoginConfig config = new LoginConfig();
config.setAuthMethod("BASIC");
config.setRealmName("Protected Area");
ctx.setLoginConfig(config);
tomcat.start();
tomcat.getServer().await();
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-tomcat-embed--tomcat-embed-core