Przeglądaj źródła

增加配置,限制允许预览的本地文件夹 #304

陈精华 3 lat temu
rodzic
commit
9d65c999e5

+ 8 - 2
server/src/main/config/application.properties

@@ -22,9 +22,15 @@ office.plugin.server.ports = 2001,2002
 ## office 转换服务 task 超时时间,默认五分钟
 office.plugin.task.timeout = 5m
 
-#文件资源路径(默认为打包根路径下的file目录下)
+#预览生成资源路径(默认为打包根路径下的file目录下)
 #file.dir = D:\\kkFileview\\
 file.dir = ${KK_FILE_DIR:default}
+
+#允许预览的本地文件夹 默认不允许任何本地文件被预览
+#file.dir = D:\\kkFileview\\
+local.preview.dir = ${KK_LOCAL_PREVIEW_DIR:default}
+
+
 #openoffice home路径
 #office.home = C:\\Program Files (x86)\\OpenOffice 4
 office.home = ${KK_OFFICE_HOME:default}
@@ -66,7 +72,7 @@ office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
 office.preview.switch.disabled = ${KK_OFFICE_PREVIEW_SWITCH_DISABLED:false}
 
 #是否禁止下载转换生成的pdf文件
-pdf.download.disable = ${KK_PDF_DOWNLOAD_DISABLE:true}
+pdf.download.disable = ${KK_PDF_DOWNLOAD_DISABLE:false}
 #是否禁用首页文件上传
 file.upload.disable = ${KK_FILE_UPLOAD_ENABLED:false}
 

+ 20 - 0
server/src/main/java/cn/keking/config/ConfigConstants.java

@@ -33,6 +33,7 @@ public class ConfigConstants {
     private static String ftpControlEncoding;
     private static String baseUrl;
     private static String fileDir = ConfigUtils.getHomePath() + File.separator + "file" + File.separator;
+    private static String localPreviewDir;
     private static CopyOnWriteArraySet<String> trustHostSet;
     private static String pdfDownloadDisable;
     private static Boolean fileUploadDisable;
@@ -47,6 +48,7 @@ public class ConfigConstants {
     public static final String DEFAULT_FTP_CONTROL_ENCODING = "UTF-8";
     public static final String DEFAULT_BASE_URL = "default";
     public static final String DEFAULT_FILE_DIR_VALUE = "default";
+    public static final String DEFAULT_LOCAL_PREVIEW_DIR_VALUE = "default";
     public static final String DEFAULT_TRUST_HOST = "default";
     public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true";
     public static final String DEFAULT_FILE_UPLOAD_DISABLE = "false";
@@ -203,6 +205,24 @@ public class ConfigConstants {
         }
     }
 
+    public static String getLocalPreviewDir() {
+        return localPreviewDir;
+    }
+
+    @Value("${local.preview.dir:default}")
+    public void setLocalPreviewDir(String localPreviewDir) {
+        setLocalPreviewDirValue(localPreviewDir);
+    }
+
+    public static void setLocalPreviewDirValue(String localPreviewDir) {
+        if (!DEFAULT_LOCAL_PREVIEW_DIR_VALUE.equals(localPreviewDir)) {
+            if (!localPreviewDir.endsWith(File.separator)) {
+                localPreviewDir = localPreviewDir + File.separator;
+            }
+        }
+        ConfigConstants.localPreviewDir = localPreviewDir;
+    }
+
     @Value("${trust.host:default}")
     public void setTrustHost(String trustHost) {
         setTrustHostValue(trustHost);

+ 8 - 0
server/src/main/java/cn/keking/utils/WebUtils.java

@@ -81,6 +81,14 @@ public class WebUtils {
      * @return 文件名
      */
     public static String getFileNameFromURL(String url) {
+        if (url.toLowerCase().startsWith("file:")) {
+            try {
+                URL urlObj = new URL(url);
+                url = urlObj.getPath().substring(1);
+            } catch (MalformedURLException e) {
+                e.printStackTrace();
+            }
+        }
         // 因为url的参数中可能会存在/的情况,所以直接url.lastIndexOf("/")会有问题
         // 所以先从?处将url截断,然后运用url.lastIndexOf("/")获取文件名
         String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());

+ 39 - 0
server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java

@@ -1,5 +1,6 @@
 package cn.keking.web.controller;
 
+import cn.keking.config.ConfigConstants;
 import cn.keking.model.FileAttribute;
 import cn.keking.service.FilePreview;
 import cn.keking.service.FilePreviewFactory;
@@ -12,6 +13,7 @@ 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,9 +27,11 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.*;
 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;
 
@@ -61,6 +65,9 @@ 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);
@@ -86,8 +93,14 @@ 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;
@@ -105,6 +118,12 @@ 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) {
@@ -125,4 +144,24 @@ 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;
+        }
+    }
+
 }

+ 1 - 2
server/src/main/resources/web/fileNotSupported.ftl

@@ -32,8 +32,7 @@
 <div class="container">
     <img src="images/sorry.jpg" />
     <span>
-    该文件类型(${file.suffix?html})系统暂时不支持在线预览,<b>说明</b>:      
-    
+    该文件类型(${fileType})系统暂时不支持在线预览,<b>说明</b>:
         <p style="color: red;">${msg}</p>
         有任何疑问,请加&nbsp;<a href="https://jq.qq.com/?_wv=1027&k=5c0UAtu">官方QQ群:613025121</a>&nbsp;咨询
     </span>