Security framework for Eclipse Jetty providing authentication, authorization, and user identity management.
—
Java Authentication and Authorization Service (JAAS) integration provides enterprise-grade authentication with pluggable login modules and standardized security contexts.
Main login service for JAAS integration:
public class JAASLoginService extends AbstractLoginService {
// Default role class name
public static final String DEFAULT_ROLE_CLASS_NAME = "org.eclipse.jetty.security.jaas.JAASRole";
// Thread-local instance access
public static final ThreadLocal<JAASLoginService> INSTANCE = new ThreadLocal<>();
// Constructors
public JAASLoginService();
public JAASLoginService(String name);
// Configuration
public String getName();
public void setName(String name);
public Configuration getConfiguration();
public void setConfiguration(Configuration configuration);
// Login module configuration
public void setLoginModuleName(String name);
public String getLoginModuleName();
// Callback handler configuration
public void setCallbackHandlerClass(String classname);
public String getCallbackHandlerClass();
// Role class configuration
public void setRoleClassNames(String[] classnames);
public String[] getRoleClassNames();
@Override
protected UserPrincipal loadUserInfo(String username);
@Override
protected List<RolePrincipal> loadRoleInfo(UserPrincipal user);
}Principal for JAAS-authenticated users:
public class JAASUserPrincipal extends UserPrincipal {
// Constructor
public JAASUserPrincipal(String name, Subject subject, LoginContext loginContext);
// JAAS-specific access
public LoginContext getLoginContext();
public Subject getSubject();
@Override
public void configureSubject(Subject subject);
@Override
public void deconfigureSubject(Subject subject);
}Principal representing a role in JAAS authentication:
public class JAASRole extends JAASPrincipal {
public JAASRole(String roleName);
@Override
public String getName();
@Override
public boolean equals(Object other);
@Override
public int hashCode();
}Base class for JAAS principals:
public abstract class JAASPrincipal implements Principal, Serializable {
protected JAASPrincipal(String name);
@Override
public abstract String getName();
@Override
public boolean equals(Object other);
@Override
public int hashCode();
@Override
public String toString();
}Base class for JAAS callback handlers:
public abstract class AbstractCallbackHandler implements CallbackHandler {
// Protected fields for subclasses
protected String _userName;
protected Object _credential;
protected Request _request;
// Accessor methods
public void setUserName(String userName);
public void setCredential(Object credential);
public void setRequest(Request request);
@Override
public abstract void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException;
}Default implementation supporting common callback types:
public class DefaultCallbackHandler extends AbstractCallbackHandler {
// Configuration
public void setRequest(Request request);
public void setCredential(Object credential);
public void setUserName(String userName);
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) callback;
nameCallback.setName(_userName);
} else if (callback instanceof PasswordCallback) {
PasswordCallback passwordCallback = (PasswordCallback) callback;
if (_credential instanceof String) {
passwordCallback.setPassword(((String) _credential).toCharArray());
}
} else if (callback instanceof ObjectCallback) {
ObjectCallback objectCallback = (ObjectCallback) callback;
objectCallback.setObject(_credential);
} else if (callback instanceof RequestCallback) {
RequestCallback requestCallback = (RequestCallback) callback;
requestCallback.setRequest(_request);
} else if (callback instanceof RequestParameterCallback) {
RequestParameterCallback paramCallback = (RequestParameterCallback) callback;
paramCallback.setParameterValues(_request.getParameters().getValues(paramCallback.getParameterName()));
} else {
throw new UnsupportedCallbackException(callback);
}
}
}
}// ObjectCallback - for retrieving arbitrary objects
public class ObjectCallback implements Callback {
public ObjectCallback();
public void setObject(Object obj);
public Object getObject();
public void clearObject();
}
// RequestCallback - for retrieving the current HTTP request
public class RequestCallback implements Callback {
public RequestCallback();
public void setRequest(Request request);
public Request getRequest();
}
// RequestParameterCallback - for retrieving request parameters
public class RequestParameterCallback implements Callback {
public RequestParameterCallback(String parameterName);
public String getParameterName();
public void setParameterValues(List<String> parameterValues);
public List<String> getParameterValues();
}Base class for JAAS login modules:
public abstract class AbstractLoginModule implements LoginModule {
// JAAS lifecycle
@Override
public boolean initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options);
@Override
public boolean login() throws LoginException;
@Override
public boolean commit() throws LoginException;
@Override
public boolean abort() throws LoginException;
@Override
public boolean logout() throws LoginException;
// Protected utility methods for subclasses
protected Set<JAASRole> getRoles() throws IOException, UnsupportedCallbackException;
protected UserPrincipal getUserPrincipal(String username) throws IOException, UnsupportedCallbackException;
}Base for database-backed login modules:
public abstract class AbstractDatabaseLoginModule extends AbstractLoginModule {
// Database connection management
protected String dbDriver;
protected String dbUrl;
protected String dbUserName;
protected String dbPassword;
// Connection lifecycle
protected Connection getConnection() throws SQLException;
protected void closeConnection(Connection connection);
// Abstract methods for subclasses
protected abstract UserInfo getUserInfo(String username) throws SQLException;
protected abstract List<String> getUserRoles(String username) throws SQLException;
}JAAS login module for JDBC database authentication:
public class JDBCLoginModule extends AbstractDatabaseLoginModule {
// Configuration options (set in JAAS config file)
// - dbDriver: JDBC driver class name
// - dbUrl: Database URL
// - dbUserName: Database username
// - dbPassword: Database password
// - userTable: User table name
// - userField: Username column name
// - credentialField: Password column name
// - roleTable: Role table name
// - roleField: Role name column name
// - userRoleTable: User-role mapping table name
@Override
public boolean login() throws LoginException {
try {
// Get credentials from callback handler
Callback[] callbacks = new Callback[] {
new NameCallback("Username: "),
new PasswordCallback("Password: ", false)
};
callbackHandler.handle(callbacks);
String username = ((NameCallback) callbacks[0]).getName();
char[] password = ((PasswordCallback) callbacks[1]).getPassword();
// Authenticate against database
if (authenticateUser(username, password)) {
// Load user and roles
currentUser = loadUserPrincipal(username);
currentRoles = loadUserRoles(username);
return true;
}
return false;
} catch (Exception e) {
throw new LoginException("JDBC authentication failed: " + e.getMessage());
}
}
@Override
protected UserInfo getUserInfo(String username) throws SQLException;
@Override
protected List<String> getUserRoles(String username) throws SQLException;
private boolean authenticateUser(String username, char[] password) throws SQLException;
private UserPrincipal loadUserPrincipal(String username) throws SQLException;
private Set<JAASRole> loadUserRoles(String username) throws SQLException;
}JAAS login module using DataSource:
public class DataSourceLoginModule extends AbstractDatabaseLoginModule {
// Configuration options:
// - dataSourceName: JNDI name of DataSource
// - userTable, userField, credentialField: User table configuration
// - roleTable, roleField, userRoleTable: Role table configuration
@Override
protected Connection getConnection() throws SQLException {
// Use JNDI DataSource instead of direct connection
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(dataSourceName);
return ds.getConnection();
}
@Override
protected UserInfo getUserInfo(String username) throws SQLException;
@Override
protected List<String> getUserRoles(String username) throws SQLException;
}JAAS login module for LDAP authentication:
public class LdapLoginModule extends AbstractLoginModule {
// LDAP configuration options:
// - hostname: LDAP server hostname
// - port: LDAP server port
// - contextFactory: LDAP context factory class
// - bindDn: Bind DN for LDAP connection
// - bindPassword: Bind password
// - userBaseDn: Base DN for user searches
// - roleBaseDn: Base DN for role searches
// - userIdAttribute: User ID attribute name
// - userPasswordAttribute: Password attribute name
// - userObjectClass: User object class
// - roleIdAttribute: Role ID attribute name
// - roleMemberAttribute: Role membership attribute
// - roleObjectClass: Role object class
@Override
public boolean login() throws LoginException {
try {
// Get credentials from callback handler
Callback[] callbacks = new Callback[] {
new NameCallback("Username: "),
new PasswordCallback("Password: ", false)
};
callbackHandler.handle(callbacks);
String username = ((NameCallback) callbacks[0]).getName();
char[] password = ((PasswordCallback) callbacks[1]).getPassword();
// Authenticate against LDAP
if (authenticateWithLDAP(username, password)) {
currentUser = createUserPrincipal(username);
currentRoles = loadLDAPRoles(username);
return true;
}
return false;
} catch (Exception e) {
throw new LoginException("LDAP authentication failed: " + e.getMessage());
}
}
private boolean authenticateWithLDAP(String username, char[] password) throws Exception;
private UserPrincipal createUserPrincipal(String username) throws Exception;
private Set<JAASRole> loadLDAPRoles(String username) throws Exception;
}JAAS login module for property file authentication:
public class PropertyFileLoginModule extends AbstractLoginModule {
// Configuration options:
// - file: Path to properties file
@Override
public boolean login() throws LoginException {
try {
// Get credentials from callback handler
Callback[] callbacks = new Callback[] {
new NameCallback("Username: "),
new PasswordCallback("Password: ", false)
};
callbackHandler.handle(callbacks);
String username = ((NameCallback) callbacks[0]).getName();
char[] password = ((PasswordCallback) callbacks[1]).getPassword();
// Load properties file
Properties users = loadUserProperties();
// Authenticate user
String userEntry = users.getProperty(username);
if (userEntry != null && authenticateUser(userEntry, password)) {
currentUser = createUserPrincipal(username);
currentRoles = parseRoles(userEntry);
return true;
}
return false;
} catch (Exception e) {
throw new LoginException("Property file authentication failed: " + e.getMessage());
}
}
private Properties loadUserProperties() throws IOException;
private boolean authenticateUser(String userEntry, char[] password);
private UserPrincipal createUserPrincipal(String username);
private Set<JAASRole> parseRoles(String userEntry);
}public class JAASConfigurationExample {
public void setupJAASLoginService() {
JAASLoginService jaasLogin = new JAASLoginService();
jaasLogin.setName("JAASRealm");
// Set login module name (must match JAAS config)
jaasLogin.setLoginModuleName("myLoginModule");
// Set callback handler
jaasLogin.setCallbackHandlerClass("com.example.MyCallbackHandler");
// Configure role classes
jaasLogin.setRoleClassNames(new String[] {
"org.eclipse.jetty.security.jaas.JAASRole",
"com.example.CustomRole"
});
// Use with security handler
SecurityHandler security = new SecurityHandler.PathMapped();
security.setLoginService(jaasLogin);
}
public void setupProgrammaticConfiguration() {
// Create JAAS configuration programmatically
Configuration jaasConfig = new Configuration() {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
if ("myLoginModule".equals(name)) {
Map<String, String> options = new HashMap<>();
options.put("dbDriver", "com.mysql.cj.jdbc.Driver");
options.put("dbUrl", "jdbc:mysql://localhost:3306/users");
options.put("dbUserName", "app");
options.put("dbPassword", "secret");
options.put("userTable", "users");
options.put("userField", "username");
options.put("credentialField", "password");
options.put("roleTable", "roles");
options.put("roleField", "role_name");
options.put("userRoleTable", "user_roles");
AppConfigurationEntry entry = new AppConfigurationEntry(
"org.eclipse.jetty.security.jaas.spi.JDBCLoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options
);
return new AppConfigurationEntry[] { entry };
}
return null;
}
};
// Set the configuration
JAASLoginService jaasLogin = new JAASLoginService();
jaasLogin.setConfiguration(jaasConfig);
}
}// jaas.conf file format
// JDBC Login Module Configuration
myApp-JDBC {
org.eclipse.jetty.security.jaas.spi.JDBCLoginModule required
dbDriver="com.mysql.cj.jdbc.Driver"
dbUrl="jdbc:mysql://localhost:3306/myapp"
dbUserName="app_user"
dbPassword="app_password"
userTable="users"
userField="username"
credentialField="password"
roleTable="user_roles"
roleField="role"
userRoleTable="user_roles";
};
// LDAP Login Module Configuration
myApp-LDAP {
org.eclipse.jetty.security.jaas.spi.LdapLoginModule required
hostname="ldap.example.com"
port="389"
contextFactory="com.sun.jndi.ldap.LdapCtxFactory"
bindDn="cn=admin,dc=example,dc=com"
bindPassword="secret"
userBaseDn="ou=users,dc=example,dc=com"
roleBaseDn="ou=roles,dc=example,dc=com"
userIdAttribute="uid"
userPasswordAttribute="userPassword"
userObjectClass="inetOrgPerson"
roleIdAttribute="cn"
roleMemberAttribute="member"
roleObjectClass="groupOfNames";
};
// Property File Login Module Configuration
myApp-Properties {
org.eclipse.jetty.security.jaas.spi.PropertyFileLoginModule required
file="/etc/myapp/users.properties";
};
// Multi-module configuration (try LDAP first, fallback to local)
myApp-Multi {
org.eclipse.jetty.security.jaas.spi.LdapLoginModule sufficient
hostname="ldap.example.com"
port="389"
userBaseDn="ou=users,dc=example,dc=com";
org.eclipse.jetty.security.jaas.spi.PropertyFileLoginModule required
file="/etc/myapp/local-users.properties";
};public class JAASSystemConfiguration {
public void configureJAASSystemProperties() {
// Set JAAS configuration file
System.setProperty("java.security.auth.login.config", "/path/to/jaas.conf");
// Enable JAAS debug logging
System.setProperty("java.security.debug", "logincontext,policy,scl,gssloginconfig");
// Set security manager (if required)
System.setProperty("java.security.manager", "");
System.setProperty("java.security.policy", "/path/to/security.policy");
// Kerberos configuration (for SPNEGO)
System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");
System.setProperty("sun.security.krb5.debug", "true");
// Create JAAS login service
JAASLoginService jaasLogin = new JAASLoginService("MyRealm");
jaasLogin.setLoginModuleName("myApp-JDBC");
}
}public class PropertyUserStoreManager {
// Static store management
public static PropertyUserStore getPropertyUserStore(String name);
public static PropertyUserStore getPropertyUserStore(String name, String configPath);
// Store lifecycle
public static void addPropertyUserStore(String name, PropertyUserStore store);
public static void removePropertyUserStore(String name);
// Configuration management
public static void setReloadInterval(String name, int seconds);
public static void enableHotReload(String name, boolean enable);
}public class TokenLoginModule extends AbstractLoginModule {
@Override
public boolean initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
super.initialize(subject, callbackHandler, sharedState, options);
// Get configuration options
this.tokenValidator = (String) options.get("tokenValidator");
this.roleAttribute = (String) options.get("roleAttribute");
return true;
}
@Override
public boolean login() throws LoginException {
try {
// Custom callback for token
Callback[] callbacks = new Callback[] {
new ObjectCallback() // Custom token callback
};
callbackHandler.handle(callbacks);
Object token = ((ObjectCallback) callbacks[0]).getObject();
if (validateToken(token)) {
TokenInfo tokenInfo = parseToken(token);
currentUser = createUserPrincipal(tokenInfo.getSubject());
currentRoles = createRoles(tokenInfo.getRoles());
return true;
}
return false;
} catch (Exception e) {
throw new LoginException("Token authentication failed: " + e.getMessage());
}
}
private boolean validateToken(Object token);
private TokenInfo parseToken(Object token);
private UserPrincipal createUserPrincipal(String subject);
private Set<JAASRole> createRoles(List<String> roleNames);
private static class TokenInfo {
public String getSubject() { return null; }
public List<String> getRoles() { return Collections.emptyList(); }
}
}public class MultiRealmJAAS {
public void setupMultipleRealms() {
// Primary realm with LDAP
JAASLoginService primaryRealm = new JAASLoginService();
primaryRealm.setName("PrimaryRealm");
primaryRealm.setLoginModuleName("primary-ldap");
primaryRealm.setCallbackHandlerClass("com.example.LDAPCallbackHandler");
// Admin realm with local properties
JAASLoginService adminRealm = new JAASLoginService();
adminRealm.setName("AdminRealm");
adminRealm.setLoginModuleName("admin-properties");
adminRealm.setCallbackHandlerClass("com.example.PropertiesCallbackHandler");
// API realm with token authentication
JAASLoginService apiRealm = new JAASLoginService();
apiRealm.setName("APIRealm");
apiRealm.setLoginModuleName("api-tokens");
apiRealm.setCallbackHandlerClass("com.example.TokenCallbackHandler");
// Configure different security handlers for different areas
SecurityHandler.PathMapped webSecurity = new SecurityHandler.PathMapped();
webSecurity.setLoginService(primaryRealm);
webSecurity.put("/app/*", Constraint.ANY_USER);
SecurityHandler.PathMapped adminSecurity = new SecurityHandler.PathMapped();
adminSecurity.setLoginService(adminRealm);
adminSecurity.put("/admin/*", Constraint.from("admin"));
SecurityHandler.PathMapped apiSecurity = new SecurityHandler.PathMapped();
apiSecurity.setLoginService(apiRealm);
apiSecurity.put("/api/*", Constraint.from("api-user"));
}
}public class CustomJAASCallbackHandler extends AbstractCallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
handleNameCallback((NameCallback) callback);
} else if (callback instanceof PasswordCallback) {
handlePasswordCallback((PasswordCallback) callback);
} else if (callback instanceof ObjectCallback) {
handleObjectCallback((ObjectCallback) callback);
} else if (callback instanceof RequestCallback) {
handleRequestCallback((RequestCallback) callback);
} else if (callback instanceof RequestParameterCallback) {
handleRequestParameterCallback((RequestParameterCallback) callback);
} else if (callback instanceof ChoiceCallback) {
handleChoiceCallback((ChoiceCallback) callback);
} else {
throw new UnsupportedCallbackException(callback,
"Callback type not supported: " + callback.getClass().getName());
}
}
}
private void handleNameCallback(NameCallback callback) {
// Extract username from request headers, parameters, or attributes
String username = extractUsernameFromRequest();
callback.setName(username);
}
private void handlePasswordCallback(PasswordCallback callback) {
// Extract password from request or use token-based authentication
String password = extractPasswordFromRequest();
callback.setPassword(password != null ? password.toCharArray() : null);
}
private void handleObjectCallback(ObjectCallback callback) {
// Handle custom object callbacks (e.g., tokens, certificates)
Object credential = extractCustomCredential();
callback.setObject(credential);
}
private void handleRequestCallback(RequestCallback callback) {
// Provide the HTTP request to login modules
callback.setRequest(_request);
}
private void handleRequestParameterCallback(RequestParameterCallback callback) {
// Extract specific request parameters
String paramName = callback.getParameterName();
List<String> values = _request.getParameters().getValues(paramName);
callback.setParameterValues(values);
}
private void handleChoiceCallback(ChoiceCallback callback) {
// Handle multiple choice selections (e.g., authentication methods)
String[] choices = callback.getChoices();
int selectedIndex = determineChoice(choices);
callback.setSelectedIndex(selectedIndex);
}
private String extractUsernameFromRequest() {
// Implementation to extract username
return null;
}
private String extractPasswordFromRequest() {
// Implementation to extract password
return null;
}
private Object extractCustomCredential() {
// Implementation to extract custom credentials
return null;
}
private int determineChoice(String[] choices) {
// Implementation to determine choice
return 0;
}
}public class JAASSecurityBestPractices {
public void configureSecureJAAS() {
JAASLoginService jaasLogin = new JAASLoginService();
jaasLogin.setName("SecureRealm");
// Use strong authentication methods
jaasLogin.setLoginModuleName("multi-factor-ldap");
// Custom callback handler with security validations
jaasLogin.setCallbackHandlerClass("com.example.SecureCallbackHandler");
// Configure multiple role classes for fine-grained access control
jaasLogin.setRoleClassNames(new String[] {
"org.eclipse.jetty.security.jaas.JAASRole",
"com.example.ApplicationRole",
"com.example.PermissionRole"
});
// Enable comprehensive logging
System.setProperty("java.security.debug", "access,logincontext,policy");
// Set up security manager
setupSecurityManager();
}
private void setupSecurityManager() {
// Configure security policy for JAAS
System.setProperty("java.security.policy", "/etc/myapp/security.policy");
// Enable security manager
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
}
}JAAS integration provides enterprise-grade authentication with standardized login modules, flexible callback handling, and comprehensive security contexts for complex authentication scenarios.
Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-security