fi.evolver.basics.spring.http.HttpInterceptor Maven / Gradle / Ivy
package fi.evolver.basics.spring.http;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import fi.evolver.basics.spring.auth.AuthorizationService;
import fi.evolver.basics.spring.job.ResultState;
import fi.evolver.basics.spring.job.entity.TaskStatus.TaskState;
import fi.evolver.basics.spring.log.LogPolicy;
import fi.evolver.basics.spring.log.LogPolicy.Policy;
import fi.evolver.basics.spring.log.entity.MessageLogMetadata;
import fi.evolver.utils.ContextUtils;
import fi.evolver.utils.attribute.ContextAttribute;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@Component
public class HttpInterceptor implements HandlerInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(HttpInterceptor.class);
public static final ContextAttribute CONTEXT_SOURCE_SYSTEM = new ContextAttribute<>(HttpLoggerFilter.class.getName() + ".SourceSystem", String.class);
public static final ContextAttribute CONTEXT_BEAN_TYPE_NAME = new ContextAttribute<>(HttpLoggerFilter.class.getName() + ".BeanTypeName", String.class);
public static final ContextAttribute CONTEXT_LOG_META_DATA = new ContextAttribute<>(HttpLoggerFilter.class.getName() + ".LogMetadata", MetadataHolder.class);
public static final ContextAttribute CONTEXT_LOG_POLICY = new ContextAttribute<>(HttpLoggerFilter.class.getName() + ".LogPolicy", Policy.class);
public static final ContextAttribute CONTEXT_MESSAGE_TYPE = new ContextAttribute<>(HttpLoggerFilter.class.getName() + ".MessageType", String.class);
public static final ContextAttribute CONTEXT_RESULT_STATE = new ContextAttribute<>(HttpLoggerFilter.class.getName() + ".ResultState", ResultState.class);
public static final ContextAttribute CONTEXT_AUTH_USER = new ContextAttribute<>(HttpLoggerFilter.class.getName() + ".AuthUser", String.class);
public static final String ATTRIBUTE_AUTH_USER = CONTEXT_AUTH_USER.name();
public static final String ATTRIBUTE_SOURCE_SYSTEM = CONTEXT_SOURCE_SYSTEM.name();
private static final ResultState UNAUTHORIZED_ACCESS = ResultState.failed("Unauthorized access");
@Autowired
private AuthorizationService authorizationService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!ContextUtils.withinContext())
return false;
String user = (String)request.getAttribute(ATTRIBUTE_AUTH_USER);
if (user != null)
CONTEXT_AUTH_USER.set(user);
if (handler instanceof HandlerMethod method) {
CONTEXT_BEAN_TYPE_NAME.set(method.getBeanType().getName());
authorizationService.authenticate(request.getHeader("Authorization"));
String permission = String.format("%s::%s", method.getMethod().getDeclaringClass().getSimpleName(), method.getMethod().getName());
if (!authorizationService.hasPermission(permission)) {
response.sendError(403);
CONTEXT_RESULT_STATE.set(UNAUTHORIZED_ACCESS);
return false;
}
}
return true;
}
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
if (ContextUtils.withinContext()) {
CONTEXT_AUTH_USER.get().ifPresent(u -> addMetadata("AuthUser", u));
if (handler instanceof HandlerMethod method) {
CONTEXT_LOG_POLICY.computeIfAbsent(() -> getLogPolicy(method));
CONTEXT_MESSAGE_TYPE.computeIfAbsent(() -> getMessageType(method));
CONTEXT_RESULT_STATE.set(inferEndState(response, ex));
}
}
}
private static ResultState inferEndState(HttpServletResponse response, Exception ex) {
Optional resultState = CONTEXT_RESULT_STATE.get();
if (ex != null && resultState.map(ResultState::getState).map(TaskState::isSuccess).orElse(true))
return ResultState.failed("%s", ex);
if (resultState.isPresent())
return resultState.get();
HttpStatus status = HttpStatus.resolve(response.getStatus());
String reason = status != null ? status.getReasonPhrase() : "?";
if (response.getStatus() >= 400)
return ResultState.failed("%s", reason);
return ResultState.ok("%s", reason);
}
private static Policy getLogPolicy(HandlerMethod method) {
return Optional.ofNullable(method.getMethodAnnotation(LogPolicy.class))
.map(LogPolicy::value)
.orElse(Policy.FULL);
}
private static String getMessageType(HandlerMethod method) {
return Optional.ofNullable(method.getMethodAnnotation(MessageType.class))
.map(MessageType::value)
.orElse("?");
}
/**
* Sets the message type of the incoming HTTP request for logging purposes.
*
* @see MessageType for a simple declarative approach.
*
* @param messageType The type of the incoming message.
*/
public static void setMessageType(String messageType) {
CONTEXT_MESSAGE_TYPE.set(messageType);
}
/**
* Sets the log policy of the incoming HTTP request.
*
* @see LogPolicy for a simple declarative approach.
*
* @param policy At what level should the message be logged if at all.
*/
public static void setLogPolicy(Policy policy) {
CONTEXT_LOG_POLICY.set(policy);
}
/**
* Sets the result state of the incoming HTTP request for logging purposes.
*
* @param resultState The result state.
*/
public static void setResultState(ResultState resultState) {
CONTEXT_RESULT_STATE.set(resultState);
}
/**
* Add message log metadata for the incoming HTTP request.
* If either the key or the value is null, no metadata is added.
*
* @param key Key for the metadata.
* @param value Value for the metadata.
*/
public static void addMetadata(String key, Object value) {
if (key == null || value == null)
return;
try {
CONTEXT_LOG_META_DATA.computeIfAbsent(MetadataHolder::new).metadata().put(key, value.toString());
}
catch (RuntimeException e) {
LOG.warn("Failed adding metadata {}='{}'", key, value, e);
}
}
record MetadataHolder(Map metadata) {
MetadataHolder() {
this(new LinkedHashMap<>());
}
List toMessageLogMetadata() {
List results = new ArrayList<>(metadata.size());
metadata.forEach((k, v) -> results.add(new MessageLogMetadata(k, v)));
return results;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy