CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-eclipse-jetty--jetty-security

Security framework for Eclipse Jetty providing authentication, authorization, and user identity management.

Pending
Overview
Eval results
Files

authentication.mddocs/

Authentication

Jetty Security provides multiple authentication mechanisms to support various security requirements, from simple Basic authentication to complex form-based flows.

Base Authenticator Classes

LoginAuthenticator

Abstract base class for login-based authenticators:

public abstract class LoginAuthenticator implements Authenticator {
    
    // User login and logout
    public UserIdentity login(String username, Object password, Request request, Response response);
    public void logout(Request request, Response response);
    
    // Access to login service
    public LoginService getLoginService();
    
    // Session management
    protected void updateSession(Request httpRequest, Response httpResponse);
    
    // Nested authentication state classes
    public static class UserAuthenticationSucceeded implements AuthenticationState.Succeeded {
        public UserAuthenticationSucceeded(String method, UserIdentity userIdentity);
        
        @Override
        public UserIdentity getUserIdentity();
        
        @Override
        public String getAuthenticationType();
    }
    
    public static class UserAuthenticationSent extends UserAuthenticationSucceeded 
            implements AuthenticationState.ResponseSent {
        public UserAuthenticationSent(String method, UserIdentity userIdentity);
    }
    
    public static class LoggedOutAuthentication implements AuthenticationState.Deferred {
        @Override
        public AuthenticationState authenticate(Request request, Response response, Callback callback);
    }
}

HTTP Basic Authentication

BasicAuthenticator

Simple username/password authentication using HTTP Basic scheme:

public class BasicAuthenticator extends LoginAuthenticator {
    
    // Character encoding configuration
    public Charset getCharset();
    public void setCharset(Charset charset);
    
    @Override
    public String getAuthenticationType() {
        return Authenticator.BASIC_AUTH;
    }
    
    @Override
    public AuthenticationState validateRequest(Request req, Response res, Callback callback) 
            throws ServerAuthException;
    
    // Utility for creating Basic auth headers
    public static String authorization(String user, String password);
}

Basic Authentication Setup

public class BasicAuthExample {
    
    public void setupBasicAuthentication() {
        SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
        
        // Configure login service
        HashLoginService loginService = new HashLoginService("MyRealm");
        loginService.setConfig(Resource.newResource("users.properties"));
        security.setLoginService(loginService);
        
        // Create and configure Basic authenticator
        BasicAuthenticator basicAuth = new BasicAuthenticator();
        basicAuth.setCharset(StandardCharsets.UTF_8);
        security.setAuthenticator(basicAuth);
        
        // Set realm name
        security.setRealmName("MyRealm");
        
        // Define protected areas
        security.put("/api/*", Constraint.from("user"));
        security.put("/admin/*", Constraint.from("admin"));
    }
    
    public void createBasicAuthHeader() {
        // Create authorization header value
        String authHeader = BasicAuthenticator.authorization("john", "password");
        
        // Use in HTTP client
        HttpClient client = new HttpClient();
        Request request = client.newRequest("https://example.com/api/data");
        request.header(HttpHeader.AUTHORIZATION, authHeader);
    }
}

Form-Based Authentication

FormAuthenticator

Web form-based authentication with custom login pages:

public class FormAuthenticator extends LoginAuthenticator {
    
    // Form authentication constants
    public static final String __FORM_LOGIN_PAGE = "org.eclipse.jetty.security.form_login_page";
    public static final String __FORM_ERROR_PAGE = "org.eclipse.jetty.security.form_error_page";
    public static final String __FORM_DISPATCH = "org.eclipse.jetty.security.dispatch";
    public static final String __J_SECURITY_CHECK = "/j_security_check";
    public static final String __J_USERNAME = "j_username";
    public static final String __J_PASSWORD = "j_password";
    
    // Constructors
    public FormAuthenticator();
    public FormAuthenticator(String login, String error, boolean dispatch);
    
    // Configuration
    public void setAlwaysSaveUri(boolean alwaysSave);
    public boolean getAlwaysSaveUri();
    
    @Override
    public String getAuthenticationType() {
        return Authenticator.FORM_AUTH;
    }
    
    // Security check methods
    public boolean isJSecurityCheck(String uri);
    public boolean isLoginOrErrorPage(String pathInContext);
    
    @Override
    public AuthenticationState validateRequest(Request req, Response res, Callback callback) 
            throws ServerAuthException;
}

