0
# Session Management
1
2
Session management provides HTTP session support with creation, invalidation, attribute storage, and timeout management for maintaining user state across requests.
3
4
## Session Interface
5
6
The core session interface for managing HTTP session state.
7
8
```java { .api }
9
public interface Session extends Attributes {
10
// Session identification
11
String getId();
12
Context getContext();
13
14
// Timing information
15
long getCreationTime();
16
long getLastAccessedTime();
17
18
// Session lifecycle
19
void setMaxInactiveInterval(int interval);
20
int getMaxInactiveInterval();
21
void invalidate();
22
boolean isNew();
23
boolean isValid();
24
25
// Attribute management (from Attributes interface)
26
Object getAttribute(String name);
27
void setAttribute(String name, Object value);
28
void removeAttribute(String name);
29
Set<String> getAttributeNameSet();
30
void clearAttributes();
31
}
32
```
33
34
## Basic Session Usage
35
36
```java
37
public class SessionDemoHandler extends Handler.Abstract {
38
39
@Override
40
public boolean handle(Request request, Response response, Callback callback)
41
throws Exception {
42
43
// Get existing session or create new one
44
Session session = request.getSession(true);
45
46
// Check if this is a new session
47
if (session.isNew()) {
48
System.out.println("New session created: " + session.getId());
49
session.setAttribute("createdAt", Instant.now());
50
}
51
52
// Update visit count
53
Integer visitCount = (Integer) session.getAttribute("visitCount");
54
if (visitCount == null) {
55
visitCount = 0;
56
}
57
visitCount++;
58
session.setAttribute("visitCount", visitCount);
59
60
// Update last visit time
61
session.setAttribute("lastVisit", Instant.now());
62
63
// Set session timeout (30 minutes)
64
session.setMaxInactiveInterval(30 * 60);
65
66
// Generate response with session info
67
response.setStatus(200);
68
response.getHeaders().put("Content-Type", "application/json");
69
70
String json = createSessionInfoJson(session);
71
response.write(true, ByteBuffer.wrap(json.getBytes()), callback);
72
73
return true;
74
}
75
76
private String createSessionInfoJson(Session session) {
77
Map<String, Object> sessionInfo = new HashMap<>();
78
sessionInfo.put("sessionId", session.getId());
79
sessionInfo.put("isNew", session.isNew());
80
sessionInfo.put("creationTime", session.getCreationTime());
81
sessionInfo.put("lastAccessedTime", session.getLastAccessedTime());
82
sessionInfo.put("maxInactiveInterval", session.getMaxInactiveInterval());
83
84
// Add session attributes
85
Map<String, Object> attributes = new HashMap<>();
86
for (String name : session.getAttributeNameSet()) {
87
Object value = session.getAttribute(name);
88
if (value instanceof Serializable) {
89
attributes.put(name, value.toString());
90
}
91
}
92
sessionInfo.put("attributes", attributes);
93
94
// Convert to JSON (simplified)
95
return toJson(sessionInfo);
96
}
97
98
private String toJson(Map<String, Object> map) {
99
// Simplified JSON conversion
100
StringBuilder json = new StringBuilder("{");
101
boolean first = true;
102
for (Map.Entry<String, Object> entry : map.entrySet()) {
103
if (!first) json.append(",");
104
json.append("\"").append(entry.getKey()).append("\":");
105
json.append("\"").append(entry.getValue()).append("\"");
106
first = false;
107
}
108
json.append("}");
109
return json.toString();
110
}
111
}
112
```
113
114
## Session Configuration
115
116
### SessionHandler
117
118
Handler that provides session support to child handlers.
119
120
```java { .api }
121
public class SessionHandler extends Handler.Wrapper {
122
// Session configuration
123
public SessionCache getSessionCache();
124
public void setSessionCache(SessionCache sessionCache);
125
public SessionDataStore getSessionDataStore();
126
public void setSessionDataStore(SessionDataStore sessionDataStore);
127
128
// Session ID configuration
129
public SessionIdManager getSessionIdManager();
130
public void setSessionIdManager(SessionIdManager sessionIdManager);
131
132
// Cookie configuration
133
public SessionCookieConfig getSessionCookieConfig();
134
public void setSessionCookieConfig(SessionCookieConfig config);
135
136
// Timeout configuration
137
public int getMaxInactiveInterval();
138
public void setMaxInactiveInterval(int maxInactiveInterval);
139
140
// Session tracking modes
141
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes();
142
public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes);
143
144
// Session lifecycle
145
public Session newSession(Request request);
146
public void complete(Session session);
147
}
148
```
149
150
### Basic Session Configuration
151
152
```java
153
public class SessionConfiguration {
154
155
public void configureSession(Server server) {
156
// Create session handler
157
SessionHandler sessionHandler = new SessionHandler();
158
159
// Configure session timeout (30 minutes)
160
sessionHandler.setMaxInactiveInterval(30 * 60);
161
162
// Configure session cookie
163
SessionCookieConfig cookieConfig = sessionHandler.getSessionCookieConfig();
164
cookieConfig.setName("JSESSIONID");
165
cookieConfig.setPath("/");
166
cookieConfig.setHttpOnly(true);
167
cookieConfig.setSecure(true); // HTTPS only
168
cookieConfig.setMaxAge(24 * 60 * 60); // 24 hours
169
170
// Set session tracking mode
171
Set<SessionTrackingMode> trackingModes = EnumSet.of(SessionTrackingMode.COOKIE);
172
sessionHandler.setSessionTrackingModes(trackingModes);
173
174
// Set application handler as child
175
sessionHandler.setHandler(new ApplicationHandler());
176
177
server.setHandler(sessionHandler);
178
}
179
}
180
```
181
182
## Advanced Session Management
183
184
### Custom Session Store
185
186
```java
187
public class DatabaseSessionDataStore extends AbstractSessionDataStore {
188
private final DataSource dataSource;
189
190
public DatabaseSessionDataStore(DataSource dataSource) {
191
this.dataSource = dataSource;
192
}
193
194
@Override
195
public SessionData doLoad(String id) throws Exception {
196
try (Connection conn = dataSource.getConnection()) {
197
String sql = "SELECT * FROM sessions WHERE session_id = ?";
198
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
199
stmt.setString(1, id);
200
try (ResultSet rs = stmt.executeQuery()) {
201
if (rs.next()) {
202
return deserializeSessionData(rs);
203
}
204
}
205
}
206
}
207
return null;
208
}
209
210
@Override
211
public boolean doStore(String id, SessionData data, long lastSaveTime) throws Exception {
212
try (Connection conn = dataSource.getConnection()) {
213
String sql = "INSERT INTO sessions (session_id, creation_time, last_access_time, " +
214
"max_inactive_interval, attributes) VALUES (?, ?, ?, ?, ?) " +
215
"ON DUPLICATE KEY UPDATE last_access_time = ?, attributes = ?";
216
217
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
218
stmt.setString(1, id);
219
stmt.setLong(2, data.getCreated());
220
stmt.setLong(3, data.getLastAccessed());
221
stmt.setInt(4, data.getMaxInactiveInterval());
222
stmt.setBytes(5, serializeAttributes(data.getAttributes()));
223
stmt.setLong(6, data.getLastAccessed());
224
stmt.setBytes(7, serializeAttributes(data.getAttributes()));
225
226
return stmt.executeUpdate() > 0;
227
}
228
}
229
}
230
231
@Override
232
public boolean doDelete(String id) throws Exception {
233
try (Connection conn = dataSource.getConnection()) {
234
String sql = "DELETE FROM sessions WHERE session_id = ?";
235
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
236
stmt.setString(1, id);
237
return stmt.executeUpdate() > 0;
238
}
239
}
240
}
241
242
@Override
243
public Set<String> doCheckExpired(Set<String> candidates, long time) throws Exception {
244
Set<String> expired = new HashSet<>();
245
246
try (Connection conn = dataSource.getConnection()) {
247
String sql = "SELECT session_id FROM sessions WHERE " +
248
"last_access_time + (max_inactive_interval * 1000) < ?";
249
250
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
251
stmt.setLong(1, time);
252
try (ResultSet rs = stmt.executeQuery()) {
253
while (rs.next()) {
254
expired.add(rs.getString("session_id"));
255
}
256
}
257
}
258
}
259
260
return expired;
261
}
262
263
@Override
264
public Set<String> doGetExpired(long time) throws Exception {
265
return doCheckExpired(Collections.emptySet(), time);
266
}
267
268
@Override
269
public void doCleanOrphans(long time) throws Exception {
270
try (Connection conn = dataSource.getConnection()) {
271
String sql = "DELETE FROM sessions WHERE " +
272
"last_access_time + (max_inactive_interval * 1000) < ?";
273
274
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
275
stmt.setLong(1, time);
276
int deleted = stmt.executeUpdate();
277
System.out.println("Cleaned " + deleted + " orphaned sessions");
278
}
279
}
280
}
281
282
private SessionData deserializeSessionData(ResultSet rs) throws SQLException {
283
SessionData data = new SessionData(
284
rs.getString("session_id"),
285
"/", // context path
286
"localhost", // virtual host
287
rs.getLong("creation_time"),
288
rs.getLong("last_access_time"),
289
rs.getLong("last_access_time"),
290
rs.getInt("max_inactive_interval")
291
);
292
293
// Deserialize attributes
294
byte[] attributeBytes = rs.getBytes("attributes");
295
if (attributeBytes != null) {
296
Map<String, Object> attributes = deserializeAttributes(attributeBytes);
297
data.putAllAttributes(attributes);
298
}
299
300
return data;
301
}
302
303
private byte[] serializeAttributes(Map<String, Object> attributes) {
304
// Implement serialization (e.g., using Java serialization or JSON)
305
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
306
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
307
oos.writeObject(attributes);
308
return baos.toByteArray();
309
} catch (IOException e) {
310
throw new RuntimeException("Failed to serialize session attributes", e);
311
}
312
}
313
314
private Map<String, Object> deserializeAttributes(byte[] data) {
315
// Implement deserialization
316
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
317
ObjectInputStream ois = new ObjectInputStream(bais)) {
318
return (Map<String, Object>) ois.readObject();
319
} catch (IOException | ClassNotFoundException e) {
320
throw new RuntimeException("Failed to deserialize session attributes", e);
321
}
322
}
323
}
324
```
325
326
### Session Cache Configuration
327
328
```java
329
public class SessionCacheConfiguration {
330
331
public SessionHandler createSessionHandler(DataSource dataSource) {
332
SessionHandler sessionHandler = new SessionHandler();
333
334
// Create session ID manager
335
DefaultSessionIdManager sessionIdManager = new DefaultSessionIdManager(server);
336
sessionIdManager.setWorkerName("node1");
337
sessionHandler.setSessionIdManager(sessionIdManager);
338
339
// Create session data store
340
DatabaseSessionDataStore sessionDataStore = new DatabaseSessionDataStore(dataSource);
341
sessionDataStore.setGracePeriodSec(3600); // 1 hour grace period
342
sessionDataStore.setSavePeriodSec(0); // Save on every change
343
344
// Create session cache
345
DefaultSessionCache sessionCache = new DefaultSessionCache(sessionHandler);
346
sessionCache.setSessionDataStore(sessionDataStore);
347
sessionCache.setEvictionPolicy(SessionCache.NEVER_EVICT);
348
sessionCache.setSaveOnCreate(true);
349
sessionCache.setSaveOnInactiveEviction(true);
350
sessionCache.setRemoveUnloadableSessions(true);
351
352
sessionHandler.setSessionCache(sessionCache);
353
354
return sessionHandler;
355
}
356
}
357
```
358
359
## Session Security
360
361
### Secure Session Configuration
362
363
```java
364
public class SecureSessionHandler extends SessionHandler {
365
366
@Override
367
protected void doStart() throws Exception {
368
super.doStart();
369
370
// Configure secure session cookie
371
SessionCookieConfig cookieConfig = getSessionCookieConfig();
372
cookieConfig.setSecure(true); // HTTPS only
373
cookieConfig.setHttpOnly(true); // No JavaScript access
374
cookieConfig.setSameSite(SameSite.STRICT); // CSRF protection
375
376
// Use random session ID generation
377
SessionIdManager idManager = getSessionIdManager();
378
if (idManager instanceof DefaultSessionIdManager) {
379
DefaultSessionIdManager defaultIdManager = (DefaultSessionIdManager) idManager;
380
defaultIdManager.setWorkerName(null); // Use random worker name
381
}
382
}
383
384
@Override
385
public Session newSession(Request request) {
386
Session session = super.newSession(request);
387
388
// Add security attributes
389
session.setAttribute("remoteAddr",
390
request.getConnectionMetaData().getRemoteSocketAddress());
391
session.setAttribute("userAgent", request.getHeaders().get("User-Agent"));
392
393
return session;
394
}
395
396
@Override
397
protected void sessionInactivityTimer(Session session) {
398
// Log session timeout for security monitoring
399
System.out.println("Session timeout: " + session.getId() +
400
" from " + session.getAttribute("remoteAddr"));
401
super.sessionInactivityTimer(session);
402
}
403
}
404
```
405
406
### Session Validation
407
408
```java
409
public class SessionValidationHandler extends Handler.Wrapper {
410
411
@Override
412
public boolean handle(Request request, Response response, Callback callback)
413
throws Exception {
414
415
Session session = request.getSession(false);
416
if (session != null && !isValidSession(session, request)) {
417
// Invalid session, invalidate and create new
418
session.invalidate();
419
session = request.getSession(true);
420
}
421
422
return super.handle(request, response, callback);
423
}
424
425
private boolean isValidSession(Session session, Request request) {
426
// Check if session is from same IP address
427
String sessionAddr = (String) session.getAttribute("remoteAddr");
428
String currentAddr = request.getConnectionMetaData().getRemoteSocketAddress().toString();
429
430
if (sessionAddr != null && !sessionAddr.equals(currentAddr)) {
431
System.err.println("Session hijack attempt detected: " + session.getId());
432
return false;
433
}
434
435
// Check user agent
436
String sessionUserAgent = (String) session.getAttribute("userAgent");
437
String currentUserAgent = request.getHeaders().get("User-Agent");
438
439
if (sessionUserAgent != null && !sessionUserAgent.equals(currentUserAgent)) {
440
System.err.println("User agent mismatch for session: " + session.getId());
441
return false;
442
}
443
444
return true;
445
}
446
}
447
```
448
449
## Session Clustering
450
451
### Distributed Session Configuration
452
453
```java
454
public class ClusteredSessionConfiguration {
455
456
public void configureClusteredSessions(Server server, String[] nodes) {
457
// Create shared session ID manager
458
DefaultSessionIdManager sessionIdManager = new DefaultSessionIdManager(server);
459
sessionIdManager.setWorkerName(getNodeName());
460
server.addBean(sessionIdManager);
461
462
// Create clustered session data store
463
ClusteredSessionDataStore dataStore = new ClusteredSessionDataStore(nodes);
464
465
// Configure session handler
466
SessionHandler sessionHandler = new SessionHandler();
467
sessionHandler.setSessionIdManager(sessionIdManager);
468
469
// Create session cache with clustering support
470
DefaultSessionCache sessionCache = new DefaultSessionCache(sessionHandler);
471
sessionCache.setSessionDataStore(dataStore);
472
sessionCache.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
473
474
sessionHandler.setSessionCache(sessionCache);
475
476
// Set application handler
477
sessionHandler.setHandler(new ApplicationHandler());
478
server.setHandler(sessionHandler);
479
}
480
481
private String getNodeName() {
482
// Generate unique node identifier
483
return "node-" + System.currentTimeMillis();
484
}
485
}
486
```
487
488
## Session Monitoring and Statistics
489
490
### Session Statistics Handler
491
492
```java
493
public class SessionStatisticsHandler extends Handler.Wrapper {
494
private final AtomicLong sessionsCreated = new AtomicLong();
495
private final AtomicLong sessionsDestroyed = new AtomicLong();
496
private final AtomicLong sessionsTimeout = new AtomicLong();
497
private final Map<String, Long> sessionStartTimes = new ConcurrentHashMap<>();
498
499
@Override
500
public boolean handle(Request request, Response response, Callback callback)
501
throws Exception {
502
503
Session session = request.getSession(false);
504
if (session != null) {
505
if (session.isNew()) {
506
sessionsCreated.incrementAndGet();
507
sessionStartTimes.put(session.getId(), System.currentTimeMillis());
508
509
// Add session listener
510
session.setAttribute("statisticsHandler", this);
511
}
512
}
513
514
return super.handle(request, response, callback);
515
}
516
517
public void onSessionDestroyed(String sessionId) {
518
sessionsDestroyed.incrementAndGet();
519
sessionStartTimes.remove(sessionId);
520
}
521
522
public void onSessionTimeout(String sessionId) {
523
sessionsTimeout.incrementAndGet();
524
sessionStartTimes.remove(sessionId);
525
}
526
527
public long getSessionsCreated() {
528
return sessionsCreated.get();
529
}
530
531
public long getSessionsDestroyed() {
532
return sessionsDestroyed.get();
533
}
534
535
public long getSessionsTimeout() {
536
return sessionsTimeout.get();
537
}
538
539
public long getActiveSessions() {
540
return sessionsCreated.get() - sessionsDestroyed.get();
541
}
542
543
public double getAverageSessionDuration() {
544
long totalDuration = 0;
545
long count = 0;
546
547
for (Long startTime : sessionStartTimes.values()) {
548
totalDuration += System.currentTimeMillis() - startTime;
549
count++;
550
}
551
552
return count > 0 ? (double) totalDuration / count : 0.0;
553
}
554
}
555
```