or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

audit.mdbuiltin-endpoints.mdendpoints.mdhealth.mdindex.mdinfo.mdmetrics.md

audit.mddocs/

0

# Audit Framework

1

2

Spring Boot Actuator's audit framework provides event auditing capabilities for tracking authentication events, authorization failures, and custom application events with pluggable storage backends.

3

4

## Capabilities

5

6

### Core Audit API

7

8

Basic audit event representation and repository interface.

9

10

```java { .api }

11

/**

12

* Audit event representation

13

*/

14

public class AuditEvent implements Serializable {

15

16

/**

17

* Create audit event with current timestamp

18

*/

19

public AuditEvent(String principal, String type, Object... data);

20

21

/**

22

* Create audit event with specific timestamp

23

*/

24

public AuditEvent(Instant timestamp, String principal, String type, Map<String, Object> data);

25

26

/**

27

* Create audit event with string data

28

*/

29

public AuditEvent(String principal, String type, String... data);

30

31

/**

32

* Get the timestamp when the event occurred

33

*/

34

public Instant getTimestamp();

35

36

/**

37

* Get the principal (user) associated with the event

38

*/

39

public String getPrincipal();

40

41

/**

42

* Get the type of audit event

43

*/

44

public String getType();

45

46

/**

47

* Get the audit event data

48

*/

49

public Map<String, Object> getData();

50

51

@Override

52

public String toString();

53

}

54

```

55

56

**Usage Example:**

57

58

```java

59

@Service

60

public class UserService {

61

62

private final AuditEventRepository auditEventRepository;

63

64

public UserService(AuditEventRepository auditEventRepository) {

65

this.auditEventRepository = auditEventRepository;

66

}

67

68

public void loginUser(String username) {

69

// Business logic for login

70

performLogin(username);

71

72

// Create audit event

73

AuditEvent loginEvent = new AuditEvent(

74

username,

75

"USER_LOGIN",

76

"success", true,

77

"ip", getCurrentUserIP(),

78

"timestamp", Instant.now()

79

);

80

81

auditEventRepository.add(loginEvent);

82

}

83

84

public void loginFailed(String username, String reason) {

85

AuditEvent failedLoginEvent = new AuditEvent(

86

username,

87

"AUTHENTICATION_FAILURE",

88

"reason", reason,

89

"ip", getCurrentUserIP()

90

);

91

92

auditEventRepository.add(failedLoginEvent);

93

}

94

}

95

```

96

97

### Audit Event Repository

98

99

Interface and implementations for storing audit events.

100

101

```java { .api }

102

/**

103

* Repository interface for audit events

104

*/

105

public interface AuditEventRepository {

106

107

/**

108

* Add an audit event to the repository

109

*/

110

void add(AuditEvent event);

111

112

/**

113

* Find audit events for a principal after a specific time

114

*/

115

List<AuditEvent> find(String principal, Instant after, String type);

116

}

117

118

/**

119

* In-memory implementation of AuditEventRepository

120

*/

121

public class InMemoryAuditEventRepository implements AuditEventRepository {

122

123

public InMemoryAuditEventRepository();

124

public InMemoryAuditEventRepository(int capacity);

125

126

@Override

127

public void add(AuditEvent event);

128

129

@Override

130

public List<AuditEvent> find(String principal, Instant after, String type);

131

132

/**

133

* Find all audit events for a principal

134

*/

135

public List<AuditEvent> find(String principal);

136

137

/**

138

* Find audit events after a specific time

139

*/

140

public List<AuditEvent> find(Instant after);

141

142

/**

143

* Find audit events of a specific type

144

*/

145

public List<AuditEvent> find(String type);

146

}

147

```

148

149

**Usage Example:**

150

151

```java

152

@Configuration

153

public class AuditConfiguration {

154

155

@Bean

156

@ConditionalOnMissingBean(AuditEventRepository.class)

157

public InMemoryAuditEventRepository auditEventRepository() {

158

return new InMemoryAuditEventRepository(1000); // Keep last 1000 events

159

}

160

161

@Bean

162

@ConditionalOnProperty("app.audit.database.enabled")

163

public AuditEventRepository databaseAuditEventRepository(JdbcTemplate jdbcTemplate) {

164

return new DatabaseAuditEventRepository(jdbcTemplate);

165

}

166

}

167

168

// Custom database implementation

169

public class DatabaseAuditEventRepository implements AuditEventRepository {

170

171

private final JdbcTemplate jdbcTemplate;

172

173

public DatabaseAuditEventRepository(JdbcTemplate jdbcTemplate) {

174

this.jdbcTemplate = jdbcTemplate;

175

}

176

177

@Override

178

public void add(AuditEvent event) {

179

String sql = "INSERT INTO audit_events (timestamp, principal, type, data) VALUES (?, ?, ?, ?)";

180

jdbcTemplate.update(sql,

181

Timestamp.from(event.getTimestamp()),

182

event.getPrincipal(),

183

event.getType(),

184

convertDataToJson(event.getData())

185

);

186

}

187

188

@Override

189

public List<AuditEvent> find(String principal, Instant after, String type) {

190

StringBuilder sql = new StringBuilder("SELECT * FROM audit_events WHERE 1=1");

191

List<Object> params = new ArrayList<>();

192

193

if (principal != null) {

194

sql.append(" AND principal = ?");

195

params.add(principal);

196

}

197

if (after != null) {

198

sql.append(" AND timestamp > ?");

199

params.add(Timestamp.from(after));

200

}

201

if (type != null) {

202

sql.append(" AND type = ?");

203

params.add(type);

204

}

205

206

return jdbcTemplate.query(sql.toString(), params.toArray(), this::mapRowToAuditEvent);

207

}

208

}

209

```

