Add POST /api/auth/login endpoint that authenticates users by email and password, returning a JWT token on success. Also fixes a critical bug where expired or malformed JWT tokens in the Authorization header caused unhandled exceptions, crashing requests to all endpoints including public ones like registration. Changes: - Add AuthController.login() endpoint with LoginRequest DTO - Add UserService.authenticate() that validates credentials and throws InvalidCredentialsException on failure - Add InvalidCredentialsException and GlobalExceptionHandler handler that maps it to 401 with Swedish error message - Fix JwtAuthenticationFilter to catch JwtException (expired, malformed) and pass through without crashing — the filter now acts as a graceful enricher rather than a gatekeeper - Add 5 controller tests for login endpoint (success, 401, validation) - Add 4 service tests for authenticate() (success, email not found, password mismatch, email normalization) - Add 2 filter tests for expired and malformed token pass-through
49 lines
2 KiB
Java
49 lines
2 KiB
Java
package se.bilhalsning.exception;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
|
import se.bilhalsning.dto.ErrorResponse;
|
|
|
|
@RestControllerAdvice
|
|
public class GlobalExceptionHandler {
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
|
|
|
@ExceptionHandler(InvalidCredentialsException.class)
|
|
public ResponseEntity<ErrorResponse> handleInvalidCredentials(InvalidCredentialsException ex) {
|
|
return ResponseEntity
|
|
.status(HttpStatus.UNAUTHORIZED)
|
|
.body(new ErrorResponse(ex.getMessage()));
|
|
}
|
|
|
|
@ExceptionHandler(EmailAlreadyExistsException.class)
|
|
public ResponseEntity<ErrorResponse> handleEmailAlreadyExists(EmailAlreadyExistsException ex) {
|
|
return ResponseEntity
|
|
.status(HttpStatus.CONFLICT)
|
|
.body(new ErrorResponse("E-postadressen är redan registrerad"));
|
|
}
|
|
|
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
|
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
|
|
String message = ex.getBindingResult().getFieldErrors().stream()
|
|
.map(e -> e.getField() + ": " + e.getDefaultMessage())
|
|
.reduce((a, b) -> a + ", " + b)
|
|
.orElse("Ogiltig indata");
|
|
return ResponseEntity
|
|
.badRequest()
|
|
.body(new ErrorResponse(message));
|
|
}
|
|
|
|
@ExceptionHandler(Exception.class)
|
|
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
|
|
log.error("Unhandled exception", ex);
|
|
return ResponseEntity
|
|
.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
|
.body(new ErrorResponse("Ett internt fel uppstod"));
|
|
}
|
|
}
|