增加配置,限制允许预览的本地文件夹 #304
This commit is contained in:
@ -22,9 +22,15 @@ office.plugin.server.ports = 2001,2002
|
|||||||
## office 转换服务 task 超时时间,默认五分钟
|
## office 转换服务 task 超时时间,默认五分钟
|
||||||
office.plugin.task.timeout = 5m
|
office.plugin.task.timeout = 5m
|
||||||
|
|
||||||
#文件资源路径(默认为打包根路径下的file目录下)
|
#预览生成资源路径(默认为打包根路径下的file目录下)
|
||||||
#file.dir = D:\\kkFileview\\
|
#file.dir = D:\\kkFileview\\
|
||||||
file.dir = ${KK_FILE_DIR:default}
|
file.dir = ${KK_FILE_DIR:default}
|
||||||
|
|
||||||
|
#允许预览的本地文件夹 默认不允许任何本地文件被预览
|
||||||
|
#file.dir = D:\\kkFileview\\
|
||||||
|
local.preview.dir = ${KK_LOCAL_PREVIEW_DIR:default}
|
||||||
|
|
||||||
|
|
||||||
#openoffice home路径
|
#openoffice home路径
|
||||||
#office.home = C:\\Program Files (x86)\\OpenOffice 4
|
#office.home = C:\\Program Files (x86)\\OpenOffice 4
|
||||||
office.home = ${KK_OFFICE_HOME:default}
|
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}
|
office.preview.switch.disabled = ${KK_OFFICE_PREVIEW_SWITCH_DISABLED:false}
|
||||||
|
|
||||||
#是否禁止下载转换生成的pdf文件
|
#是否禁止下载转换生成的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}
|
file.upload.disable = ${KK_FILE_UPLOAD_ENABLED:false}
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ public class ConfigConstants {
|
|||||||
private static String ftpControlEncoding;
|
private static String ftpControlEncoding;
|
||||||
private static String baseUrl;
|
private static String baseUrl;
|
||||||
private static String fileDir = ConfigUtils.getHomePath() + File.separator + "file" + File.separator;
|
private static String fileDir = ConfigUtils.getHomePath() + File.separator + "file" + File.separator;
|
||||||
|
private static String localPreviewDir;
|
||||||
private static CopyOnWriteArraySet<String> trustHostSet;
|
private static CopyOnWriteArraySet<String> trustHostSet;
|
||||||
private static String pdfDownloadDisable;
|
private static String pdfDownloadDisable;
|
||||||
private static Boolean fileUploadDisable;
|
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_FTP_CONTROL_ENCODING = "UTF-8";
|
||||||
public static final String DEFAULT_BASE_URL = "default";
|
public static final String DEFAULT_BASE_URL = "default";
|
||||||
public static final String DEFAULT_FILE_DIR_VALUE = "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_TRUST_HOST = "default";
|
||||||
public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true";
|
public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true";
|
||||||
public static final String DEFAULT_FILE_UPLOAD_DISABLE = "false";
|
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}")
|
@Value("${trust.host:default}")
|
||||||
public void setTrustHost(String trustHost) {
|
public void setTrustHost(String trustHost) {
|
||||||
setTrustHostValue(trustHost);
|
setTrustHostValue(trustHost);
|
||||||
|
|||||||
@ -81,6 +81,14 @@ public class WebUtils {
|
|||||||
* @return 文件名
|
* @return 文件名
|
||||||
*/
|
*/
|
||||||
public static String getFileNameFromURL(String url) {
|
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("/")会有问题
|
||||||
// 所以先从?处将url截断,然后运用url.lastIndexOf("/")获取文件名
|
// 所以先从?处将url截断,然后运用url.lastIndexOf("/")获取文件名
|
||||||
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
|
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package cn.keking.web.controller;
|
package cn.keking.web.controller;
|
||||||
|
|
||||||
|
import cn.keking.config.ConfigConstants;
|
||||||
import cn.keking.model.FileAttribute;
|
import cn.keking.model.FileAttribute;
|
||||||
import cn.keking.service.FilePreview;
|
import cn.keking.service.FilePreview;
|
||||||
import cn.keking.service.FilePreviewFactory;
|
import cn.keking.service.FilePreviewFactory;
|
||||||
@ -12,6 +13,7 @@ import fr.opensagres.xdocreport.core.io.IOUtils;
|
|||||||
import io.mola.galimatias.GalimatiasParseException;
|
import io.mola.galimatias.GalimatiasParseException;
|
||||||
import jodd.io.NetUtil;
|
import jodd.io.NetUtil;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.artofsolving.jodconverter.util.PlatformUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
@ -25,9 +27,11 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLDecoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE;
|
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");
|
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
|
||||||
return otherFilePreview.notSupportedFile(model, errorMsg);
|
return otherFilePreview.notSupportedFile(model, errorMsg);
|
||||||
}
|
}
|
||||||
|
if (!allowPreview(fileUrl)) {
|
||||||
|
return otherFilePreview.notSupportedFile(model, "该文件不允许预览:" + fileUrl);
|
||||||
|
}
|
||||||
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req);
|
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req);
|
||||||
model.addAttribute("file", fileAttribute);
|
model.addAttribute("file", fileAttribute);
|
||||||
FilePreview filePreview = previewFactory.get(fileAttribute);
|
FilePreview filePreview = previewFactory.get(fileAttribute);
|
||||||
@ -86,8 +93,14 @@ public class OnlinePreviewController {
|
|||||||
String currentUrl = req.getParameter("currentUrl");
|
String currentUrl = req.getParameter("currentUrl");
|
||||||
if (StringUtils.hasText(currentUrl)) {
|
if (StringUtils.hasText(currentUrl)) {
|
||||||
String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl));
|
String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl));
|
||||||
|
if (!allowPreview(decodedCurrentUrl)) {
|
||||||
|
return otherFilePreview.notSupportedFile(model, "该文件不允许预览:" + decodedCurrentUrl);
|
||||||
|
}
|
||||||
model.addAttribute("currentUrl", decodedCurrentUrl);
|
model.addAttribute("currentUrl", decodedCurrentUrl);
|
||||||
} else {
|
} else {
|
||||||
|
if (!allowPreview(imgUrls.get(0))) {
|
||||||
|
return otherFilePreview.notSupportedFile(model, "该文件不允许预览:" + imgUrls.get(0));
|
||||||
|
}
|
||||||
model.addAttribute("currentUrl", imgUrls.get(0));
|
model.addAttribute("currentUrl", imgUrls.get(0));
|
||||||
}
|
}
|
||||||
return PICTURE_FILE_PREVIEW_PAGE;
|
return PICTURE_FILE_PREVIEW_PAGE;
|
||||||
@ -105,6 +118,12 @@ public class OnlinePreviewController {
|
|||||||
logger.info("下载跨域pdf文件url:{}", urlPath);
|
logger.info("下载跨域pdf文件url:{}", urlPath);
|
||||||
try {
|
try {
|
||||||
URL url = WebUtils.normalizedURL(urlPath);
|
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());
|
byte[] bytes = NetUtil.downloadBytes(url.toString());
|
||||||
IOUtils.write(bytes, response.getOutputStream());
|
IOUtils.write(bytes, response.getOutputStream());
|
||||||
} catch (IOException | GalimatiasParseException e) {
|
} catch (IOException | GalimatiasParseException e) {
|
||||||
@ -125,4 +144,24 @@ public class OnlinePreviewController {
|
|||||||
return "success";
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,8 +32,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<img src="images/sorry.jpg" />
|
<img src="images/sorry.jpg" />
|
||||||
<span>
|
<span>
|
||||||
该文件类型(${file.suffix?html})系统暂时不支持在线预览,<b>说明</b>:
|
该文件类型(${fileType})系统暂时不支持在线预览,<b>说明</b>:
|
||||||
|
|
||||||
<p style="color: red;">${msg}</p>
|
<p style="color: red;">${msg}</p>
|
||||||
有任何疑问,请加 <a href="https://jq.qq.com/?_wv=1027&k=5c0UAtu">官方QQ群:613025121</a> 咨询
|
有任何疑问,请加 <a href="https://jq.qq.com/?_wv=1027&k=5c0UAtu">官方QQ群:613025121</a> 咨询
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user