Security framework for Eclipse Jetty providing authentication, authorization, and user identity management.
—
The core security framework provides the foundational interfaces and handlers for authentication and authorization in Jetty applications.
The SecurityHandler is the central component that orchestrates security for web applications.
public abstract class SecurityHandler extends Handler.Abstract {
// Identity and authentication services
public IdentityService getIdentityService();
public void setIdentityService(IdentityService identityService);
public LoginService getLoginService();
public void setLoginService(LoginService loginService);
public Authenticator getAuthenticator();
public void setAuthenticator(Authenticator authenticator);
public Authenticator.Factory getAuthenticatorFactory();
public void setAuthenticatorFactory(Authenticator.Factory authenticatorFactory);
// Realm configuration
public String getRealmName();
public void setRealmName(String realmName);
public String getAuthenticationType();
public void setAuthenticationType(String authenticationType);
// Parameter management
public String getParameter(String key);
public String setParameter(String key, String value);
// Session configuration
public boolean isSessionRenewedOnAuthentication();
public void setSessionRenewedOnAuthentication(boolean renew);
public int getSessionMaxInactiveIntervalOnAuthentication();
public void setSessionMaxInactiveIntervalOnAuthentication(int seconds);
// Abstract method for constraint resolution
protected abstract Constraint getConstraint(String pathInContext, Request request);
// Static utility
public static SecurityHandler getCurrentSecurityHandler();
}The concrete implementation using path mappings for constraints:
public static class PathMapped extends SecurityHandler {
// Path-based constraint mapping
public void put(String pathSpec, Constraint constraint);
public Constraint get(String pathSpec);
public boolean remove(String pathSpec);
// Bulk operations
public void putAll(Map<String, Constraint> constraints);
public void clear();
@Override
protected Constraint getConstraint(String pathInContext, Request request) {
// Implementation resolves constraint based on path mapping
}
}Defines security requirements for resources:
public interface Constraint {
// Basic properties
String getName();
Transport getTransport();
Authorization getAuthorization();
Set<String> getRoles();
// Authorization levels
enum Authorization {
FORBIDDEN, // Access denied to all
ALLOWED, // Access allowed to all
ANY_USER, // Any authenticated user
KNOWN_ROLE, // User must have at least one role
SPECIFIC_ROLE, // User must have specific role(s)
INHERIT // Inherit from parent constraint
}
// Transport requirements
enum Transport {
SECURE, // HTTPS required
ANY, // HTTP or HTTPS
INHERIT // Inherit from parent constraint
}
// Factory methods
static Constraint from(String... roles);
static Constraint from(String name, Transport transport);
static Constraint from(String name, Authorization authorization, String... roles);
static Constraint combine(Constraint leastSpecific, Constraint mostSpecific);
}public class ConstraintExamples {
public void demonstrateConstraints() {
// Allow all access
Constraint allowAll = Constraint.ALLOWED;
// Deny all access
Constraint denyAll = Constraint.FORBIDDEN;
// Require any authenticated user
Constraint anyUser = Constraint.ANY_USER;
// Require specific roles
Constraint adminOnly = Constraint.from("admin");
Constraint userOrMod = Constraint.from("user", "moderator");
// Require HTTPS transport
Constraint secureOnly = Constraint.SECURE_TRANSPORT;
// Combined constraints
Constraint secureAdmin = Constraint.from("SecureAdmin",
Constraint.Transport.SECURE, "admin");
}
}Represents the current authentication status of a request:
public interface AuthenticationState {
// Static utility methods
static AuthenticationState getAuthenticationState(Request request);
static void setAuthenticationState(Request request, AuthenticationState authenticationState);
static Principal getUserPrincipal(Request request);
static Succeeded authenticate(Request request);
static Succeeded authenticate(Request request, Response response, Callback callback);
static Succeeded login(String username, String password, Request request, Response response);
static boolean logout(Request request, Response response);
// Nested state interfaces
interface Succeeded extends AuthenticationState {
UserIdentity getUserIdentity();
String getAuthenticationType();
}
interface ResponseSent extends AuthenticationState {
// Marker interface for responses that were sent
}
interface Deferred extends AuthenticationState {
AuthenticationState authenticate(Request request, Response response, Callback callback);
}
}public class AuthenticationStates {
public void handleAuthenticationStates(AuthenticationState state) {
// Challenge was sent to client
if (state == AuthenticationState.CHALLENGE) {
// Client needs to provide credentials
}
// Failure response was sent
else if (state == AuthenticationState.SEND_FAILURE) {
// Authentication failed, error sent
}
// Success response was sent
else if (state == AuthenticationState.SEND_SUCCESS) {
// Authentication succeeded, response sent
}
// Successful authentication
else if (state instanceof AuthenticationState.Succeeded) {
AuthenticationState.Succeeded success = (AuthenticationState.Succeeded) state;
UserIdentity user = success.getUserIdentity();
String authType = success.getAuthenticationType();
}
// Deferred authentication
else if (state instanceof AuthenticationState.Deferred) {
AuthenticationState.Deferred deferred = (AuthenticationState.Deferred) state;
// Authentication will be handled later
}
}
}public interface Authenticator {
// Authentication type constants
String BASIC_AUTH = "BASIC";
String FORM_AUTH = "FORM";
String DIGEST_AUTH = "DIGEST";
String CERT_AUTH = "CLIENT_CERT";
String CERT_AUTH2 = "CLIENT-CERT";
String SPNEGO_AUTH = "SPNEGO";
String NEGOTIATE_AUTH = "NEGOTIATE";
String OPENID_AUTH = "OPENID";
// Configuration and identification
void setConfiguration(Configuration configuration);
String getAuthenticationType();
// Request preparation and validation
default Request prepareRequest(Request request, AuthenticationState authenticationState) {
return request;
}
default Constraint.Authorization getConstraintAuthentication(String pathInContext,
Constraint.Authorization existing, Function<Boolean, Session> getSession) {
return existing == null ? Constraint.Authorization.ALLOWED : existing;
}
AuthenticationState validateRequest(Request request, Response response, Callback callback)
throws ServerAuthException;
// Nested configuration interface
interface Configuration {
String getRealmName();
String getParameter(String key);
LoginService getLoginService();
IdentityService getIdentityService();
}
// Factory interface
interface Factory {
Authenticator getAuthenticator(Server server, Context context, Configuration configuration);
}
}Default implementation of the Authenticator.Factory interface that creates authenticators based on configuration:
public class DefaultAuthenticatorFactory implements Authenticator.Factory {
@Override
public Authenticator getAuthenticator(Server server, Context context, Configuration configuration);
}The DefaultAuthenticatorFactory creates authenticators based on the configured authentication type:
BASIC → BasicAuthenticatorDIGEST → DigestAuthenticatorFORM → FormAuthenticatorCLIENT-CERT → SslClientCertAuthenticator (requires single SslContextFactory)SPNEGO/NEGOTIATE → SPNEGOAuthenticatorLoginAuthenticator instances are wrapped with DeferredAuthenticationState for non-mandatory authentication scenarios.
Exception class for server-side authentication and authorization failures:
public class ServerAuthException extends GeneralSecurityException {
// Constructors
public ServerAuthException();
public ServerAuthException(String message);
public ServerAuthException(String message, Throwable cause);
public ServerAuthException(Throwable cause);
}Thrown by authenticators during request validation when authentication or authorization fails.
public class BasicSecuritySetup {
public SecurityHandler createBasicSecurity() {
SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
// Configure realm
security.setRealmName("MyApplication");
// Set up login service
HashLoginService loginService = new HashLoginService();
loginService.setName("MyApplication");
loginService.setConfig(Resource.newResource("users.properties"));
security.setLoginService(loginService);
// Configure authenticator
BasicAuthenticator authenticator = new BasicAuthenticator();
security.setAuthenticator(authenticator);
// Define constraints
security.put("/public/*", Constraint.ALLOWED);
security.put("/users/*", Constraint.ANY_USER);
security.put("/admin/*", Constraint.from("admin"));
security.put("/api/secure/*", Constraint.from("SecureAPI",
Constraint.Transport.SECURE, "api-user"));
return security;
}
}public class AdvancedSecurityConfig {
public void configureAdvancedSecurity(SecurityHandler security) {
// Identity service for thread association
DefaultIdentityService identityService = new DefaultIdentityService();
security.setIdentityService(identityService);
// Session security settings
security.setSessionRenewedOnAuthentication(true);
security.setSessionMaxInactiveIntervalOnAuthentication(1800); // 30 minutes
// Custom authenticator factory
security.setAuthenticatorFactory(new DefaultAuthenticatorFactory());
// Parameters for authenticators
security.setParameter("charset", "UTF-8");
security.setParameter("realmName", "SecureRealm");
// Complex constraint combinations
setupComplexConstraints((SecurityHandler.PathMapped) security);
}
private void setupComplexConstraints(SecurityHandler.PathMapped security) {
// Public resources - no authentication required
security.put("/css/*", Constraint.ALLOWED);
security.put("/js/*", Constraint.ALLOWED);
security.put("/images/*", Constraint.ALLOWED);
security.put("/favicon.ico", Constraint.ALLOWED);
// API endpoints with role-based access
security.put("/api/public/*", Constraint.ALLOWED);
security.put("/api/users/*", Constraint.ANY_USER);
security.put("/api/admin/*", Constraint.from("admin", "super-admin"));
// Secure administrative areas requiring HTTPS
security.put("/admin/*", Constraint.from("AdminArea",
Constraint.Transport.SECURE, "admin"));
security.put("/config/*", Constraint.from("ConfigArea",
Constraint.Transport.SECURE, "super-admin"));
// User-specific areas
security.put("/user/*", Constraint.from("user", "premium-user"));
security.put("/premium/*", Constraint.from("premium-user"));
}
}public class CustomConstraintExample {
public static class TimeBasedConstraint implements Constraint {
private final Constraint base;
private final int startHour;
private final int endHour;
public TimeBasedConstraint(Constraint base, int startHour, int endHour) {
this.base = base;
this.startHour = startHour;
this.endHour = endHour;
}
@Override
public String getName() {
return "TimeBased-" + base.getName();
}
@Override
public Transport getTransport() {
return base.getTransport();
}
@Override
public Authorization getAuthorization() {
int hour = LocalTime.now().getHour();
if (hour >= startHour && hour < endHour) {
return base.getAuthorization();
}
return Authorization.FORBIDDEN;
}
@Override
public Set<String> getRoles() {
return base.getRoles();
}
}
public void useTimeBasedConstraints(SecurityHandler.PathMapped security) {
// Business hours only access (9 AM to 6 PM)
Constraint businessHours = new TimeBasedConstraint(
Constraint.from("business-user"), 9, 18);
security.put("/business/*", businessHours);
// After-hours admin access
Constraint afterHours = new TimeBasedConstraint(
Constraint.from("admin"), 18, 9);
security.put("/maintenance/*", afterHours);
}
}public class CustomSecurityHandler extends SecurityHandler {
private final Map<String, Constraint> dynamicConstraints = new ConcurrentHashMap<>();
@Override
protected Constraint getConstraint(String pathInContext, Request request) {
// Check dynamic constraints first
Constraint dynamic = dynamicConstraints.get(pathInContext);
if (dynamic != null) {
return dynamic;
}
// Check user-specific constraints
UserIdentity user = getCurrentUser(request);
if (user != null && user.isUserInRole("super-admin")) {
return Constraint.ALLOWED; // Super admins bypass all constraints
}
// Default to forbidden for unknown paths
return Constraint.FORBIDDEN;
}
public void addDynamicConstraint(String path, Constraint constraint) {
dynamicConstraints.put(path, constraint);
}
public void removeDynamicConstraint(String path) {
dynamicConstraints.remove(path);
}
private UserIdentity getCurrentUser(Request request) {
AuthenticationState auth = AuthenticationState.getAuthenticationState(request);
if (auth instanceof AuthenticationState.Succeeded) {
return ((AuthenticationState.Succeeded) auth).getUserIdentity();
}
return null;
}
}public class SecurityErrorHandling {
public void handleSecurityErrors(SecurityHandler security) {
try {
// Security operations that might fail
Constraint constraint = security.getConstraint("/protected", request);
} catch (ServerAuthException e) {
// Handle authentication/authorization errors
logger.error("Security error: " + e.getMessage(), e);
// Send appropriate error response
response.setStatus(HttpStatus.UNAUTHORIZED_401);
response.getHeaders().put(HttpHeader.WWW_AUTHENTICATE,
"Basic realm=\"" + security.getRealmName() + "\"");
}
}
public void customErrorHandling(Request request, Response response, Throwable error) {
if (error instanceof ServerAuthException) {
ServerAuthException authError = (ServerAuthException) error;
// Log security violations
logger.warn("Authentication failure for user: {} from IP: {}",
request.getHeaders().get("X-User"),
Request.getRemoteAddr(request));
// Custom error page
response.setStatus(HttpStatus.FORBIDDEN_403);
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/html");
try {
response.getWriter().write("<html><body>" +
"<h1>Access Denied</h1>" +
"<p>You do not have permission to access this resource.</p>" +
"</body></html>");
} catch (IOException e) {
logger.error("Error writing security error page", e);
}
}
}
}public class SecurityBestPractices {
public SecurityHandler createProductionSecurity() {
SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
// Always use strong realm names
security.setRealmName("ProductionApp-" + UUID.randomUUID().toString());
// Enable session renewal on authentication
security.setSessionRenewedOnAuthentication(true);
// Set reasonable session timeout
security.setSessionMaxInactiveIntervalOnAuthentication(1800); // 30 minutes
// Secure transport for sensitive operations
security.put("/login/*", Constraint.from("Login",
Constraint.Transport.SECURE));
security.put("/admin/*", Constraint.from("Admin",
Constraint.Transport.SECURE, "admin"));
security.put("/api/payment/*", Constraint.from("Payment",
Constraint.Transport.SECURE, "payment-user"));
return security;
}
public void secureDefaultsSetup(SecurityHandler security) {
// Deny by default - explicit allow only
security.put("/*", Constraint.FORBIDDEN);
// Explicitly allow public resources
security.put("/public/*", Constraint.ALLOWED);
security.put("/health", Constraint.ALLOWED);
security.put("/status", Constraint.ALLOWED);
// Authenticated areas
security.put("/app/*", Constraint.ANY_USER);
security.put("/user/*", Constraint.ANY_USER);
// Role-based areas
security.put("/admin/*", Constraint.from("admin"));
security.put("/reports/*", Constraint.from("admin", "manager"));
}
}Utility class for configuration resource management:
public abstract class Resource {
// Factory methods for creating resources
public static Resource newResource(String resource);
public static Resource newClassPathResource(String name);
public static Resource newResource(URI uri);
public static Resource newResource(File file);
// Resource properties
public abstract boolean exists();
public abstract boolean isDirectory();
public abstract String getName();
public abstract URI getURI();
}The Resource class provides abstracted access to configuration files, supporting filesystem paths, classpath resources, and URIs. Commonly used for specifying login service configuration files.
The Security Framework provides the foundation for all authentication and authorization in Jetty applications, offering flexible constraint definition, pluggable authenticators, and comprehensive security state management.
Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-security