210

211

### Audit Event Listeners

212

213

Listeners for handling audit events and Spring application events.

214

215

```java { .api }

216

/**

217

* Base class for audit listeners

218

*/

219

public abstract class AbstractAuditListener {

220

221

private AuditEventRepository auditEventRepository;

222

223

public AbstractAuditListener(AuditEventRepository auditEventRepository);

224

225

protected void publish(AuditEvent event);

226

227

protected AuditEventRepository getAuditEventRepository();

228

}

229

230

/**

231

* Spring application event for auditing

232

*/

233

public class AuditApplicationEvent extends ApplicationEvent {

234

235

public AuditApplicationEvent(Object source, AuditEvent auditEvent);

236

public AuditApplicationEvent(Object source, String principal, String type, Object... data);

237

238

public AuditEvent getAuditEvent();

239

}

240

241

/**

242

* Default audit event listener

243

*/

244

public class AuditListener extends AbstractAuditListener {

245

246

public AuditListener(AuditEventRepository auditEventRepository);

247

248

@EventListener

249

public void onAuditEvent(AuditApplicationEvent event);

250

}

251

```

252

253

**Usage Example:**

254

255

```java

256

@Component

257

public class SecurityAuditListener extends AbstractAuditListener {

258

259

public SecurityAuditListener(AuditEventRepository auditEventRepository) {

260

super(auditEventRepository);

261

}

262

263

@EventListener

264

public void onAuthenticationSuccess(AuthenticationSuccessEvent event) {

265

String principal = event.getAuthentication().getName();

266

AuditEvent auditEvent = new AuditEvent(

267

principal,

268

"AUTHENTICATION_SUCCESS",

269

"type", event.getAuthentication().getClass().getSimpleName(),

270

"authorities", event.getAuthentication().getAuthorities().toString()

271

);

272

publish(auditEvent);

273

}

274

275

@EventListener

276

public void onAuthenticationFailure(AbstractAuthenticationFailureEvent event) {

277

String principal = event.getAuthentication().getName();

278

AuditEvent auditEvent = new AuditEvent(

279

principal,

280

"AUTHENTICATION_FAILURE",

281

"type", event.getException().getClass().getSimpleName(),

282

"message", event.getException().getMessage()

283

);

284

publish(auditEvent);

285

}

286

287

@EventListener

288

public void onAuthorizationFailure(AuthorizationFailureEvent event) {

289

String principal = event.getAuthentication().getName();

290

AuditEvent auditEvent = new AuditEvent(

291

principal,

292

"AUTHORIZATION_FAILURE",

293

"resource", event.getResource().toString(),

294

"reason", event.getAccessDeniedException().getMessage()

295

);

296

publish(auditEvent);

297

}

298

}

299

300

@Service

301

public class BusinessAuditService {

302

303

private final ApplicationEventPublisher eventPublisher;

304

305

public BusinessAuditService(ApplicationEventPublisher eventPublisher) {

306

this.eventPublisher = eventPublisher;

307

}

308

309

public void auditBusinessOperation(String principal, String operation, Object... data) {

310

AuditApplicationEvent auditEvent = new AuditApplicationEvent(

311

this, principal, operation, data

312

);

313

eventPublisher.publishEvent(auditEvent);

314

}

315

316

public void auditDataAccess(String principal, String resource, String action) {

317

auditBusinessOperation(principal, "DATA_ACCESS",

318

"resource", resource,

319

"action", action,

320

"timestamp", Instant.now()

321

);

322

}

323

}

324

```

325

326

### Audit Events Endpoint

327

328

Built-in endpoint for exposing audit events.

329

330

