KeycloakAuthenticationEntryPoint.java
package com.taxonomy.security.keycloak;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Profile;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Map;
/**
* Custom {@link AuthenticationEntryPoint} for the Keycloak/JWT resource server.
* <p>
* Returns a structured JSON 401 response when JWT authentication fails
* (e.g., missing, expired, or invalid token). Invoked by the Spring Security
* filter chain before any controller is reached, ensuring consistent error
* handling for all JWT validation failures.
* <p>
* The error message is generic to avoid leaking internal validation details.
*/
@Component
@Profile("keycloak")
public class KeycloakAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
MAPPER.writeValue(response.getOutputStream(), Map.of(
"error", "Authentication required",
"status", 401
));
}
}