package com.madeu.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.resource.PathResourceResolver; import java.io.IOException; @Configuration public class WebConfig implements WebMvcConfigurer { @Value("${file.upload-path}") private String uploadPath; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/cdn/**") .addResourceLocations("file:" + uploadPath + "/") .setCachePeriod(3600) // 1시간 캐싱 .resourceChain(true) .addResolver(new PathResourceResolver() { @Override protected Resource getResource(String resourcePath, Resource location) throws IOException { Resource requestedResource = location.createRelative(resourcePath); // 보안 검증: 허용된 파일 타입만 if (requestedResource.exists() && requestedResource.isReadable() && isAllowedResource(requestedResource)) { return requestedResource; } return null; } private boolean isAllowedResource(Resource resource) { try { String filename = resource.getFilename(); return filename != null && (filename.toLowerCase().endsWith(".jpg") || filename.toLowerCase().endsWith(".jpeg") || filename.toLowerCase().endsWith(".png") || filename.toLowerCase().endsWith(".gif") || filename.toLowerCase().endsWith(".webp") || filename.toLowerCase().endsWith(".bmp") || filename.toLowerCase().endsWith(".svg")); } catch (Exception e) { return false; } } }); } }