다운로드 호출 로그관리 작업
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package com.kamco.cd.kamcoback.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.kamco.cd.kamcoback.auth.CustomUserDetails;
|
||||
import com.kamco.cd.kamcoback.config.api.ApiLogFunction;
|
||||
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
|
||||
import com.kamco.cd.kamcoback.menu.service.MenuService;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FileDownloadInteceptor implements HandlerInterceptor {
|
||||
|
||||
private final AuditLogRepository auditLogRepository;
|
||||
private final MenuService menuService;
|
||||
|
||||
@Autowired private ObjectMapper objectMapper;
|
||||
|
||||
public FileDownloadInteceptor(AuditLogRepository auditLogRepository, MenuService menuService) {
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
this.menuService = menuService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(
|
||||
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
|
||||
// 파일 다운로드 API만 필터링
|
||||
if (!request.getRequestURI().contains("/download")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Long userId = extractUserId(request);
|
||||
String ip = ApiLogFunction.getClientIp(request);
|
||||
|
||||
List<?> list = menuService.getFindAll();
|
||||
List<MenuDto.Basic> result =
|
||||
list.stream()
|
||||
.map(
|
||||
item -> {
|
||||
if (item instanceof LinkedHashMap<?, ?> map) {
|
||||
return objectMapper.convertValue(map, MenuDto.Basic.class);
|
||||
} else if (item instanceof MenuDto.Basic dto) {
|
||||
return dto;
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported cache type: " + item.getClass());
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
|
||||
String normalizedUri = request.getRequestURI().replace("/api", "");
|
||||
MenuDto.Basic basic =
|
||||
result.stream()
|
||||
.filter(
|
||||
menu -> menu.getMenuUrl() != null && normalizedUri.startsWith(menu.getMenuUrl()))
|
||||
.max(Comparator.comparingInt(m -> m.getMenuUrl().length()))
|
||||
.orElse(null);
|
||||
|
||||
AuditLogEntity log =
|
||||
AuditLogEntity.forFileDownload(
|
||||
userId,
|
||||
request.getRequestURI(),
|
||||
Objects.requireNonNull(basic).getMenuUid(),
|
||||
ip,
|
||||
response.getStatus());
|
||||
|
||||
auditLogRepository.save(log);
|
||||
}
|
||||
|
||||
private Long extractUserId(HttpServletRequest request) {
|
||||
if (request.getUserPrincipal() instanceof UsernamePasswordAuthenticationToken auth
|
||||
&& auth.getPrincipal() instanceof CustomUserDetails userDetails) {
|
||||
return userDetails.getMember().getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,18 @@ import org.locationtech.jts.geom.Polygon;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
private final FileDownloadInteceptor fileDownloadInteceptor;
|
||||
|
||||
public WebConfig(FileDownloadInteceptor fileDownloadInteceptor) {
|
||||
this.fileDownloadInteceptor = fileDownloadInteceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
SimpleModule module = new SimpleModule();
|
||||
@@ -29,4 +36,11 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
return Jackson2ObjectMapperBuilder.json().modulesToInstall(module).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry
|
||||
.addInterceptor(fileDownloadInteceptor)
|
||||
.addPathPatterns("/api/inference/download/**"); // 추론 파일 다운로드 API만 //TODO 추후 추가
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.kamco.cd.kamcoback.log.dto.EventType;
|
||||
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -47,7 +48,7 @@ public class ApiLogFunction {
|
||||
String method = request.getMethod().toUpperCase();
|
||||
String uri = request.getRequestURI().toLowerCase();
|
||||
|
||||
// URL 기반 DOWNLOAD/PRINT 분류
|
||||
// URL 기반 DOWNLOAD/PRINT 분류 -> /download는 FileDownloadInterceptor로 옮김
|
||||
if (uri.contains("/download") || uri.contains("/export")) {
|
||||
return EventType.DOWNLOAD;
|
||||
}
|
||||
@@ -121,13 +122,15 @@ public class ApiLogFunction {
|
||||
|
||||
public static String getUriMenuInfo(List<MenuDto.Basic> menuList, String uri) {
|
||||
|
||||
MenuDto.Basic m =
|
||||
String normalizedUri = uri.replace("/api", "");
|
||||
MenuDto.Basic basic =
|
||||
menuList.stream()
|
||||
.filter(menu -> menu.getMenuApiUrl() != null && uri.contains(menu.getMenuApiUrl()))
|
||||
.findFirst()
|
||||
.filter(
|
||||
menu -> menu.getMenuUrl() != null && normalizedUri.startsWith(menu.getMenuUrl()))
|
||||
.max(Comparator.comparingInt(m -> m.getMenuUrl().length()))
|
||||
.orElse(null);
|
||||
|
||||
return m != null ? m.getMenuUid() : "SYSTEM";
|
||||
return basic != null ? basic.getMenuUid() : "SYSTEM";
|
||||
}
|
||||
|
||||
public static String cutRequestBody(String value) {
|
||||
|
||||
@@ -4,7 +4,14 @@ import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
|
||||
import com.kamco.cd.kamcoback.log.dto.EventStatus;
|
||||
import com.kamco.cd.kamcoback.log.dto.EventType;
|
||||
import com.kamco.cd.kamcoback.postgres.CommonCreateEntity;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -14,6 +21,7 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Table(name = "tb_audit_log")
|
||||
public class AuditLogEntity extends CommonCreateEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "audit_log_uid", nullable = false)
|
||||
@@ -62,6 +70,22 @@ public class AuditLogEntity extends CommonCreateEntity {
|
||||
this.errorLogUid = errorLogUid;
|
||||
}
|
||||
|
||||
/** 파일 다운로드 이력 생성 */
|
||||
public static AuditLogEntity forFileDownload(
|
||||
Long userId, String requestUri, String menuUid, String ip, int httpStatus) {
|
||||
|
||||
return new AuditLogEntity(
|
||||
userId,
|
||||
EventType.DOWNLOAD, // 이벤트 타입 고정
|
||||
httpStatus < 400 ? EventStatus.FAILED : EventStatus.SUCCESS, // 성공 여부
|
||||
menuUid,
|
||||
ip,
|
||||
requestUri,
|
||||
null, // requestBody 없음
|
||||
null // errorLogUid 없음
|
||||
);
|
||||
}
|
||||
|
||||
public AuditLogDto.Basic toDto() {
|
||||
return new AuditLogDto.Basic(
|
||||
this.id,
|
||||
|
||||
Reference in New Issue
Block a user