```java { .api }

331

/**

332

* Endpoint to expose audit events

333

*/

334

@Endpoint(id = "auditevents")

335

public class AuditEventsEndpoint {

336

337

public AuditEventsEndpoint(AuditEventRepository repository);

338

339

/**

340

* Get audit events with optional filtering

341

*/

342

@ReadOperation

343

public AuditEventsDescriptor eventsWithPrincipalAndType(@Nullable String principal, @Nullable String type);

344

345

/**

346

* Get audit events after a specific date

347

*/

348

@ReadOperation

349

public AuditEventsDescriptor eventsWithPrincipalAndTypeAndDate(@Nullable String principal,

350

@Nullable String type,

351

@Nullable String after);

352

353

/**

354

* Descriptor for audit events response

355

*/

356

public static final class AuditEventsDescriptor {

357

private final List<AuditEvent> events;

358

359

public AuditEventsDescriptor(List<AuditEvent> events);

360

361

public List<AuditEvent> getEvents();

362

}

363

}

364

```

365

366

**Usage Example:**

367

368

```java

369

@RestController

370

public class CustomAuditController {

371

372

private final AuditEventRepository auditEventRepository;

373

374

public CustomAuditController(AuditEventRepository auditEventRepository) {

375

this.auditEventRepository = auditEventRepository;

376

}

377

378

@GetMapping("/admin/audit/users/{username}")

379

public ResponseEntity<List<AuditEvent>> getUserAuditEvents(@PathVariable String username) {

380

List<AuditEvent> events = auditEventRepository.find(username, null, null);

381

return ResponseEntity.ok(events);

382

}

383

384

@GetMapping("/admin/audit/failures")

385

public ResponseEntity<List<AuditEvent>> getFailureEvents() {

386

Instant oneWeekAgo = Instant.now().minus(7, ChronoUnit.DAYS);

387

List<AuditEvent> failures = auditEventRepository.find(null, oneWeekAgo, "AUTHENTICATION_FAILURE");

388

return ResponseEntity.ok(failures);

389

}

390

391

@PostMapping("/admin/audit/custom")

392

public ResponseEntity<Void> createCustomAuditEvent(@RequestBody CustomAuditRequest request) {

393

AuditEvent event = new AuditEvent(

394

request.getPrincipal(),

395

request.getType(),

396

request.getData()

397

);

398

auditEventRepository.add(event);

399

return ResponseEntity.ok().build();

400

}

401

}

402

```

403

404

### Common Audit Event Types

405

406

Standard audit event types commonly used in Spring Boot applications.

407

408

```java { .api }

409

// Common audit event type constants (typically defined as string constants)

410

411

/**

412

* Authentication-related audit events

413

*/

414

public final class AuthenticationAuditEventTypes {

415

public static final String AUTHENTICATION_SUCCESS = "AUTHENTICATION_SUCCESS";

416

public static final String AUTHENTICATION_FAILURE = "AUTHENTICATION_FAILURE";

417

public static final String AUTHENTICATION_SWITCH = "AUTHENTICATION_SWITCH";

418

public static final String LOGOUT = "LOGOUT";

419

}

420

421

/**

422

* Authorization-related audit events

423

*/

424

public final class AuthorizationAuditEventTypes {

425

public static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";

426

public static final String ACCESS_GRANTED = "ACCESS_GRANTED";

427

public static final String ACCESS_DENIED = "ACCESS_DENIED";

428

}

429

430

/**

431

* Session-related audit events

432

*/

433

public final class SessionAuditEventTypes {

434

public static final String SESSION_CREATED = "SESSION_CREATED";

435

public static final String SESSION_DESTROYED = "SESSION_DESTROYED";

436

public static final String SESSION_EXPIRED = "SESSION_EXPIRED";

437

}

438

```

439

440

**Usage Example:**

441

442

```java

443

@Component

444

public class ComprehensiveAuditEventPublisher {

445

446

private final ApplicationEventPublisher eventPublisher;

447

448

public ComprehensiveAuditEventPublisher(ApplicationEventPublisher eventPublisher) {

449

this.eventPublisher = eventPublisher;

450

}

451

452

public void publishAuthenticationSuccess(String username, String source) {

453

publishAuditEvent(username, AuthenticationAuditEventTypes.AUTHENTICATION_SUCCESS,

454

"source", source,

455

"timestamp", Instant.now()

456

);

457

}

458

459

public void publishAuthenticationFailure(String username, String reason) {

460

publishAuditEvent(username, AuthenticationAuditEventTypes.AUTHENTICATION_FAILURE,

461

"reason", reason,

462

"timestamp", Instant.now()

463

);

464

}

465

466

public void publishAuthorizationFailure(String username, String resource) {

467

publishAuditEvent(username, AuthorizationAuditEventTypes.AUTHORIZATION_FAILURE,

468

"resource", resource,

469

"timestamp", Instant.now()

470

);

471

}

472

473

private void publishAuditEvent(String principal, String type, Object... data) {

474

AuditApplicationEvent event = new AuditApplicationEvent(this, principal, type, data);

475

eventPublisher.publishEvent(event);

476

}

477

}

478

```