Form Authentication Setup

public class FormAuthExample {
    
    public void setupFormAuthentication() {
        SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
        
        // Configure login service
        HashLoginService loginService = new HashLoginService("FormRealm");
        loginService.setConfig(Resource.newResource("users.properties"));
        security.setLoginService(loginService);
        
        // Create form authenticator
        FormAuthenticator formAuth = new FormAuthenticator(
            "/login.html",      // Login page
            "/login-error.html", // Error page
            false              // Don't dispatch
        );
        formAuth.setAlwaysSaveUri(true); // Save original URI for redirect
        security.setAuthenticator(formAuth);
        
        // Security configuration
        security.setRealmName("FormRealm");
        security.setSessionRenewedOnAuthentication(true);
        
        // Constraints
        security.put("/login.html", Constraint.ALLOWED);
        security.put("/login-error.html", Constraint.ALLOWED);
        security.put("/css/*", Constraint.ALLOWED);
        security.put("/js/*", Constraint.ALLOWED);
        security.put("/app/*", Constraint.ANY_USER);
    }
}

Custom Login Pages

<!-- login.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <h2>Please Login</h2>
    <form method="post" action="j_security_check">
        <table>
            <tr>
                <td>Username:</td>
                <td><input type="text" name="j_username"/></td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type="password" name="j_password"/></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="Login"/></td>
            </tr>
        </table>
    </form>
</body>
</html>

HTTP Digest Authentication

DigestAuthenticator

More secure than Basic auth, using challenge-response mechanism:

public class DigestAuthenticator extends LoginAuthenticator {
    
    @Override
    public String getAuthenticationType() {
        return Authenticator.DIGEST_AUTH;
    }
    
    // Nonce configuration for security
    public int getMaxNonceAge();
    public void setMaxNonceAge(int maxNonceAge);
    
    public long getNonceSecret();
    public void setNonceSecret(long nonceSecret);
    
    @Override
    public AuthenticationState validateRequest(Request req, Response res, Callback callback) 
            throws ServerAuthException;
}

Digest Authentication Setup

public class DigestAuthExample {
    
    public void setupDigestAuthentication() {
        SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
        
        // Configure login service with digest-compatible credentials
        HashLoginService loginService = new HashLoginService("DigestRealm");
        loginService.setConfig(Resource.newResource("digest-users.properties"));
        security.setLoginService(loginService);
        
        // Create and configure Digest authenticator
        DigestAuthenticator digestAuth = new DigestAuthenticator();
        digestAuth.setMaxNonceAge(60000);  // 1 minute nonce lifetime
        digestAuth.setNonceSecret(System.currentTimeMillis());
        security.setAuthenticator(digestAuth);
        
        security.setRealmName("DigestRealm");
        
        // Protected areas
        security.put("/secure/*", Constraint.ANY_USER);
    }
}

SSL Client Certificate Authentication

SslClientCertAuthenticator

Authentication using SSL client certificates:

public class SslClientCertAuthenticator extends LoginAuthenticator {
    
    @Override
    public String getAuthenticationType() {
        return Authenticator.CERT_AUTH;
    }
    
    // Certificate validation configuration
    public boolean isValidateCerts();
    public void setValidateCerts(boolean validateCerts);
    
    @Override
    public AuthenticationState validateRequest(Request req, Response res, Callback callback) 
            throws ServerAuthException;
}

Client Certificate Setup

public class ClientCertExample {
    
    public void setupClientCertAuth() {
        SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
        
        // Special login service for certificate authentication
        CertificateLoginService loginService = new CertificateLoginService();
        loginService.setName("CertRealm");
        security.setLoginService(loginService);
        
        // Certificate authenticator
        SslClientCertAuthenticator certAuth = new SslClientCertAuthenticator();
        certAuth.setValidateCerts(true); // Validate certificate chain
        security.setAuthenticator(certAuth);
        
        security.setRealmName("CertRealm");
        
        // All access requires valid certificate
        security.put("/*", Constraint.ANY_USER);
        
        // Admin areas require specific certificate roles
        security.put("/admin/*", Constraint.from("admin-cert"));
    }
    
    // Custom login service for certificates
    public static class CertificateLoginService extends AbstractLoginService {
        
        @Override
        protected UserPrincipal loadUserInfo(String username) {
            // Load user based on certificate subject
            return findUserByCertificateSubject(username);
        }
        
