Commit 2144b776 authored by 陈精华's avatar 陈精华 Committed by kl

【新特性】支持限制预览源站点,保护预览服务不被滥用

parent a8022df1
...@@ -40,6 +40,10 @@ cache.clean.cron = ${KK_CACHE_CLEAN_CRON:0 0 3 * * ?} ...@@ -40,6 +40,10 @@ cache.clean.cron = ${KK_CACHE_CLEAN_CRON:0 0 3 * * ?}
#base.url = https://file.keking.cn #base.url = https://file.keking.cn
base.url = ${KK_BASE_URL:default} base.url = ${KK_BASE_URL:default}
#信任站点,多个用','隔开,设置了之后,会限制只能预览来自信任站点列表的文件,默认不限制
#trust.host = file.keking.cn,kkfileview.keking.cn
trust.host = ${KK_TRUST_HOST:default}
#是否启用缓存 #是否启用缓存
cache.enabled = ${KK_CACHE_ENABLED:true} cache.enabled = ${KK_CACHE_ENABLED:true}
......
...@@ -5,6 +5,9 @@ import org.springframework.beans.factory.annotation.Value; ...@@ -5,6 +5,9 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/** /**
* @auther: chenjh * @auther: chenjh
...@@ -23,8 +26,19 @@ public class ConfigConstants { ...@@ -23,8 +26,19 @@ public class ConfigConstants {
private static String ftpControlEncoding; private static String ftpControlEncoding;
private static String fileDir = OfficeUtils.getHomePath() + File.separator + "file" + File.separator; private static String fileDir = OfficeUtils.getHomePath() + File.separator + "file" + File.separator;
private static String baseUrl; private static String baseUrl;
private static String trustHost;
private static Set<String> trustHostSet;
public static final String DEFAULT_CACHE_ENABLED = "true";
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd";
public static final String DEFAULT_MEDIA_TYPE = "mp3,wav,mp4,flv";
public static final String DEFAULT_FILE_DIR_VALUE = "default"; public static final String DEFAULT_FILE_DIR_VALUE = "default";
public static final String DEFAULT_FTP_USERNAME = null;
public static final String DEFAULT_FTP_PASSWORD = null;
public static final String DEFAULT_FTP_CONTROL_ENCODING = "UTF-8";
public static final String DEFAULT_OFFICE_PREVIEW_TYPE = "image";
public static final String DEFAULT_BASE_URL = "default";
public static final String DEFAULT_TRUST_HOST = "default";
public static Boolean isCacheEnabled() { public static Boolean isCacheEnabled() {
return cacheEnabled; return cacheEnabled;
...@@ -104,4 +118,29 @@ public class ConfigConstants { ...@@ -104,4 +118,29 @@ public class ConfigConstants {
} }
} }
static String getTrustHost() {
return trustHost;
}
@Value("${trust.host:default}")
static void setTrustHost(String trustHost) {
ConfigConstants.trustHost = trustHost;
Set<String> trustHostSet;
if (DEFAULT_TRUST_HOST.equals(trustHost.toLowerCase())) {
trustHostSet = new HashSet<>();
} else {
String[] trustHostArray = trustHost.toLowerCase().split(",");
trustHostSet = new HashSet<>(Arrays.asList(trustHostArray));
ConfigConstants.setTrustHostSet(trustHostSet);
}
ConfigConstants.setTrustHostSet(trustHostSet);
}
public static Set<String> getTrustHostSet() {
return trustHostSet;
}
private static void setTrustHostSet(Set<String> trustHostSet) {
ConfigConstants.trustHostSet = trustHostSet;
}
} }
...@@ -22,15 +22,6 @@ public class ConfigRefreshComponent { ...@@ -22,15 +22,6 @@ public class ConfigRefreshComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigRefreshComponent.class); private static final Logger LOGGER = LoggerFactory.getLogger(ConfigRefreshComponent.class);
public static final String DEFAULT_CACHE_ENABLED = "true";
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd";
public static final String DEFAULT_MEDIA_TYPE = "mp3,wav,mp4,flv";
public static final String DEFAULT_FTP_USERNAME = null;
public static final String DEFAULT_FTP_PASSWORD = null;
public static final String DEFAULT_FTP_CONTROL_ENCODING = "UTF-8";
public static final String DEFAULT_BASE_URL = "default";
@PostConstruct @PostConstruct
void refresh() { void refresh() {
Thread configRefreshThread = new Thread(new ConfigRefreshThread()); Thread configRefreshThread = new Thread(new ConfigRefreshThread());
...@@ -53,21 +44,23 @@ public class ConfigRefreshComponent { ...@@ -53,21 +44,23 @@ public class ConfigRefreshComponent {
String ftpControlEncoding; String ftpControlEncoding;
String configFilePath = OfficeUtils.getCustomizedConfigPath(); String configFilePath = OfficeUtils.getCustomizedConfigPath();
String baseUrl; String baseUrl;
String trustHost;
while (true) { while (true) {
FileReader fileReader = new FileReader(configFilePath); FileReader fileReader = new FileReader(configFilePath);
BufferedReader bufferedReader = new BufferedReader(fileReader); BufferedReader bufferedReader = new BufferedReader(fileReader);
properties.load(bufferedReader); properties.load(bufferedReader);
OfficeUtils.restorePropertiesFromEnvFormat(properties); OfficeUtils.restorePropertiesFromEnvFormat(properties);
cacheEnabled = new Boolean(properties.getProperty("cache.enabled", DEFAULT_CACHE_ENABLED)); cacheEnabled = new Boolean(properties.getProperty("cache.enabled", ConfigConstants.DEFAULT_CACHE_ENABLED));
text = properties.getProperty("simText", DEFAULT_TXT_TYPE); text = properties.getProperty("simText", ConfigConstants.DEFAULT_TXT_TYPE);
media = properties.getProperty("media", DEFAULT_MEDIA_TYPE); media = properties.getProperty("media", ConfigConstants.DEFAULT_MEDIA_TYPE);
officePreviewType = properties.getProperty("office.preview.type", OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE); officePreviewType = properties.getProperty("office.preview.type", ConfigConstants.DEFAULT_OFFICE_PREVIEW_TYPE);
ftpUsername = properties.getProperty("ftp.username", DEFAULT_FTP_USERNAME); ftpUsername = properties.getProperty("ftp.username", ConfigConstants.DEFAULT_FTP_USERNAME);
ftpPassword = properties.getProperty("ftp.password", DEFAULT_FTP_PASSWORD); ftpPassword = properties.getProperty("ftp.password", ConfigConstants.DEFAULT_FTP_PASSWORD);
ftpControlEncoding = properties.getProperty("ftp.control.encoding", DEFAULT_FTP_CONTROL_ENCODING); ftpControlEncoding = properties.getProperty("ftp.control.encoding", ConfigConstants.DEFAULT_FTP_CONTROL_ENCODING);
textArray = text.split(","); textArray = text.split(",");
mediaArray = media.split(","); mediaArray = media.split(",");
baseUrl = properties.getProperty("base.url", DEFAULT_BASE_URL); baseUrl = properties.getProperty("base.url", ConfigConstants.DEFAULT_BASE_URL);
trustHost = properties.getProperty("trust.host", ConfigConstants.DEFAULT_TRUST_HOST);
ConfigConstants.setCacheEnabled(cacheEnabled); ConfigConstants.setCacheEnabled(cacheEnabled);
ConfigConstants.setSimText(textArray); ConfigConstants.setSimText(textArray);
ConfigConstants.setMedia(mediaArray); ConfigConstants.setMedia(mediaArray);
...@@ -76,6 +69,7 @@ public class ConfigRefreshComponent { ...@@ -76,6 +69,7 @@ public class ConfigRefreshComponent {
ConfigConstants.setFtpPassword(ftpPassword); ConfigConstants.setFtpPassword(ftpPassword);
ConfigConstants.setFtpControlEncoding(ftpControlEncoding); ConfigConstants.setFtpControlEncoding(ftpControlEncoding);
ConfigConstants.setBaseUrl(baseUrl); ConfigConstants.setBaseUrl(baseUrl);
ConfigConstants.setTrustHost(trustHost);
bufferedReader.close(); bufferedReader.close();
fileReader.close(); fileReader.close();
Thread.sleep(1000L); Thread.sleep(1000L);
......
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;
...@@ -34,16 +33,14 @@ public class OnlinePreviewController { ...@@ -34,16 +33,14 @@ public class OnlinePreviewController {
private static final Logger LOGGER = LoggerFactory.getLogger(OnlinePreviewController.class); private static final Logger LOGGER = LoggerFactory.getLogger(OnlinePreviewController.class);
@Autowired @Autowired
FilePreviewFactory previewFactory; private FilePreviewFactory previewFactory;
@Autowired @Autowired
CacheService cacheService; private CacheService cacheService;
@Autowired @Autowired
private FileUtils fileUtils; private FileUtils fileUtils;
private String fileDir = ConfigConstants.getFileDir();
/** /**
* @param url * @param url
* @param model * @param model
......
package cn.keking.filters; package cn.keking.web.filter;
import cn.keking.config.ConfigConstants; import cn.keking.config.ConfigConstants;
import cn.keking.config.ConfigRefreshComponent;
import javax.servlet.*; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -29,7 +28,7 @@ public class ChinesePathFilter implements Filter { ...@@ -29,7 +28,7 @@ public class ChinesePathFilter implements Filter {
.append(request.getServerPort()).append(((HttpServletRequest) request).getContextPath()).append("/"); .append(request.getServerPort()).append(((HttpServletRequest) request).getContextPath()).append("/");
localBaseUrl = pathBuilder.toString(); localBaseUrl = pathBuilder.toString();
String baseUrlTmp = ConfigConstants.getBaseUrl(); String baseUrlTmp = ConfigConstants.getBaseUrl();
if (baseUrlTmp != null && !ConfigRefreshComponent.DEFAULT_BASE_URL.equals(baseUrlTmp.toLowerCase())) { if (baseUrlTmp != null && !ConfigConstants.DEFAULT_BASE_URL.equals(baseUrlTmp.toLowerCase())) {
if (!baseUrlTmp.endsWith("/")) { if (!baseUrlTmp.endsWith("/")) {
baseUrlTmp = baseUrlTmp.concat("/"); baseUrlTmp = baseUrlTmp.concat("/");
} }
......
package cn.keking.filters; package cn.keking.web.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.util.HashSet;
import java.util.Set;
/** /**
* *
...@@ -13,11 +16,26 @@ import org.springframework.context.annotation.Configuration; ...@@ -13,11 +16,26 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
public class FilterConfiguration { public class FilterConfiguration {
@Bean @Bean
public FilterRegistrationBean getChinesePathFilter(){ public FilterRegistrationBean getChinesePathFilter() {
ChinesePathFilter filter = new ChinesePathFilter(); ChinesePathFilter filter = new ChinesePathFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean(); FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter); registrationBean.setFilter(filter);
return registrationBean; return registrationBean;
} }
@Bean
public FilterRegistrationBean getTrustHostFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
filterUri.add("/getCorsFile");
filterUri.add("/addTask");
TrustHostFilter filter = new TrustHostFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
} }
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import javax.servlet.*;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
/**
* @author chenjh
* @since 2020/2/18 19:13
*/
public class TrustHostFilter implements Filter {
private String notTrustHost;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ClassPathResource classPathResource = new ClassPathResource("web/notTrustHost.html");
try {
classPathResource.getInputStream();
byte[] bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
this.notTrustHost = 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 = getSourceUrl(request);
String host = getHost(url);
if (!ConfigConstants.getTrustHostSet().isEmpty() && !ConfigConstants.getTrustHostSet().contains(host)) {
String html = this.notTrustHost.replace("${current_host}", host);
response.getWriter().write(html);
response.getWriter().close();
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
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 e) {
}
return null;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style type="text/css">
body {
margin: 0 auto;
width: 900px;
background-color: #CCB;
}
.container {
width: 700px;
height: 700px;
margin: 0 auto;
}
img {
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
padding-bottom: 36px;
}
p {
display: block;
font-size: 20px;
color: blue;
}
</style>
</head>
<body>
<div class="container">
<img src="images/sorry.jpg" />
<p>
预览源文件来自不受信任的站点:<span style="color: red; display: inline;">${current_host}</span> ,请联系管理员 <br>
有任何疑问,请加&nbsp;<a href="https://jq.qq.com/?_wv=1027&k=5c0UAtu">官方QQ群:613025121</a>&nbsp;咨询
</p>
</div>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment