0
# Security Framework
1
2
The core security framework provides the foundational interfaces and handlers for authentication and authorization in Jetty applications.
3
4
## Core Interfaces
5
6
### SecurityHandler
7
8
The `SecurityHandler` is the central component that orchestrates security for web applications.
9
10
```java { .api }
11
public abstract class SecurityHandler extends Handler.Abstract {
12
13
// Identity and authentication services
14
public IdentityService getIdentityService();
15
public void setIdentityService(IdentityService identityService);
16
17
public LoginService getLoginService();
18
public void setLoginService(LoginService loginService);
19
20
public Authenticator getAuthenticator();
21
public void setAuthenticator(Authenticator authenticator);
22
23
public Authenticator.Factory getAuthenticatorFactory();
24
public void setAuthenticatorFactory(Authenticator.Factory authenticatorFactory);
25
26
// Realm configuration
27
public String getRealmName();
28
public void setRealmName(String realmName);
29
30
public String getAuthenticationType();
31
public void setAuthenticationType(String authenticationType);
32
33
// Parameter management
34
public String getParameter(String key);
35
public String setParameter(String key, String value);
36
37
// Session configuration
38
public boolean isSessionRenewedOnAuthentication();
39
public void setSessionRenewedOnAuthentication(boolean renew);
40
41
public int getSessionMaxInactiveIntervalOnAuthentication();
42
public void setSessionMaxInactiveIntervalOnAuthentication(int seconds);
43
44
// Abstract method for constraint resolution
45
protected abstract Constraint getConstraint(String pathInContext, Request request);
46
47
// Static utility
48
public static SecurityHandler getCurrentSecurityHandler();
49
}
50
```
51
52
### PathMapped SecurityHandler
53
54
The concrete implementation using path mappings for constraints:
55
56
```java { .api }
57
public static class PathMapped extends SecurityHandler {
58
// Path-based constraint mapping
59
public void put(String pathSpec, Constraint constraint);
60
public Constraint get(String pathSpec);
61
public boolean remove(String pathSpec);
62
63
// Bulk operations
64
public void putAll(Map<String, Constraint> constraints);
65
public void clear();
66
67
@Override
68
protected Constraint getConstraint(String pathInContext, Request request) {
69
// Implementation resolves constraint based on path mapping
70
}
71
}
72
```
73
74
## Constraint System
75
76
### Constraint Interface
77
78
Defines security requirements for resources:
79
80
```java { .api }
81
public interface Constraint {
82
83
// Basic properties
84
String getName();
85
Transport getTransport();
86
Authorization getAuthorization();
87
Set<String> getRoles();
88
89
// Authorization levels
90
enum Authorization {
91
FORBIDDEN, // Access denied to all
92
ALLOWED, // Access allowed to all
93
ANY_USER, // Any authenticated user
94
KNOWN_ROLE, // User must have at least one role
95
SPECIFIC_ROLE, // User must have specific role(s)
96
INHERIT // Inherit from parent constraint
97
}
98
99
// Transport requirements
100
enum Transport {
101
SECURE, // HTTPS required
102
ANY, // HTTP or HTTPS
103
INHERIT // Inherit from parent constraint
104
}
105
106
// Factory methods
107
static Constraint from(String... roles);
108
static Constraint from(String name, Transport transport);
109
static Constraint from(String name, Authorization authorization, String... roles);
110
static Constraint combine(Constraint leastSpecific, Constraint mostSpecific);
111
}
112
```
113
114
### Predefined Constraints
115
116
```java { .api }
117
public class ConstraintExamples {
118
119
public void demonstrateConstraints() {
120
// Allow all access
121
Constraint allowAll = Constraint.ALLOWED;
122
123
// Deny all access
124
Constraint denyAll = Constraint.FORBIDDEN;
125
126
// Require any authenticated user
127
Constraint anyUser = Constraint.ANY_USER;
128
129
// Require specific roles
130
Constraint adminOnly = Constraint.from("admin");
131
Constraint userOrMod = Constraint.from("user", "moderator");
132
133
// Require HTTPS transport
134
Constraint secureOnly = Constraint.SECURE_TRANSPORT;
135
136
// Combined constraints
137
Constraint secureAdmin = Constraint.from("SecureAdmin",
138
Constraint.Transport.SECURE, "admin");
139
}
140
}
141
```
142
143
## Authentication State
144
145
### AuthenticationState Interface
146
147
Represents the current authentication status of a request:
148
149
```java { .api }
150
public interface AuthenticationState {
151
152
// Static utility methods
153
static AuthenticationState getAuthenticationState(Request request);
154
static void setAuthenticationState(Request request, AuthenticationState authenticationState);
155
156
static Principal getUserPrincipal(Request request);
157
static Succeeded authenticate(Request request);
158
static Succeeded authenticate(Request request, Response response, Callback callback);
159
160
static Succeeded login(String username, String password, Request request, Response response);
161
static boolean logout(Request request, Response response);
162
163
// Nested state interfaces
164
interface Succeeded extends AuthenticationState {
165
UserIdentity getUserIdentity();
166
String getAuthenticationType();
167
}
168
169
interface ResponseSent extends AuthenticationState {
170
// Marker interface for responses that were sent
171
}
172
173
interface Deferred extends AuthenticationState {
174
AuthenticationState authenticate(Request request, Response response, Callback callback);
175
}
176
}
177
```
178
179
### Authentication State Constants
180
181
```java { .api }
182
public class AuthenticationStates {
183
184
public void handleAuthenticationStates(AuthenticationState state) {
185
186
// Challenge was sent to client
187
if (state == AuthenticationState.CHALLENGE) {
188
// Client needs to provide credentials
189
}
190
191
// Failure response was sent
192
else if (state == AuthenticationState.SEND_FAILURE) {
193
// Authentication failed, error sent
194
}
195
196
// Success response was sent
197
else if (state == AuthenticationState.SEND_SUCCESS) {
198
// Authentication succeeded, response sent
199
}
200
201
// Successful authentication
202
else if (state instanceof AuthenticationState.Succeeded) {
203
AuthenticationState.Succeeded success = (AuthenticationState.Succeeded) state;
204
UserIdentity user = success.getUserIdentity();
205
String authType = success.getAuthenticationType();
206
}
207
208
// Deferred authentication
209
else if (state instanceof AuthenticationState.Deferred) {
210
AuthenticationState.Deferred deferred = (AuthenticationState.Deferred) state;
211
// Authentication will be handled later
212
}
213
}
214
}
215
```
216
217
## Authenticator Interface
218
219
### Core Authenticator Methods
220
221
```java { .api }
222
public interface Authenticator {
223
224
// Authentication type constants
225
String BASIC_AUTH = "BASIC";
226
String FORM_AUTH = "FORM";
227
String DIGEST_AUTH = "DIGEST";
228
String CERT_AUTH = "CLIENT_CERT";
229
String CERT_AUTH2 = "CLIENT-CERT";
230
String SPNEGO_AUTH = "SPNEGO";
231
String NEGOTIATE_AUTH = "NEGOTIATE";
232
String OPENID_AUTH = "OPENID";
233
234
// Configuration and identification
235
void setConfiguration(Configuration configuration);
236
String getAuthenticationType();
237
238
// Request preparation and validation
239
default Request prepareRequest(Request request, AuthenticationState authenticationState) {
240
return request;
241
}
242
243
default Constraint.Authorization getConstraintAuthentication(String pathInContext,
244
Constraint.Authorization existing, Function<Boolean, Session> getSession) {
245
return existing == null ? Constraint.Authorization.ALLOWED : existing;
246
}
247
248
AuthenticationState validateRequest(Request request, Response response, Callback callback)
249
throws ServerAuthException;
250
251
// Nested configuration interface
252
interface Configuration {
253
String getRealmName();
254
String getParameter(String key);
255
LoginService getLoginService();
256
IdentityService getIdentityService();
257
}
258
259
// Factory interface
260
interface Factory {
261
Authenticator getAuthenticator(Server server, Context context, Configuration configuration);
262
}
263
}
264
```
265
266
### DefaultAuthenticatorFactory
267
268
Default implementation of the Authenticator.Factory interface that creates authenticators based on configuration:
269
270
```java { .api }
271
public class DefaultAuthenticatorFactory implements Authenticator.Factory {
272
273
@Override
274
public Authenticator getAuthenticator(Server server, Context context, Configuration configuration);
275
}
276
```
277
278
The DefaultAuthenticatorFactory creates authenticators based on the configured authentication type:
279
- `BASIC` → `BasicAuthenticator`
280
- `DIGEST` → `DigestAuthenticator`
281
- `FORM` → `FormAuthenticator`
282
- `CLIENT-CERT` → `SslClientCertAuthenticator` (requires single SslContextFactory)
283
- `SPNEGO`/`NEGOTIATE` → `SPNEGOAuthenticator`
284
285
LoginAuthenticator instances are wrapped with DeferredAuthenticationState for non-mandatory authentication scenarios.
286
287
### ServerAuthException
288
289
Exception class for server-side authentication and authorization failures:
290
291
```java { .api }
292
public class ServerAuthException extends GeneralSecurityException {
293
294
// Constructors
295
public ServerAuthException();
296
public ServerAuthException(String message);
297
public ServerAuthException(String message, Throwable cause);
298
public ServerAuthException(Throwable cause);
299
}
300
```
301
302
Thrown by authenticators during request validation when authentication or authorization fails.
303
304
## Practical Examples
305
306
### Basic Security Setup
307
308
```java { .api }
309
public class BasicSecuritySetup {
310
311
public SecurityHandler createBasicSecurity() {
312
SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
313
314
// Configure realm
315
security.setRealmName("MyApplication");
316
317
// Set up login service
318
HashLoginService loginService = new HashLoginService();
319
loginService.setName("MyApplication");
320
loginService.setConfig(Resource.newResource("users.properties"));
321
security.setLoginService(loginService);
322
323
// Configure authenticator
324
BasicAuthenticator authenticator = new BasicAuthenticator();
325
security.setAuthenticator(authenticator);
326
327
// Define constraints
328
security.put("/public/*", Constraint.ALLOWED);
329
security.put("/users/*", Constraint.ANY_USER);
330
security.put("/admin/*", Constraint.from("admin"));
331
security.put("/api/secure/*", Constraint.from("SecureAPI",
332
Constraint.Transport.SECURE, "api-user"));
333
334
return security;
335
}
336
}
337
```
338
339
### Advanced Security Configuration
340
341
```java { .api }
342
public class AdvancedSecurityConfig {
343
344
public void configureAdvancedSecurity(SecurityHandler security) {
345
346
// Identity service for thread association
347
DefaultIdentityService identityService = new DefaultIdentityService();
348
security.setIdentityService(identityService);
349
350
// Session security settings
351
security.setSessionRenewedOnAuthentication(true);
352
security.setSessionMaxInactiveIntervalOnAuthentication(1800); // 30 minutes
353
354
// Custom authenticator factory
355
security.setAuthenticatorFactory(new DefaultAuthenticatorFactory());
356
357
// Parameters for authenticators
358
security.setParameter("charset", "UTF-8");
359
security.setParameter("realmName", "SecureRealm");
360
361
// Complex constraint combinations
362
setupComplexConstraints((SecurityHandler.PathMapped) security);
363
}
364
365
private void setupComplexConstraints(SecurityHandler.PathMapped security) {
366
367
// Public resources - no authentication required
368
security.put("/css/*", Constraint.ALLOWED);
369
security.put("/js/*", Constraint.ALLOWED);
370
security.put("/images/*", Constraint.ALLOWED);
371
security.put("/favicon.ico", Constraint.ALLOWED);
372
373
// API endpoints with role-based access
374
security.put("/api/public/*", Constraint.ALLOWED);
375
security.put("/api/users/*", Constraint.ANY_USER);
376
security.put("/api/admin/*", Constraint.from("admin", "super-admin"));
377
378
// Secure administrative areas requiring HTTPS
379
security.put("/admin/*", Constraint.from("AdminArea",
380
Constraint.Transport.SECURE, "admin"));
381
security.put("/config/*", Constraint.from("ConfigArea",
382
Constraint.Transport.SECURE, "super-admin"));
383
384
// User-specific areas
385
security.put("/user/*", Constraint.from("user", "premium-user"));
386
security.put("/premium/*", Constraint.from("premium-user"));
387
}
388
}
389
```
390
391
### Custom Constraint Implementation
392
393
```java { .api }
394
public class CustomConstraintExample {
395
396
public static class TimeBasedConstraint implements Constraint {
397
private final Constraint base;
398
private final int startHour;
399
private final int endHour;
400
401
public TimeBasedConstraint(Constraint base, int startHour, int endHour) {
402
this.base = base;
403
this.startHour = startHour;
404
this.endHour = endHour;
405
}
406
407
@Override
408
public String getName() {
409
return "TimeBased-" + base.getName();
410
}
411
412
@Override
413
public Transport getTransport() {
414
return base.getTransport();
415
}
416
417
@Override
418
public Authorization getAuthorization() {
419
int hour = LocalTime.now().getHour();
420
if (hour >= startHour && hour < endHour) {
421
return base.getAuthorization();
422
}
423
return Authorization.FORBIDDEN;
424
}
425
426
@Override
427
public Set<String> getRoles() {
428
return base.getRoles();
429
}
430
}
431
432
public void useTimeBasedConstraints(SecurityHandler.PathMapped security) {
433
// Business hours only access (9 AM to 6 PM)
434
Constraint businessHours = new TimeBasedConstraint(
435
Constraint.from("business-user"), 9, 18);
436
security.put("/business/*", businessHours);
437
438
// After-hours admin access
439
Constraint afterHours = new TimeBasedConstraint(
440
Constraint.from("admin"), 18, 9);
441
security.put("/maintenance/*", afterHours);
442
}
443
}
444
```
445
446
### SecurityHandler Extension
447
448
```java { .api }
449
public class CustomSecurityHandler extends SecurityHandler {
450
private final Map<String, Constraint> dynamicConstraints = new ConcurrentHashMap<>();
451
452
@Override
453
protected Constraint getConstraint(String pathInContext, Request request) {
454
455
// Check dynamic constraints first
456
Constraint dynamic = dynamicConstraints.get(pathInContext);
457
if (dynamic != null) {
458
return dynamic;
459
}
460
461
// Check user-specific constraints
462
UserIdentity user = getCurrentUser(request);
463
if (user != null && user.isUserInRole("super-admin")) {
464
return Constraint.ALLOWED; // Super admins bypass all constraints
465
}
466
467
// Default to forbidden for unknown paths
468
return Constraint.FORBIDDEN;
469
}
470
471
public void addDynamicConstraint(String path, Constraint constraint) {
472
dynamicConstraints.put(path, constraint);
473
}
474
475
public void removeDynamicConstraint(String path) {
476
dynamicConstraints.remove(path);
477
}
478
479
private UserIdentity getCurrentUser(Request request) {
480
AuthenticationState auth = AuthenticationState.getAuthenticationState(request);
481
if (auth instanceof AuthenticationState.Succeeded) {
482
return ((AuthenticationState.Succeeded) auth).getUserIdentity();
483
}
484
return null;
485
}
486
}
487
```
488
489
## Error Handling
490
491
### Security Exceptions
492
493
```java { .api }
494
public class SecurityErrorHandling {
495
496
public void handleSecurityErrors(SecurityHandler security) {
497
498
try {
499
// Security operations that might fail
500
Constraint constraint = security.getConstraint("/protected", request);
501
502
} catch (ServerAuthException e) {
503
// Handle authentication/authorization errors
504
logger.error("Security error: " + e.getMessage(), e);
505
506
// Send appropriate error response
507
response.setStatus(HttpStatus.UNAUTHORIZED_401);
508
response.getHeaders().put(HttpHeader.WWW_AUTHENTICATE,
509
"Basic realm=\"" + security.getRealmName() + "\"");
510
}
511
}
512
513
public void customErrorHandling(Request request, Response response, Throwable error) {
514
515
if (error instanceof ServerAuthException) {
516
ServerAuthException authError = (ServerAuthException) error;
517
518
// Log security violations
519
logger.warn("Authentication failure for user: {} from IP: {}",
520
request.getHeaders().get("X-User"),
521
Request.getRemoteAddr(request));
522
523
// Custom error page
524
response.setStatus(HttpStatus.FORBIDDEN_403);
525
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/html");
526
527
try {
528
response.getWriter().write("<html><body>" +
529
"<h1>Access Denied</h1>" +
530
"<p>You do not have permission to access this resource.</p>" +
531
"</body></html>");
532
} catch (IOException e) {
533
logger.error("Error writing security error page", e);
534
}
535
}
536
}
537
}
538
```
539
540
## Best Practices
541
542
### Security Configuration
543
544
```java { .api }
545
public class SecurityBestPractices {
546
547
public SecurityHandler createProductionSecurity() {
548
SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();
549
550
// Always use strong realm names
551
security.setRealmName("ProductionApp-" + UUID.randomUUID().toString());
552
553
// Enable session renewal on authentication
554
security.setSessionRenewedOnAuthentication(true);
555
556
// Set reasonable session timeout
557
security.setSessionMaxInactiveIntervalOnAuthentication(1800); // 30 minutes
558
559
// Secure transport for sensitive operations
560
security.put("/login/*", Constraint.from("Login",
561
Constraint.Transport.SECURE));
562
security.put("/admin/*", Constraint.from("Admin",
563
Constraint.Transport.SECURE, "admin"));
564
security.put("/api/payment/*", Constraint.from("Payment",
565
Constraint.Transport.SECURE, "payment-user"));
566
567
return security;
568
}
569
570
public void secureDefaultsSetup(SecurityHandler security) {
571
572
// Deny by default - explicit allow only
573
security.put("/*", Constraint.FORBIDDEN);
574
575
// Explicitly allow public resources
576
security.put("/public/*", Constraint.ALLOWED);
577
security.put("/health", Constraint.ALLOWED);
578
security.put("/status", Constraint.ALLOWED);
579
580
// Authenticated areas
581
security.put("/app/*", Constraint.ANY_USER);
582
security.put("/user/*", Constraint.ANY_USER);
583
584
// Role-based areas
585
security.put("/admin/*", Constraint.from("admin"));
586
security.put("/reports/*", Constraint.from("admin", "manager"));
587
}
588
}
589
```
590
591
## Utility Classes
592
593
### Resource
594
595
Utility class for configuration resource management:
596
597
```java { .api }
598
public abstract class Resource {
599
600
// Factory methods for creating resources
601
public static Resource newResource(String resource);
602
public static Resource newClassPathResource(String name);
603
public static Resource newResource(URI uri);
604
public static Resource newResource(File file);
605
606
// Resource properties
607
public abstract boolean exists();
608
public abstract boolean isDirectory();
609
public abstract String getName();
610
public abstract URI getURI();
611
}
612
```
613
614
The Resource class provides abstracted access to configuration files, supporting filesystem paths, classpath resources, and URIs. Commonly used for specifying login service configuration files.
615
616
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.