        @Override
        protected List<RolePrincipal> loadRoleInfo(UserPrincipal user) {
            // Load roles based on certificate attributes
            return getCertificateRoles(user);
        }
        
        private UserPrincipal findUserByCertificateSubject(String subject) {
            // Implementation to map certificate subject to user
            return null;
        }
        
        private List<RolePrincipal> getCertificateRoles(UserPrincipal user) {
            // Implementation to determine roles from certificate
            return new ArrayList<>();
        }
    }
}

SPNEGO Authentication

SPNEGOAuthenticator

Kerberos-based authentication for enterprise environments:

public class SPNEGOAuthenticator extends LoginAuthenticator {
    
    @Override
    public String getAuthenticationType() {
        return Authenticator.SPNEGO_AUTH;
    }
    
    @Override
    public AuthenticationState validateRequest(Request req, Response res, Callback callback) 
            throws ServerAuthException;
}

SPNEGOUserPrincipal

Principal class for SPNEGO-authenticated users that carries encoded authentication tokens:

public class SPNEGOUserPrincipal implements Principal {
    
    // Constructor
    public SPNEGOUserPrincipal(String name, String encodedToken);
    
    // Principal interface
    @Override
    public String getName();
    
    // SPNEGO-specific access
    public String getEncodedToken();
}

SPNEGO Setup

public class SPNEGOExample {
    
    public void setupSPNEGOAuthentication() {
        SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
        
        // SPNEGO login service
        SPNEGOLoginService spnegoService = new SPNEGOLoginService();
        spnegoService.setName("SPNEGO");
        // Configure Kerberos settings via system properties or JAAS config
        security.setLoginService(spnegoService);
        
        // SPNEGO authenticator
        SPNEGOAuthenticator spnegoAuth = new SPNEGOAuthenticator();
        security.setAuthenticator(spnegoAuth);
        
        security.setRealmName("KERBEROS");
        
        // Enterprise application areas
        security.put("/app/*", Constraint.ANY_USER);
        security.put("/admin/*", Constraint.from("Domain Admins"));
    }
}

Session Authentication

SessionAuthentication

Manages authentication state in HTTP sessions:

public class SessionAuthentication implements AuthenticationState.Succeeded, Session.ValueListener {
    
    // Session attribute for authenticated user
    public static final String AUTHENTICATED_ATTRIBUTE = "org.eclipse.jetty.security.UserIdentity";
    
    // Constructor
    public SessionAuthentication(String method, UserIdentity userIdentity, Object credentials);
    
    @Override
    public UserIdentity getUserIdentity();
    
    @Override
    public String getAuthenticationType();
    
    // Session lifecycle methods
    @Override
    public void valueSet(Session session, String name, Object newValue, Object oldValue);
    
    @Override
    public void valueRemoved(Session session, String name, Object oldValue);
}

Custom Authenticator Implementation

Creating Custom Authenticators

public class TokenAuthenticator extends LoginAuthenticator {
    private final String tokenHeader;
    private final TokenValidator tokenValidator;
    
    public TokenAuthenticator(String tokenHeader, TokenValidator validator) {
        this.tokenHeader = tokenHeader;
        this.tokenValidator = validator;
    }
    
    @Override
    public String getAuthenticationType() {
        return "TOKEN";
    }
    
    @Override
    public AuthenticationState validateRequest(Request request, Response response, Callback callback) 
            throws ServerAuthException {
        
        // Extract token from header
        String token = request.getHeaders().get(tokenHeader);
        if (token == null || token.isEmpty()) {
            return sendChallenge(response, callback);
        }
        
        try {
            // Validate token
            TokenInfo tokenInfo = tokenValidator.validate(token);
            if (tokenInfo == null) {
                return AuthenticationState.SEND_FAILURE;
            }
            
            // Create user identity
            UserIdentity user = createUserIdentity(tokenInfo);
            if (user == null) {
                return AuthenticationState.SEND_FAILURE;
            }
            
            return new UserAuthenticationSucceeded(getAuthenticationType(), user);
            
        } catch (Exception e) {
            throw new ServerAuthException("Token validation failed", e);
        }
    }
    
    private AuthenticationState sendChallenge(Response response, Callback callback) {
        response.setStatus(HttpStatus.UNAUTHORIZED_401);
        response.getHeaders().put("WWW-Authenticate", "Token realm=\"" + getConfiguration().getRealmName() + "\"");
        callback.succeeded();
        return AuthenticationState.CHALLENGE;
    }
    
