Security framework for Eclipse Jetty providing authentication, authorization, and user identity management.
—
User identity management in Jetty Security encompasses user principals, roles, identity services, and the association of security contexts with execution threads.
Central interface representing an authenticated user:
public interface UserIdentity {
// Core identity access
Subject getSubject();
Principal getUserPrincipal();
// Role checking
boolean isUserInRole(String role);
// Static factory methods
static UserIdentity from(Subject subject, Principal userPrincipal, String... roles);
}public class UserIdentityExample {
public void createUserIdentity() {
// Create subject for user
Subject subject = new Subject();
// Create user principal
UserPrincipal userPrincipal = new UserPrincipal("john",
Credential.getCredential("password123"));
// Create user identity with roles
UserIdentity identity = UserIdentity.from(subject, userPrincipal,
"user", "developer", "api-access");
// Role checking
boolean canAccess = identity.isUserInRole("developer");
boolean isAdmin = identity.isUserInRole("admin");
// Access underlying components
Subject userSubject = identity.getSubject();
Principal principal = identity.getUserPrincipal();
}
}Manages associations between user identities and execution threads:
public interface IdentityService {
// Thread association
Association associate(UserIdentity user, RunAsToken runAsToken);
// User lifecycle
void onLogout(UserIdentity user);
// Factory methods
UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles);
RunAsToken newRunAsToken(String roleName);
// System user access
UserIdentity getSystemUserIdentity();
// Nested interfaces
interface Association extends AutoCloseable {
@Override
void close();
}
interface RunAsToken {
// Opaque token for run-as operations
}
}Default implementation of IdentityService:
public class DefaultIdentityService implements IdentityService {
@Override
public Association associate(UserIdentity user, RunAsToken runAsToken) {
// Associate user identity with current thread
return new AssociationImpl(user, runAsToken);
}
@Override
public void onLogout(UserIdentity user) {
// Clear any thread associations for this user
clearThreadAssociations(user);
}
@Override
public UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles) {
// Create DefaultUserIdentity with provided components
return new DefaultUserIdentity(subject, userPrincipal, roles);
}
@Override
public RunAsToken newRunAsToken(String roleName) {
// Create run-as token for the specified role
return new RoleRunAsToken(roleName);
}
@Override
public UserIdentity getSystemUserIdentity() {
// Return null - no system user by default
return null;
}
// Static utility methods
public static boolean isRoleAssociated(String role);
// Private implementation classes
private static class AssociationImpl implements Association {
// Implementation details
}
}Represents a user with authentication capabilities:
public class UserPrincipal implements Principal {
// Constructor
public UserPrincipal(String name, Credential credential);
@Override
public String getName();
// Authentication methods
public boolean authenticate(Object credentials);
public boolean authenticate(Credential c);
public boolean authenticate(UserPrincipal u);
// Subject integration
public void configureSubject(Subject subject);
public void deconfigureSubject(Subject subject);
// Utility methods
@Override
public boolean equals(Object other);
@Override
public int hashCode();
@Override
public String toString();
}public class UserPrincipalExample {
public void createAndUseUserPrincipal() {
// Create user principal with password
Credential password = Credential.getCredential("secretPassword");
UserPrincipal user = new UserPrincipal("alice", password);
// Test authentication
boolean validPassword = user.authenticate("secretPassword");
boolean invalidPassword = user.authenticate("wrongPassword");
// Test credential object authentication
Credential testCred = Credential.getCredential("secretPassword");
boolean credValid = user.authenticate(testCred);
// Test user-to-user authentication
UserPrincipal sameUser = new UserPrincipal("alice", password);
boolean sameUserAuth = user.authenticate(sameUser);
// Configure subject
Subject subject = new Subject();
user.configureSubject(subject);
// Subject now contains the user principal
Set<Principal> principals = subject.getPrincipals();
boolean containsUser = principals.contains(user);
}
public void createUserWithHashedPassword() {
// Create user with MD5 hashed password
Credential md5Cred = Credential.getCredential("MD5:5d41402abc4b2a76b9719d911017c592");
UserPrincipal user = new UserPrincipal("bob", md5Cred);
// Authenticate with plain text (will be hashed for comparison)
boolean authenticated = user.authenticate("hello");
// Create user with crypt password
Credential cryptCred = Credential.getCredential("CRYPT:$1$salt$hash");
UserPrincipal cryptUser = new UserPrincipal("charlie", cryptCred);
}
}Utility class for secure credential handling and password storage:
public abstract class Credential {
// Factory method for creating credentials
public static Credential getCredential(String credential);
// Credential verification
public abstract boolean check(Object credentials);
// Common credential types supported:
// - Plain text passwords: "password123"
// - MD5 hashed: "MD5:5d41402abc4b2a76b9719d911017c592"
// - Crypt hashed: "CRYPT:$1$salt$hash"
// - BCrypt hashed: "$2y$10$..."
}The Credential class provides secure password storage and verification. It automatically detects the credential type and applies appropriate hashing and verification algorithms.
Represents a role that can be associated with subjects:
public class RolePrincipal implements Principal {
// Constructor
public RolePrincipal(String name);
@Override
public String getName();
// Subject integration
public void configureSubject(Subject subject);
public void deconfigureSubject(Subject subject);
@Override
public boolean equals(Object other);
@Override
public int hashCode();
@Override
public String toString();
}public class RoleManagementExample {
public void manageUserRoles() {
// Create user
UserPrincipal user = new UserPrincipal("manager",
Credential.getCredential("password"));
// Create roles
RolePrincipal userRole = new RolePrincipal("user");
RolePrincipal managerRole = new RolePrincipal("manager");
RolePrincipal adminRole = new RolePrincipal("admin");
// Create subject and configure
Subject subject = new Subject();
user.configureSubject(subject);
userRole.configureSubject(subject);
managerRole.configureSubject(subject);
// Create user identity
IdentityService identityService = new DefaultIdentityService();
UserIdentity identity = identityService.newUserIdentity(subject, user,
new String[]{"user", "manager"});
// Check roles
boolean isUser = identity.isUserInRole("user"); // true
boolean isManager = identity.isUserInRole("manager"); // true
boolean isAdmin = identity.isUserInRole("admin"); // false
}
public void createHierarchicalRoles() {
// Create role hierarchy
Map<String, Set<String>> roleHierarchy = new HashMap<>();
roleHierarchy.put("admin", Set.of("admin", "manager", "user"));
roleHierarchy.put("manager", Set.of("manager", "user"));
roleHierarchy.put("user", Set.of("user"));
// Custom identity service with role hierarchy
IdentityService hierarchicalService = new HierarchicalIdentityService(roleHierarchy);
// User with only "manager" role
UserPrincipal user = new UserPrincipal("supervisor",
Credential.getCredential("password"));
Subject subject = new Subject();
user.configureSubject(subject);
UserIdentity identity = hierarchicalService.newUserIdentity(subject, user,
new String[]{"manager"});
// Manager can act as user due to hierarchy
boolean canActAsUser = identity.isUserInRole("user"); // true
boolean canActAsManager = identity.isUserInRole("manager"); // true
boolean canActAsAdmin = identity.isUserInRole("admin"); // false
}
// Custom identity service with role hierarchy
private static class HierarchicalIdentityService extends DefaultIdentityService {
private final Map<String, Set<String>> roleHierarchy;
public HierarchicalIdentityService(Map<String, Set<String>> roleHierarchy) {
this.roleHierarchy = roleHierarchy;
}
@Override
public UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles) {
// Expand roles based on hierarchy
Set<String> expandedRoles = new HashSet<>();
for (String role : roles) {
Set<String> impliedRoles = roleHierarchy.get(role);
if (impliedRoles != null) {
expandedRoles.addAll(impliedRoles);
} else {
expandedRoles.add(role);
}
}
return super.newUserIdentity(subject, userPrincipal,
expandedRoles.toArray(new String[0]));
}
}
}public class ThreadAssociationExample {
public void demonstrateThreadAssociation() {
IdentityService identityService = new DefaultIdentityService();
// Create user identity
UserPrincipal user = new UserPrincipal("worker",
Credential.getCredential("password"));
Subject subject = new Subject();
UserIdentity identity = identityService.newUserIdentity(subject, user,
new String[]{"worker", "api-user"});
// Associate with current thread
try (IdentityService.Association association =
identityService.associate(identity, null)) {
// Code in this block runs with user identity
performSecureOperation();
// Association is automatically cleaned up when block exits
}
// User identity is no longer associated with thread
}
public void demonstrateRunAs() {
IdentityService identityService = new DefaultIdentityService();
// Create regular user
UserIdentity regularUser = createUserIdentity("john", "user");
// Create run-as token for admin role
IdentityService.RunAsToken adminToken = identityService.newRunAsToken("admin");
// Associate regular user but run as admin
try (IdentityService.Association association =
identityService.associate(regularUser, adminToken)) {
// Check if admin role is associated with thread
boolean isAdmin = DefaultIdentityService.isRoleAssociated("admin");
// Perform admin operations
performAdminOperation();
}
}
public void demonstrateNestedAssociations() {
IdentityService identityService = new DefaultIdentityService();
UserIdentity user1 = createUserIdentity("alice", "user");
UserIdentity user2 = createUserIdentity("bob", "manager");
try (IdentityService.Association outer = identityService.associate(user1, null)) {
// Outer context: alice as user
performUserOperation();
try (IdentityService.Association inner = identityService.associate(user2, null)) {
// Inner context: bob as manager
performManagerOperation();
} // bob's association ends
// Back to alice's context
performUserOperation();
} // alice's association ends
}
private UserIdentity createUserIdentity(String name, String role) {
IdentityService identityService = new DefaultIdentityService();
UserPrincipal user = new UserPrincipal(name, Credential.getCredential("password"));
Subject subject = new Subject();
return identityService.newUserIdentity(subject, user, new String[]{role});
}
private void performSecureOperation() {
// Implementation
}
private void performAdminOperation() {
// Implementation requiring admin privileges
}
private void performUserOperation() {
// Implementation for regular users
}
private void performManagerOperation() {
// Implementation for managers
}
}public class RoleDelegateUserIdentity implements UserIdentity {
private final UserIdentity delegate;
private final Subject subject;
private final Principal userPrincipal;
// Constructor
public RoleDelegateUserIdentity(UserIdentity delegate, Subject subject, Principal userPrincipal);
@Override
public Subject getSubject() {
return subject;
}
@Override
public Principal getUserPrincipal() {
return userPrincipal;
}
@Override
public boolean isUserInRole(String role) {
// Delegate role checking to another UserIdentity
return delegate.isUserInRole(role);
}
}public class RoleDelegationExample {
public void demonstrateRoleDelegation() {
// Create primary user identity with roles
UserPrincipal primaryUser = new UserPrincipal("primary",
Credential.getCredential("password"));
Subject primarySubject = new Subject();
IdentityService identityService = new DefaultIdentityService();
UserIdentity primaryIdentity = identityService.newUserIdentity(primarySubject,
primaryUser, new String[]{"admin", "manager", "user"});
// Create secondary user with different principal but same role checking
UserPrincipal secondaryUser = new UserPrincipal("secondary",
Credential.getCredential("password"));
Subject secondarySubject = new Subject();
// Create role delegate identity
RoleDelegateUserIdentity delegateIdentity = new RoleDelegateUserIdentity(
primaryIdentity, // Delegate role checking to primary
secondarySubject, // But use secondary subject
secondaryUser // And secondary principal
);
// Identity has secondary user's principal
String name = delegateIdentity.getUserPrincipal().getName(); // "secondary"
// But role checking is delegated to primary user
boolean isAdmin = delegateIdentity.isUserInRole("admin"); // true (from primary)
boolean isManager = delegateIdentity.isUserInRole("manager"); // true (from primary)
}
public void createServiceAccountWithUserRoles() {
// Create service account identity
UserPrincipal serviceAccount = new UserPrincipal("api-service",
Credential.getCredential("service-key"));
Subject serviceSubject = new Subject();
// Get user identity to delegate roles from
UserIdentity userIdentity = getCurrentUserIdentity(); // Assume this returns current user
// Create service account that inherits user's roles
RoleDelegateUserIdentity serviceIdentity = new RoleDelegateUserIdentity(
userIdentity, // Delegate to current user's roles
serviceSubject, // Service account subject
serviceAccount // Service account principal
);
// Service account can perform operations with user's roles
try (IdentityService.Association association =
new DefaultIdentityService().associate(serviceIdentity, null)) {
performServiceOperation();
}
}
private UserIdentity getCurrentUserIdentity() {
// Implementation to get current user identity
return null;
}
private void performServiceOperation() {
// Service operation that requires user's roles
}
}public interface UserStore {
// User retrieval
UserPrincipal getUserPrincipal(String username);
List<RolePrincipal> getRolePrincipals(String username);
// User management (optional)
default void addUser(String username, Credential credential, String[] roles) {
throw new UnsupportedOperationException();
}
default void removeUser(String username) {
throw new UnsupportedOperationException();
}
default void updateUser(String username, Credential credential, String[] roles) {
throw new UnsupportedOperationException();
}
}public class CustomIdentityManager {
private final IdentityService identityService;
private final UserStore userStore;
public CustomIdentityManager(IdentityService identityService, UserStore userStore) {
this.identityService = identityService;
this.userStore = userStore;
}
public UserIdentity authenticateUser(String username, String password) {
// Get user principal from store
UserPrincipal userPrincipal = userStore.getUserPrincipal(username);
if (userPrincipal == null) {
return null;
}
// Authenticate credentials
if (!userPrincipal.authenticate(password)) {
return null;
}
// Get user roles
List<RolePrincipal> rolePrincipals = userStore.getRolePrincipals(username);
String[] roles = rolePrincipals.stream()
.map(RolePrincipal::getName)
.toArray(String[]::new);
// Create subject and configure
Subject subject = new Subject();
userPrincipal.configureSubject(subject);
for (RolePrincipal role : rolePrincipals) {
role.configureSubject(subject);
}
// Create user identity
return identityService.newUserIdentity(subject, userPrincipal, roles);
}
public boolean addUser(String username, String password, String... roles) {
try {
// Create credential
Credential credential = Credential.getCredential(password);
// Add to user store
userStore.addUser(username, credential, roles);
return true;
} catch (Exception e) {
logger.error("Failed to add user: " + username, e);
return false;
}
}
public boolean updateUserRoles(String username, String... newRoles) {
try {
// Get existing user
UserPrincipal existing = userStore.getUserPrincipal(username);
if (existing == null) {
return false;
}
// Update with new roles (keeping same credential)
userStore.updateUser(username, existing.getCredential(), newRoles);
return true;
} catch (Exception e) {
logger.error("Failed to update user roles: " + username, e);
return false;
}
}
public void logoutUser(UserIdentity user) {
// Notify identity service of logout
identityService.onLogout(user);
// Additional cleanup if needed
performLogoutCleanup(user);
}
private void performLogoutCleanup(UserIdentity user) {
// Custom logout cleanup logic
}
}public class CompositeIdentityService implements IdentityService {
private final List<IdentityService> delegates;
public CompositeIdentityService(IdentityService... delegates) {
this.delegates = Arrays.asList(delegates);
}
@Override
public Association associate(UserIdentity user, RunAsToken runAsToken) {
// Associate with all delegates
List<Association> associations = delegates.stream()
.map(service -> service.associate(user, runAsToken))
.collect(Collectors.toList());
return new CompositeAssociation(associations);
}
@Override
public UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles) {
// Use first delegate for identity creation
return delegates.get(0).newUserIdentity(subject, userPrincipal, roles);
}
@Override
public void onLogout(UserIdentity user) {
// Notify all delegates
delegates.forEach(service -> service.onLogout(user));
}
@Override
public RunAsToken newRunAsToken(String roleName) {
// Create composite run-as token
List<RunAsToken> tokens = delegates.stream()
.map(service -> service.newRunAsToken(roleName))
.collect(Collectors.toList());
return new CompositeRunAsToken(tokens);
}
@Override
public UserIdentity getSystemUserIdentity() {
// Return system user from first delegate that has one
return delegates.stream()
.map(IdentityService::getSystemUserIdentity)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
private static class CompositeAssociation implements Association {
private final List<Association> associations;
public CompositeAssociation(List<Association> associations) {
this.associations = associations;
}
@Override
public void close() {
associations.forEach(Association::close);
}
}
private static class CompositeRunAsToken implements RunAsToken {
private final List<RunAsToken> tokens;
public CompositeRunAsToken(List<RunAsToken> tokens) {
this.tokens = tokens;
}
}
}public class CachingIdentityService implements IdentityService {
private final IdentityService delegate;
private final Cache<String, UserIdentity> identityCache;
public CachingIdentityService(IdentityService delegate, Duration cacheTTL) {
this.delegate = delegate;
this.identityCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(cacheTTL)
.build();
}
@Override
public UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles) {
String cacheKey = buildCacheKey(userPrincipal, roles);
return identityCache.get(cacheKey, key ->
delegate.newUserIdentity(subject, userPrincipal, roles));
}
@Override
public Association associate(UserIdentity user, RunAsToken runAsToken) {
return delegate.associate(user, runAsToken);
}
@Override
public void onLogout(UserIdentity user) {
// Invalidate cache entries for this user
String username = user.getUserPrincipal().getName();
identityCache.asMap().entrySet().removeIf(entry ->
entry.getKey().startsWith(username + ":"));
delegate.onLogout(user);
}
@Override
public RunAsToken newRunAsToken(String roleName) {
return delegate.newRunAsToken(roleName);
}
@Override
public UserIdentity getSystemUserIdentity() {
return delegate.getSystemUserIdentity();
}
private String buildCacheKey(Principal userPrincipal, String[] roles) {
return userPrincipal.getName() + ":" + Arrays.toString(roles);
}
}public class IdentityBestPractices {
public void configureProductionIdentityService() {
// Use caching for performance
IdentityService baseService = new DefaultIdentityService();
IdentityService cachingService = new CachingIdentityService(baseService, Duration.ofMinutes(15));
// Create secure user store
UserStore secureStore = createSecureUserStore();
// Configure identity manager
CustomIdentityManager identityManager = new CustomIdentityManager(cachingService, secureStore);
// Use with security handler
SecurityHandler security = new SecurityHandler.PathMapped();
security.setIdentityService(cachingService);
}
private UserStore createSecureUserStore() {
// Use property store with secure defaults
PropertyUserStore store = new PropertyUserStore();
store.setConfig(Resource.newResource("secure-users.properties"));
store.setReloadInterval(300); // 5 minutes
return store;
}
public void demonstrateSecureIdentityCreation() {
IdentityService identityService = new DefaultIdentityService();
// Create user with strong password
String strongPassword = generateStrongPassword();
UserPrincipal user = new UserPrincipal("alice",
Credential.getCredential(strongPassword));
// Create subject with proper configuration
Subject subject = new Subject();
user.configureSubject(subject);
// Add role principals to subject
RolePrincipal userRole = new RolePrincipal("user");
RolePrincipal appRole = new RolePrincipal("app-user");
userRole.configureSubject(subject);
appRole.configureSubject(subject);
// Create identity with minimal necessary roles
UserIdentity identity = identityService.newUserIdentity(subject, user,
new String[]{"user", "app-user"});
// Always use try-with-resources for associations
try (IdentityService.Association association =
identityService.associate(identity, null)) {
performUserOperation();
}
}
private String generateStrongPassword() {
// Implementation to generate cryptographically strong password
return "StrongPassword123!";
}
private void performUserOperation() {
// Implementation
}
}User Identity Management provides comprehensive support for user principals, roles, thread associations, and secure identity contexts, enabling fine-grained access control and secure execution environments.
Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-security