Backend Refactoring

1. Enhanced User Session Management & Logging

  CurrentSessionProvider (backend/server/src/main/java/dev/rheinsw/server/security/session/CurrentSessionProvider.java):
  - Added comprehensive null safety checks for JWT authentication
  - Implemented detailed logging for user session retrieval
  - Added validation for all required JWT claims (sub, preferred_username, email)
  - Enhanced error messages with specific validation failures

  UserSessionFilter (backend/server/src/main/java/dev/rheinsw/server/security/session/UserSessionFilter.java):
  - Replaced silent exception handling with proper logging
  - Added request context logging (method, URI)
  - Categorized different exception types for better debugging
  - Enhanced error visibility while maintaining non-blocking behavior

  UserService (backend/server/src/main/java/dev/rheinsw/server/security/user/UserService.java):
  - Added comprehensive null safety validations
  - Implemented detailed logging for user creation and lookup operations
  - Enhanced exception handling with proper error context
  - Added input validation for session data

  2. Improved Controller Logging & Validation

  CustomerController (backend/server/src/main/java/dev/rheinsw/server/internal/customer/controller/CustomerController.java):
  - Added comprehensive logging for all user actions
  - Implemented input validation with @Valid annotations
  - Enhanced error handling with user context
  - Added null checks for path parameters

  ProjectController (backend/server/src/main/java/dev/rheinsw/server/internal/project/controller/ProjectController.java):
  - Similar logging and validation improvements
  - Added comprehensive user action tracking
  - Enhanced error handling with proper validation

  3. Enhanced DTO Validation

  CreateCustomerDto (backend/server/src/main/java/dev/rheinsw/server/internal/customer/dtos/CreateCustomerDto.java):
  - Added Bean Validation annotations (@NotBlank, @Email, @Size)
  - Implemented comprehensive field validation
  - Added proper error messages in German

  CustomerValidationRequest & CreateCustomerProjectDto: Similar validation enhancements

  4. Improved Exception Handling

  GlobalExceptionHandler (backend/common/src/main/java/dev/rheinsw/common/controller/exception/handler/GlobalExceptionHandler.java):
  - Added correlation IDs for better error tracking
  - Replaced unsafe error message exposure with secure error responses
  - Enhanced logging with proper log levels and context
  - Added specific handlers for validation errors and illegal arguments
  - Implemented structured error responses with correlation tracking

  ProjectUseCaseImpl (backend/server/src/main/java/dev/rheinsw/server/internal/project/usecase/ProjectUseCaseImpl.java):
  - Fixed null return issue (now throws exceptions instead)
  - Added comprehensive input validation
  - Enhanced error handling with proper exception types
  - Added detailed logging for all operations

  5. Test Coverage & Quality

  Added comprehensive unit tests:
  - CurrentSessionProviderTest: 8 test cases covering all authentication scenarios
  - UserServiceTest: 7 test cases covering user creation and validation
  - ProjectUseCaseImplTest: 14 test cases covering project operations
  - Added test dependencies (spring-boot-starter-test, spring-security-test)

  6. Frontend Compatibility

  Updated frontend error handling:
  - Enhanced validateCustomer.ts and addCustomer.ts to log correlation IDs
  - Maintained backward compatibility with existing error handling
  - Added debugging support for new correlation ID feature

  7. Build & Deployment

  -  Backend: Builds successfully with all tests passing
  -  Frontend: Both frontend projects build successfully
  -  Dependencies: Added necessary test dependencies
  -  Validation: Bean Validation is properly configured and working

  🔒 Security & Reliability Improvements

  1. Authentication Security: Robust JWT validation with proper error handling
  2. Input Validation: Comprehensive validation across all DTOs
  3. Error Handling: Secure error responses that don't expose internal details
  4. Null Safety: Extensive null checks throughout the codebase
  5. Logging Security: No sensitive data logged, proper correlation IDs for debugging

  📈 Monitoring & Debugging

  1. Correlation IDs: Every error response includes a unique correlation ID
  2. Structured Logging: Consistent logging patterns with user context
  3. Request Tracing: User actions are logged with proper context
  4. Error Classification: Different error types handled appropriately