    private UserIdentity createUserIdentity(TokenInfo tokenInfo) {
        // Create user identity from token information
        Subject subject = new Subject();
        UserPrincipal userPrincipal = new UserPrincipal(tokenInfo.getUsername(), Credential.getCredential(""));
        String[] roles = tokenInfo.getRoles().toArray(new String[0]);
        
        return getConfiguration().getIdentityService().newUserIdentity(subject, userPrincipal, roles);
    }
    
    // Token validation interface
    public interface TokenValidator {
        TokenInfo validate(String token) throws Exception;
    }
    
    // Token information class
    public static class TokenInfo {
        private final String username;
        private final List<String> roles;
        private final long expiration;
        
        public TokenInfo(String username, List<String> roles, long expiration) {
            this.username = username;
            this.roles = roles;
            this.expiration = expiration;
        }
        
        public String getUsername() { return username; }
        public List<String> getRoles() { return roles; }
        public long getExpiration() { return expiration; }
        public boolean isExpired() { return System.currentTimeMillis() > expiration; }
    }
}

JWT Token Authenticator

public class JWTAuthenticator extends TokenAuthenticator {
    
    public JWTAuthenticator(String secretKey) {
        super("Authorization", new JWTTokenValidator(secretKey));
    }
    
    @Override
    public String getAuthenticationType() {
        return "JWT";
    }
    
    private static class JWTTokenValidator implements TokenValidator {
        private final String secretKey;
        
        public JWTTokenValidator(String secretKey) {
            this.secretKey = secretKey;
        }
        
        @Override
        public TokenInfo validate(String token) throws Exception {
            // Remove "Bearer " prefix if present
            if (token.startsWith("Bearer ")) {
                token = token.substring(7);
            }
            
            // JWT validation logic (using a JWT library)
            // This is a simplified example
            JWTClaims claims = parseAndValidateJWT(token, secretKey);
            
            String username = claims.getSubject();
            List<String> roles = claims.getRoles();
            long expiration = claims.getExpiration();
            
            return new TokenInfo(username, roles, expiration);
        }
        
        private JWTClaims parseAndValidateJWT(String token, String secret) throws Exception {
            // JWT parsing and validation implementation
            // Would typically use a library like jose4j or jsonwebtoken
            throw new UnsupportedOperationException("JWT parsing implementation required");
        }
    }
    
    private static class JWTClaims {
        public String getSubject() { return null; }
        public List<String> getRoles() { return Collections.emptyList(); }
        public long getExpiration() { return 0; }
    }
}

Multi-Factor Authentication

Multi-Factor Authenticator Wrapper

public class MultiFactorAuthenticator implements Authenticator {
    private final List<Authenticator> authenticators;
    private final boolean requireAll;
    
    public MultiFactorAuthenticator(boolean requireAll, Authenticator... authenticators) {
        this.requireAll = requireAll;
        this.authenticators = Arrays.asList(authenticators);
    }
    
    @Override
    public void setConfiguration(Configuration configuration) {
        authenticators.forEach(auth -> auth.setConfiguration(configuration));
    }
    
    @Override
    public String getAuthenticationType() {
        return "MULTI_FACTOR";
    }
    
    @Override
    public AuthenticationState validateRequest(Request request, Response response, Callback callback) 
            throws ServerAuthException {
        
        List<AuthenticationState.Succeeded> successes = new ArrayList<>();
        
        for (Authenticator authenticator : authenticators) {
            AuthenticationState state = authenticator.validateRequest(request, response, callback);
            
            if (state instanceof AuthenticationState.Succeeded) {
                successes.add((AuthenticationState.Succeeded) state);
            } else if (requireAll) {
                return state; // Fail if any required factor fails
            }
        }
        
        // Check if we have enough successful authentications
        if (requireAll && successes.size() != authenticators.size()) {
            return AuthenticationState.SEND_FAILURE;
        } else if (!requireAll && successes.isEmpty()) {
            return AuthenticationState.SEND_FAILURE;
        }
        
        // Combine successful authentications
        UserIdentity combinedUser = combineUserIdentities(successes);
        return new LoginAuthenticator.UserAuthenticationSucceeded(getAuthenticationType(), combinedUser);
    }
    
