diff --git a/server/src/main/java/cn/keking/config/WebConfig.java b/server/src/main/java/cn/keking/config/WebConfig.java index 43a584f2..09029f2a 100644 --- a/server/src/main/java/cn/keking/config/WebConfig.java +++ b/server/src/main/java/cn/keking/config/WebConfig.java @@ -1,11 +1,17 @@ package cn.keking.config; +import cn.keking.web.filter.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import java.util.HashSet; +import java.util.Set; + /** * @author: chenjh * @since: 2019/4/16 20:04 @@ -23,4 +29,63 @@ public class WebConfig implements WebMvcConfigurer { LOGGER.info("Add resource locations: {}", filePath); registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/","classpath:/resources/","classpath:/static/","classpath:/public/","file:" + filePath); } + + @Bean + public FilterRegistrationBean getChinesePathFilter() { + ChinesePathFilter filter = new ChinesePathFilter(); + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(filter); + return registrationBean; + } + + @Bean + public FilterRegistrationBean getTrustHostFilter() { + Set filterUri = new HashSet<>(); + filterUri.add("/onlinePreview"); + filterUri.add("/picturesPreview"); + TrustHostFilter filter = new TrustHostFilter(); + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(filter); + registrationBean.setUrlPatterns(filterUri); + return registrationBean; + } + + @Bean + public FilterRegistrationBean getTrustDirFilter() { + Set filterUri = new HashSet<>(); + filterUri.add("/onlinePreview"); + filterUri.add("/picturesPreview"); + filterUri.add("/getCorsFile"); + TrustDirFilter filter = new TrustDirFilter(); + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(filter); + registrationBean.setUrlPatterns(filterUri); + return registrationBean; + } + + @Bean + public FilterRegistrationBean getBaseUrlFilter() { + Set filterUri = new HashSet<>(); + filterUri.add("/index"); + filterUri.add("/onlinePreview"); + filterUri.add("/picturesPreview"); + BaseUrlFilter filter = new BaseUrlFilter(); + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(filter); + registrationBean.setUrlPatterns(filterUri); + return registrationBean; + } + + @Bean + public FilterRegistrationBean getWatermarkConfigFilter() { + Set filterUri = new HashSet<>(); + filterUri.add("/index"); + filterUri.add("/onlinePreview"); + filterUri.add("/picturesPreview"); + AttributeSetFilter filter = new AttributeSetFilter(); + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(filter); + registrationBean.setUrlPatterns(filterUri); + return registrationBean; + } } diff --git a/server/src/main/java/cn/keking/utils/WebUtils.java b/server/src/main/java/cn/keking/utils/WebUtils.java index b2bce0b3..38472eaf 100644 --- a/server/src/main/java/cn/keking/utils/WebUtils.java +++ b/server/src/main/java/cn/keking/utils/WebUtils.java @@ -1,11 +1,15 @@ package cn.keking.utils; import io.mola.galimatias.GalimatiasParseException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.Base64Utils; +import javax.servlet.ServletRequest; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -17,6 +21,7 @@ public class WebUtils { /** * 获取标准的URL + * * @param urlStr url * @return 标准的URL */ @@ -138,4 +143,46 @@ public class WebUtils { } return url.substring(0, fileNameStartIndex) + encodedFileName + url.substring(fileNameEndIndex); } + + /** + * 从 ServletRequest 获取预览的源 url , 已 base64 解码 + * + * @param request 请求 request + * @return url + */ + public static String getSourceUrl(ServletRequest request) { + String url = request.getParameter("url"); + String urls = request.getParameter("urls"); + String currentUrl = request.getParameter("currentUrl"); + String urlPath = request.getParameter("urlPath"); + if (StringUtils.isNotBlank(url)) { + return new String(Base64Utils.decodeFromString(url), StandardCharsets.UTF_8); + } + if (StringUtils.isNotBlank(currentUrl)) { + return new String(Base64Utils.decodeFromString(currentUrl), StandardCharsets.UTF_8); + } + if (StringUtils.isNotBlank(urlPath)) { + return new String(Base64Utils.decodeFromString(urlPath), StandardCharsets.UTF_8); + } + if (StringUtils.isNotBlank(urls)) { + urls = new String(Base64Utils.decodeFromString(urls), StandardCharsets.UTF_8); + String[] images = urls.split("\\|"); + return images[0]; + } + return null; + } + + /** + * 获取 url 的 host + * @param urlStr url + * @return host + */ + public static String getHost(String urlStr) { + try { + URL url = new URL(urlStr); + return url.getHost().toLowerCase(); + } catch (MalformedURLException ignored) { + } + return null; + } } diff --git a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java index 42804746..b6e88360 100644 --- a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java +++ b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java @@ -1,19 +1,16 @@ package cn.keking.web.controller; -import cn.keking.config.ConfigConstants; import cn.keking.model.FileAttribute; +import cn.keking.service.FileHandlerService; import cn.keking.service.FilePreview; import cn.keking.service.FilePreviewFactory; - import cn.keking.service.cache.CacheService; import cn.keking.service.impl.OtherFilePreviewImpl; -import cn.keking.service.FileHandlerService; import cn.keking.utils.WebUtils; import fr.opensagres.xdocreport.core.io.IOUtils; import io.mola.galimatias.GalimatiasParseException; import jodd.io.NetUtil; import org.apache.commons.codec.binary.Base64; -import org.artofsolving.jodconverter.util.PlatformUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; @@ -25,13 +22,12 @@ import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.*; +import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URL; -import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; -import java.util.Locale; import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE; @@ -65,9 +61,6 @@ public class OnlinePreviewController { String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url"); return otherFilePreview.notSupportedFile(model, errorMsg); } - if (!allowPreview(fileUrl)) { - return otherFilePreview.notSupportedFile(model, "该文件不允许预览:" + fileUrl); - } FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req); model.addAttribute("file", fileAttribute); FilePreview filePreview = previewFactory.get(fileAttribute); @@ -93,14 +86,8 @@ public class OnlinePreviewController { String currentUrl = req.getParameter("currentUrl"); if (StringUtils.hasText(currentUrl)) { String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl)); - if (!allowPreview(decodedCurrentUrl)) { - return otherFilePreview.notSupportedFile(model, "该文件不允许预览:" + decodedCurrentUrl); - } model.addAttribute("currentUrl", decodedCurrentUrl); } else { - if (!allowPreview(imgUrls.get(0))) { - return otherFilePreview.notSupportedFile(model, "该文件不允许预览:" + imgUrls.get(0)); - } model.addAttribute("currentUrl", imgUrls.get(0)); } return PICTURE_FILE_PREVIEW_PAGE; @@ -118,12 +105,6 @@ public class OnlinePreviewController { logger.info("下载跨域pdf文件url:{}", urlPath); try { URL url = WebUtils.normalizedURL(urlPath); - if (!allowPreview(urlPath)) { - response.setHeader("content-type", "text/html;charset=utf-8"); - response.getOutputStream().println("forbidden"); - response.setStatus(401); - return; - } byte[] bytes = NetUtil.downloadBytes(url.toString()); IOUtils.write(bytes, response.getOutputStream()); } catch (IOException | GalimatiasParseException e) { @@ -144,24 +125,6 @@ public class OnlinePreviewController { return "success"; } - private boolean allowPreview(String urlPath) { - try { - URL url = WebUtils.normalizedURL(urlPath); - if ("file".equals(url.getProtocol().toLowerCase(Locale.ROOT))) { - String filePath = URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8.name()); - if (PlatformUtils.isWindows()) { - filePath = filePath.replaceAll("/", "\\\\"); - } - filePath = filePath.substring(1); - if (!filePath.startsWith(ConfigConstants.getFileDir()) && !filePath.startsWith(ConfigConstants.getLocalPreviewDir())) { - return false; - } - } - return true; - } catch (IOException | GalimatiasParseException e) { - logger.error("解析URL异常,url:{}", urlPath, e); - return false; - } - } + } diff --git a/server/src/main/java/cn/keking/web/filter/FilterConfiguration.java b/server/src/main/java/cn/keking/web/filter/FilterConfiguration.java deleted file mode 100644 index 4e6ab288..00000000 --- a/server/src/main/java/cn/keking/web/filter/FilterConfiguration.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.keking.web.filter; - -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.HashSet; -import java.util.Set; - - -/** - * - * @author yudian-it - * @date 2017/11/30 - */ -@Configuration -public class FilterConfiguration { - - - @Bean - public FilterRegistrationBean getChinesePathFilter() { - ChinesePathFilter filter = new ChinesePathFilter(); - FilterRegistrationBean registrationBean = new FilterRegistrationBean(); - registrationBean.setFilter(filter); - return registrationBean; - } - - @Bean - public FilterRegistrationBean getTrustHostFilter() { - Set filterUri = new HashSet<>(); - filterUri.add("/onlinePreview"); - filterUri.add("/picturesPreview"); - TrustHostFilter filter = new TrustHostFilter(); - FilterRegistrationBean registrationBean = new FilterRegistrationBean(); - registrationBean.setFilter(filter); - registrationBean.setUrlPatterns(filterUri); - return registrationBean; - } - - @Bean - public FilterRegistrationBean getBaseUrlFilter() { - Set filterUri = new HashSet<>(); - filterUri.add("/index"); - filterUri.add("/onlinePreview"); - filterUri.add("/picturesPreview"); - BaseUrlFilter filter = new BaseUrlFilter(); - FilterRegistrationBean registrationBean = new FilterRegistrationBean(); - registrationBean.setFilter(filter); - registrationBean.setUrlPatterns(filterUri); - return registrationBean; - } - - @Bean - public FilterRegistrationBean getWatermarkConfigFilter() { - Set filterUri = new HashSet<>(); - filterUri.add("/index"); - filterUri.add("/onlinePreview"); - filterUri.add("/picturesPreview"); - AttributeSetFilter filter = new AttributeSetFilter(); - FilterRegistrationBean registrationBean = new FilterRegistrationBean(); - registrationBean.setFilter(filter); - registrationBean.setUrlPatterns(filterUri); - return registrationBean; - } -} diff --git a/server/src/main/java/cn/keking/web/filter/TrustDirFilter.java b/server/src/main/java/cn/keking/web/filter/TrustDirFilter.java new file mode 100644 index 00000000..ec0cee40 --- /dev/null +++ b/server/src/main/java/cn/keking/web/filter/TrustDirFilter.java @@ -0,0 +1,73 @@ +package cn.keking.web.filter; + +import cn.keking.config.ConfigConstants; +import cn.keking.utils.WebUtils; +import io.mola.galimatias.GalimatiasParseException; +import org.artofsolving.jodconverter.util.PlatformUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.FileCopyUtils; + +import javax.servlet.*; +import java.io.IOException; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +/** + * @author : kl (http://kailing.pub) + * @since : 2022-05-25 17:45 + */ +public class TrustDirFilter implements Filter { + + private String notTrustDirView; + private final Logger logger = LoggerFactory.getLogger(TrustDirFilter.class); + + + @Override + public void init(FilterConfig filterConfig) { + ClassPathResource classPathResource = new ClassPathResource("web/notTrustDir.html"); + try { + classPathResource.getInputStream(); + byte[] bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream()); + this.notTrustDirView = new String(bytes, StandardCharsets.UTF_8); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + String url = WebUtils.getSourceUrl(request); + if (!allowPreview(url)) { + response.getWriter().write(this.notTrustDirView); + response.getWriter().close(); + } else { + chain.doFilter(request, response); + } + } + + @Override + public void destroy() { + + } + + private boolean allowPreview(String urlPath) { + try { + URL url = WebUtils.normalizedURL(urlPath); + if ("file".equals(url.getProtocol().toLowerCase(Locale.ROOT))) { + String filePath = URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8.name()); + if (PlatformUtils.isWindows()) { + filePath = filePath.replaceAll("/", "\\\\"); + } + return filePath.startsWith(ConfigConstants.getFileDir()) || filePath.startsWith(ConfigConstants.getLocalPreviewDir()); + } + return true; + } catch (IOException | GalimatiasParseException e) { + logger.error("解析URL异常,url:{}", urlPath, e); + return false; + } + } +} diff --git a/server/src/main/java/cn/keking/web/filter/TrustHostFilter.java b/server/src/main/java/cn/keking/web/filter/TrustHostFilter.java index 5d09feac..fc26c3cd 100644 --- a/server/src/main/java/cn/keking/web/filter/TrustHostFilter.java +++ b/server/src/main/java/cn/keking/web/filter/TrustHostFilter.java @@ -1,6 +1,7 @@ package cn.keking.web.filter; import cn.keking.config.ConfigConstants; +import cn.keking.utils.WebUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.core.io.ClassPathResource; import org.springframework.util.Base64Utils; @@ -34,11 +35,8 @@ public class TrustHostFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - String url = getSourceUrl(request); - if(url != null){ - url = new String(Base64Utils.decodeFromString(url), StandardCharsets.UTF_8); - } - String host = getHost(url); + String url = WebUtils.getSourceUrl(request); + String host = WebUtils.getHost(url); if (host != null &&!ConfigConstants.getTrustHostSet().isEmpty() && !ConfigConstants.getTrustHostSet().contains(host)) { String html = this.notTrustHost.replace("${current_host}", host); response.getWriter().write(html); @@ -52,28 +50,4 @@ public class TrustHostFilter implements Filter { } - private String getSourceUrl(ServletRequest request) { - String url = request.getParameter("url"); - String currentUrl = request.getParameter("currentUrl"); - String urlPath = request.getParameter("urlPath"); - if (StringUtils.isNotBlank(url)) { - return url; - } - if (StringUtils.isNotBlank(currentUrl)) { - return currentUrl; - } - if (StringUtils.isNotBlank(urlPath)) { - return urlPath; - } - return null; - } - - private String getHost(String urlStr) { - try { - URL url = new URL(urlStr); - return url.getHost().toLowerCase(); - } catch (MalformedURLException ignored) { - } - return null; - } } diff --git a/server/src/main/resources/web/notTrustDir.html b/server/src/main/resources/web/notTrustDir.html new file mode 100644 index 00000000..c899368b --- /dev/null +++ b/server/src/main/resources/web/notTrustDir.html @@ -0,0 +1,41 @@ + + + + + + + + +
+ +

+ 预览源文件来自未授信的目录 ,请停止访问
+ 有任何疑问,请加 官方QQ群:613025121 咨询 +

+
+ + + \ No newline at end of file