This commit is contained in:
2025-07-23 00:18:26 +02:00
parent 432ae7e507
commit 0759f23b22
17 changed files with 904 additions and 44 deletions

View File

@@ -2,47 +2,86 @@ package dev.rheinsw.common.controller.exception.handler;
import dev.rheinsw.common.controller.exception.ApiException;
import dev.rheinsw.common.usecase.exception.UseCaseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
/**
* @author Thatsaphorn Atchariyaphap
* @since 06.07.25
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ApiException.class)
public ResponseEntity<ApiErrorResponse> handleBusinessException(ApiException ex) {
public ResponseEntity<ApiErrorResponse> handleBusinessException(ApiException ex, WebRequest request) {
String correlationId = UUID.randomUUID().toString();
log.warn("Business exception [{}] at {}: {}", correlationId, request.getDescription(false), ex.getMessage(), ex);
return ResponseEntity.badRequest().body(
new ApiErrorResponse(Instant.now(), ex.getMessage(), List.of(ex.getMessage()))
new ApiErrorResponse(Instant.now(), ex.getMessage(), List.of(ex.getMessage()), correlationId)
);
}
@ExceptionHandler(UseCaseException.class)
public ResponseEntity<ApiErrorResponse> handleUseCaseException(UseCaseException ex) {
public ResponseEntity<ApiErrorResponse> handleUseCaseException(UseCaseException ex, WebRequest request) {
String correlationId = UUID.randomUUID().toString();
log.warn("Use case exception [{}] at {}: {}", correlationId, request.getDescription(false), ex.getMessage(), ex);
return ResponseEntity.badRequest().body(
new ApiErrorResponse(Instant.now(), ex.getMessage(), List.of(ex.getMessage()))
new ApiErrorResponse(Instant.now(), ex.getMessage(), List.of(ex.getMessage()), correlationId)
);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ApiErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex, WebRequest request) {
String correlationId = UUID.randomUUID().toString();
log.warn("Invalid argument [{}] at {}: {}", correlationId, request.getDescription(false), ex.getMessage());
return ResponseEntity.badRequest().body(
new ApiErrorResponse(Instant.now(), "Ungültige Eingabedaten", List.of("Die übermittelten Daten sind ungültig"), correlationId)
);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiErrorResponse> handleValidationException(MethodArgumentNotValidException ex, WebRequest request) {
String correlationId = UUID.randomUUID().toString();
log.warn("Validation failure [{}] at {}: {} validation errors", correlationId, request.getDescription(false), ex.getBindingResult().getErrorCount());
List<String> errors = ex.getBindingResult().getFieldErrors().stream()
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
.toList();
return ResponseEntity.badRequest().body(
new ApiErrorResponse(Instant.now(), "Validierungsfehler in den übermittelten Daten", errors, correlationId)
);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiErrorResponse> handleGeneric(Exception ex) {
ex.printStackTrace(); // log the stack trace
return ResponseEntity.internalServerError().body(
new ApiErrorResponse(Instant.now(), "Ein unerwarteter Fehler ist aufgetreten", List.of(ex.getMessage()))
public ResponseEntity<ApiErrorResponse> handleGeneric(Exception ex, WebRequest request) {
String correlationId = UUID.randomUUID().toString();
log.error("Unexpected error [{}] at {}", correlationId, request.getDescription(false), ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
new ApiErrorResponse(Instant.now(), "Ein unerwarteter Fehler ist aufgetreten",
List.of("Bitte versuchen Sie es später erneut oder kontaktieren Sie den Support"), correlationId)
);
}
public record ApiErrorResponse(
Instant timestamp,
String message,
List<String> errors
List<String> errors,
String correlationId
) {
}
}