    private UserIdentity combineUserIdentities(List<AuthenticationState.Succeeded> successes) {
        // Logic to combine multiple user identities
        // Could merge roles, use primary identity, etc.
        AuthenticationState.Succeeded primary = successes.get(0);
        return primary.getUserIdentity();
    }
}

Authentication Error Handling

Comprehensive Error Handling

public class AuthenticationErrorHandling {
    
    public void handleAuthenticationErrors(Authenticator authenticator, 
            Request request, Response response, Callback callback) {
        
        try {
            AuthenticationState state = authenticator.validateRequest(request, response, callback);
            
            if (state instanceof AuthenticationState.Succeeded) {
                // Authentication successful
                AuthenticationState.Succeeded success = (AuthenticationState.Succeeded) state;
                logSuccessfulAuthentication(success);
                
            } else if (state == AuthenticationState.CHALLENGE) {
                // Challenge sent to client
                logAuthenticationChallenge(request);
                
            } else if (state == AuthenticationState.SEND_FAILURE) {
                // Authentication failed
                logAuthenticationFailure(request);
                
            } else if (state instanceof AuthenticationState.Deferred) {
                // Deferred authentication
                logDeferredAuthentication(request);
            }
            
        } catch (ServerAuthException e) {
            handleServerAuthException(e, request, response);
        }
    }
    
    private void logSuccessfulAuthentication(AuthenticationState.Succeeded success) {
        logger.info("Authentication successful: user={}, type={}", 
            success.getUserIdentity().getUserPrincipal().getName(),
            success.getAuthenticationType());
    }
    
    private void logAuthenticationChallenge(Request request) {
        logger.debug("Authentication challenge sent to client: {}", 
            Request.getRemoteAddr(request));
    }
    
    private void logAuthenticationFailure(Request request) {
        logger.warn("Authentication failed for client: {}", 
            Request.getRemoteAddr(request));
    }
    
    private void logDeferredAuthentication(Request request) {
        logger.debug("Authentication deferred for request: {}", 
            request.getHttpURI().getPath());
    }
    
    private void handleServerAuthException(ServerAuthException e, Request request, Response response) {
        logger.error("Server authentication exception for client: {} - {}", 
            Request.getRemoteAddr(request), e.getMessage());
        
        if (!response.isCommitted()) {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
            try {
                response.getWriter().write("Authentication service unavailable");
            } catch (IOException ioE) {
                logger.error("Error writing authentication error response", ioE);
            }
        }
    }
}

Performance Optimization

Caching and Optimization

public class AuthenticationOptimization {
    
    // Cached authenticator for frequently accessed resources
    public static class CachingAuthenticator implements Authenticator {
        private final Authenticator delegate;
        private final Cache<String, AuthenticationState> cache;
        
        public CachingAuthenticator(Authenticator delegate, Duration cacheTTL) {
            this.delegate = delegate;
            this.cache = Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(cacheTTL)
                .build();
        }
        
        @Override
        public void setConfiguration(Configuration configuration) {
            delegate.setConfiguration(configuration);
        }
        
        @Override
        public String getAuthenticationType() {
            return delegate.getAuthenticationType();
        }
        
        @Override
        public AuthenticationState validateRequest(Request request, Response response, Callback callback) 
                throws ServerAuthException {
            
            String cacheKey = buildCacheKey(request);
            if (cacheKey != null) {
                AuthenticationState cached = cache.getIfPresent(cacheKey);
                if (cached instanceof AuthenticationState.Succeeded) {
                    return cached;
                }
            }
            
            AuthenticationState result = delegate.validateRequest(request, response, callback);
            
            if (result instanceof AuthenticationState.Succeeded && cacheKey != null) {
                cache.put(cacheKey, result);
            }
            
            return result;
        }
        
        private String buildCacheKey(Request request) {
            // Build cache key from authentication credentials
            String authHeader = request.getHeaders().get(HttpHeader.AUTHORIZATION);
            return authHeader != null ? authHeader.hashCode() + "" : null;
        }
    }
}

Authentication in Jetty Security provides flexible, secure, and performant solutions for various authentication requirements, from simple Basic auth to complex multi-factor scenarios.

Install with Tessl CLI

npx tessl i tessl/maven-org-eclipse-jetty--jetty-security

docs

authentication.md

index.md

jaas.md

login-services.md

security-framework.md

user-identity.md

tile.json