or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdconnection-management.mdcontext-resources.mdhandlers.mdindex.mdrequest-logging.mdrequest-response.mdsecurity-ssl.mdserver-core.mdsession-management.mdutility-handlers.md

session-management.mddocs/

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

```