diff --git a/admin/src/assets/favicon.ico b/admin/src/assets/favicon.ico deleted file mode 100644 index cdf5a8d..0000000 Binary files a/admin/src/assets/favicon.ico and /dev/null differ diff --git a/udf-api/udf-api-general/src/main/java/org/itkk/udf/api/general/web/FilePublicController.java b/udf-api/udf-api-general/src/main/java/org/itkk/udf/api/general/web/FilePublicController.java new file mode 100644 index 0000000..828c5d0 --- /dev/null +++ b/udf-api/udf-api-general/src/main/java/org/itkk/udf/api/general/web/FilePublicController.java @@ -0,0 +1,65 @@ +package org.itkk.udf.api.general.web; + + +import org.itkk.udf.api.common.CommonConstant; +import org.itkk.udf.starter.core.exception.ParameterValidException; +import org.itkk.udf.starter.file.db.DbFileConstant; +import org.itkk.udf.starter.file.db.dto.DbFileInfoDto; +import org.itkk.udf.starter.file.db.web.DbFileBase; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; + +@RestController +@RequestMapping(value = CommonConstant.URL_ROOT_WEB_PUBLIC + "file/") +public class FilePublicController extends DbFileBase { + /** + * 文件预览 + * + * @param id id + * @param width width + * @param height height + * @throws IOException IOException + */ + @GetMapping("preview/{id}") + public void preview(@PathVariable String id, @RequestParam(name = "width", required = false) Integer width, @RequestParam(name = "height", required = false) Integer height) throws IOException { + //获得文件信息 + DbFileInfoDto dbFileInfoDto = dbFileService.get(id); + //判空 + if (dbFileInfoDto == null) { + throw new ParameterValidException("文件ID:" + id + "对应的文件不存在"); + } + //判断是否公共文件 + if (DbFileConstant.PUBLIC_ROOT_PATH_CODE.equals(dbFileInfoDto.getRootPathCode())) { + throw new ParameterValidException("文件ID:" + id + "不是公共文件"); + } + //设置头信息 + this.setResponseHeader(id, true); + //下载 + dbFileService.download(id, width, height, response.getOutputStream()); + } + + /** + * 文件下载 + * + * @param id id + * @throws IOException IOException + */ + @GetMapping("download/{id}") + public void download(@PathVariable String id) throws IOException { + //获得文件信息 + DbFileInfoDto dbFileInfoDto = dbFileService.get(id); + //判空 + if (dbFileInfoDto == null) { + throw new ParameterValidException("文件ID:" + id + "对应的文件不存在"); + } + //判断是否公共文件 + if (DbFileConstant.PUBLIC_ROOT_PATH_CODE.equals(dbFileInfoDto.getRootPathCode())) { + throw new ParameterValidException("文件ID:" + id + "不是公共文件"); + } + //设置头信息 + this.setResponseHeader(id, false); + //下载 + dbFileService.download(id, null, null, response.getOutputStream()); + } +} diff --git a/udf-api/udf-api-server/src/main/resources/application-dev.properties b/udf-api/udf-api-server/src/main/resources/application-dev.properties index 483b6c3..c8d7910 100644 --- a/udf-api/udf-api-server/src/main/resources/application-dev.properties +++ b/udf-api/udf-api-server/src/main/resources/application-dev.properties @@ -11,6 +11,7 @@ logging.file.max-size=100MB logging.file.max-history=31 # FILE DB org.itkk.udf.starter.db.file.properties.mapping.common=D:/app/file/common/ +org.itkk.udf.starter.db.file.properties.mapping.public=D:/app/file/public/ org.itkk.udf.starter.db.file.properties.mapping.temp=D:/app/file/temp/ org.itkk.udf.starter.db.file.properties.fileUploadSupportType=pdf,jpg,jpeg,png org.itkk.udf.starter.db.file.properties.previewSupportContentType=application/pdf,image/jpeg,image/png diff --git a/udf-api/udf-api-server/src/main/resources/application-prod.properties b/udf-api/udf-api-server/src/main/resources/application-prod.properties index 2dd0253..6b0089c 100644 --- a/udf-api/udf-api-server/src/main/resources/application-prod.properties +++ b/udf-api/udf-api-server/src/main/resources/application-prod.properties @@ -11,6 +11,7 @@ logging.file.max-size=100MB logging.file.max-history=31 # FILE DB org.itkk.udf.starter.db.file.properties.mapping.common=/usr/local/share/nginx/file/common/ +org.itkk.udf.starter.db.file.properties.mapping.public=/usr/local/share/nginx/file/public/ org.itkk.udf.starter.db.file.properties.mapping.temp=/usr/local/share/nginx/file/temp/ org.itkk.udf.starter.db.file.properties.fileUploadSupportType=pdf,jpg,jpeg,png org.itkk.udf.starter.db.file.properties.previewSupportContentType=application/pdf,image/jpeg,image/png diff --git a/udf-starter/udf-starter-core/src/main/java/org/itkk/udf/starter/core/CoreUtil.java b/udf-starter/udf-starter-core/src/main/java/org/itkk/udf/starter/core/CoreUtil.java index 75c9781..841ef77 100644 --- a/udf-starter/udf-starter-core/src/main/java/org/itkk/udf/starter/core/CoreUtil.java +++ b/udf-starter/udf-starter-core/src/main/java/org/itkk/udf/starter/core/CoreUtil.java @@ -90,8 +90,12 @@ public class CoreUtil { * @return 包含中文标点 true , 不包含中文标点 false */ public static boolean isChinesePunctuation(String s) { + final char[] skipArr = new char[]{'>', '<'}; boolean rv = false; for (char c : s.toCharArray()) { + if (ArrayUtils.contains(skipArr, c)) { + continue; + } if (isChinesePunctuation(c)) { rv = true; break; diff --git a/udf-starter/udf-starter-core/src/main/java/org/itkk/udf/starter/core/xss/XssUtil.java b/udf-starter/udf-starter-core/src/main/java/org/itkk/udf/starter/core/xss/XssUtil.java index 7b2eebf..481b680 100644 --- a/udf-starter/udf-starter-core/src/main/java/org/itkk/udf/starter/core/xss/XssUtil.java +++ b/udf-starter/udf-starter-core/src/main/java/org/itkk/udf/starter/core/xss/XssUtil.java @@ -18,8 +18,6 @@ public class XssUtil { public static String xssEncode(String s) { if (s == null || s.isEmpty()) { return s; - } else { - s = stripXSSAndSql(s); } StringBuilder sb = new StringBuilder(s.length() + 16); for (int i = 0; i < s.length(); i++) { @@ -31,13 +29,13 @@ public class XssUtil { case '<': sb.append("<");// 转义小于号 break; - case '\'': + /*case '\'': sb.append("'");// 转义单引号 break; case '\"': sb.append(""");// 转义双引号 break; - /*case '&': + case '&': sb.append("&");// 转义& break; case '#': @@ -48,7 +46,7 @@ public class XssUtil { break; } } - return sb.toString(); + return stripXSSAndSql(sb.toString()); } /** @@ -64,7 +62,7 @@ public class XssUtil { scriptPattern7 = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE), scriptPattern8 = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE), scriptPattern9 = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), - scriptPattern10 = Pattern.compile("(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|and|or|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute|union|where|limit|group by|hex|set)\\b)", Pattern.CASE_INSENSITIVE), + scriptPattern10 = Pattern.compile("(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(update|delete|select|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute|union|where|limit|group by|hex)\\b)", Pattern.CASE_INSENSITIVE), scriptPattern11 = Pattern.compile("\\*\\/(\\s|.)*?\\/\\*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); private static String stripXSSAndSql(String value) { @@ -88,7 +86,7 @@ public class XssUtil { // Avoid onload= expressions value = scriptPattern9.matcher(value).replaceAll(REPLACE_STRING); // Avoid sql= expressions - value = scriptPattern10.matcher(value).replaceAll(REPLACE_STRING); + //value = scriptPattern10.matcher(value).replaceAll(REPLACE_STRING); // Avoid */xxx/*(...) expressions value = scriptPattern11.matcher(value).replaceAll(REPLACE_STRING); } diff --git a/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/DbFileConstant.java b/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/DbFileConstant.java index a398640..e7ecb14 100644 --- a/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/DbFileConstant.java +++ b/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/DbFileConstant.java @@ -7,10 +7,17 @@ import lombok.extern.slf4j.Slf4j; */ @Slf4j public class DbFileConstant { + + /** + * 公共根路径代码 + */ + public static final String PUBLIC_ROOT_PATH_CODE = "public"; + /** * JPEG上下文类型 */ public static final String CONTENT_TYPE_JPEG = "image/jpeg"; + /** * PNG上下文类型 */ diff --git a/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/DbFileBase.java b/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/DbFileBase.java new file mode 100644 index 0000000..34b5d4c --- /dev/null +++ b/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/DbFileBase.java @@ -0,0 +1,97 @@ +package org.itkk.udf.starter.file.db.web; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.itkk.udf.starter.core.CoreConstant; +import org.itkk.udf.starter.core.CoreProperties; +import org.itkk.udf.starter.core.exception.ParameterValidException; +import org.itkk.udf.starter.file.db.DbFileProperties; +import org.itkk.udf.starter.file.db.dto.DbFileInfoDto; +import org.itkk.udf.starter.file.db.service.DbFileService; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +public abstract class DbFileBase { + + /** + * dbFileService + */ + @Autowired + protected DbFileService dbFileService; + + /** + * dbFileProperties + */ + @Autowired + protected DbFileProperties dbFileProperties; + + /** + * coreProperties + */ + @Autowired + protected CoreProperties coreProperties; + + /** + * response + */ + @Autowired + protected HttpServletResponse response; + + /** + * request + */ + @Autowired + protected HttpServletRequest request; + + /** + * 设置头信息 + * + * @param id id + * @param preview preview + * @throws UnsupportedEncodingException + */ + protected void setResponseHeader(String id, Boolean preview) throws UnsupportedEncodingException { + //获得文件信息 + DbFileInfoDto fileInfo = dbFileService.get(id); + //判空 + if (fileInfo == null) { + throw new ParameterValidException("文件" + id + "不存在"); + } + //rootPath判空 + if (MapUtils.isEmpty(dbFileProperties.getMapping()) || !dbFileProperties.getMapping().containsKey(fileInfo.getRootPathCode())) { + throw new ParameterValidException("文件路径映射" + fileInfo.getRootPathCode() + "不存在"); + } + //清空response + response.reset(); + //设置response + response.setContentLengthLong(fileInfo.getFileSize()); + response.setCharacterEncoding(CoreConstant.CHARACTER_SET); + response.setContentType(fileInfo.getContentType()); + response.setDateHeader("Expires", 0L); + response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); + //response.addHeader("Cache-Control", "post-check=0, pre-check=0"); + response.setHeader("Pragma", "no-cache"); + //判断是否预览 (预览则要限定一下上下文类型){ + if (preview) { + if (StringUtils.isNotBlank(dbFileProperties.getPreviewSupportContentType())) { + if (!dbFileProperties.getPreviewSupportContentType().contains(fileInfo.getContentType())) { + throw new ParameterValidException("不支持[" + fileInfo.getContentType() + "]此上下文类型的预览"); + } + } + } + //判断是否预览 (预览则不用设置文件名) + if (!preview) { + //设置文件名 + String fileName = URLEncoder.encode(fileInfo.getFileName(), CoreConstant.CHARACTER_SET); + if (request.getHeader("User-Agent").toUpperCase().indexOf("MSIE") > 0) { + response.setHeader("Content-Disposition", "attachment;filename=" + fileName); + } else { + response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + fileName); + } + } + } +} diff --git a/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/DbFileBaseController.java b/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/DbFileBaseController.java index f667937..49cff6d 100644 --- a/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/DbFileBaseController.java +++ b/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/DbFileBaseController.java @@ -1,57 +1,16 @@ package org.itkk.udf.starter.file.db.web; -import org.apache.commons.collections4.MapUtils; -import org.apache.commons.lang3.StringUtils; -import org.itkk.udf.starter.core.CoreConstant; -import org.itkk.udf.starter.core.CoreProperties; import org.itkk.udf.starter.core.RestResponse; import org.itkk.udf.starter.core.exception.ParameterValidException; -import org.itkk.udf.starter.file.db.DbFileProperties; import org.itkk.udf.starter.file.db.dto.DbFileInfoDto; -import org.itkk.udf.starter.file.db.service.DbFileService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.List; import java.util.Map; -public abstract class DbFileBaseController { - - /** - * dbFileService - */ - @Autowired - private DbFileService dbFileService; - - /** - * dbFileProperties - */ - @Autowired - private DbFileProperties dbFileProperties; - - /** - * coreProperties - */ - @Autowired - private CoreProperties coreProperties; - - /** - * response - */ - @Autowired - private HttpServletResponse response; - - /** - * request - */ - @Autowired - private HttpServletRequest request; +public abstract class DbFileBaseController extends DbFileBase { /** * 获得当前用户ID @@ -144,52 +103,4 @@ public abstract class DbFileBaseController { dbFileService.delete(id); return RestResponse.success(); } - - /** - * 设置头信息 - * - * @param id id - * @param preview preview - * @throws UnsupportedEncodingException - */ - private void setResponseHeader(String id, Boolean preview) throws UnsupportedEncodingException { - //获得文件信息 - DbFileInfoDto fileInfo = dbFileService.get(id); - //判空 - if (fileInfo == null) { - throw new ParameterValidException("文件" + id + "不存在"); - } - //rootPath判空 - if (MapUtils.isEmpty(dbFileProperties.getMapping()) || !dbFileProperties.getMapping().containsKey(fileInfo.getRootPathCode())) { - throw new ParameterValidException("文件路径映射" + fileInfo.getRootPathCode() + "不存在"); - } - //清空response - response.reset(); - //设置response - response.setContentLengthLong(fileInfo.getFileSize()); - response.setCharacterEncoding(CoreConstant.CHARACTER_SET); - response.setContentType(fileInfo.getContentType()); - response.setDateHeader("Expires", 0L); - response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); - //response.addHeader("Cache-Control", "post-check=0, pre-check=0"); - response.setHeader("Pragma", "no-cache"); - //判断是否预览 (预览则要限定一下上下文类型){ - if (preview) { - if (StringUtils.isNotBlank(dbFileProperties.getPreviewSupportContentType())) { - if (!dbFileProperties.getPreviewSupportContentType().contains(fileInfo.getContentType())) { - throw new ParameterValidException("不支持[" + fileInfo.getContentType() + "]此上下文类型的预览"); - } - } - } - //判断是否预览 (预览则不用设置文件名) - if (!preview) { - //设置文件名 - String fileName = URLEncoder.encode(fileInfo.getFileName(), CoreConstant.CHARACTER_SET); - if (request.getHeader("User-Agent").toUpperCase().indexOf("MSIE") > 0) { - response.setHeader("Content-Disposition", "attachment;filename=" + fileName); - } else { - response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + fileName); - } - } - } } diff --git a/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/WopiBaseContrller.java b/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/WopiBaseContrller.java deleted file mode 100644 index e20a5c6..0000000 --- a/udf-starter/udf-starter-file-db/src/main/java/org/itkk/udf/starter/file/db/web/WopiBaseContrller.java +++ /dev/null @@ -1,148 +0,0 @@ -package org.itkk.udf.starter.file.db.web; - -import org.itkk.udf.starter.core.exception.SystemRuntimeException; -import org.itkk.udf.starter.file.db.dto.WopiFileInfo; -import org.itkk.udf.starter.file.db.service.DbFileService; -import org.apache.commons.codec.binary.Base64; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.*; -import java.security.MessageDigest; - -public abstract class WopiBaseContrller { - - /** - * request - */ - @Autowired - private HttpServletRequest request; - - /** - * response - */ - @Autowired - private HttpServletResponse response; - - /** - * dbFileService - */ - @Autowired - private DbFileService dbFileService; - - /** - * 获取文件流 - * - * @param name - */ - @GetMapping("files/{name}/contents") - public void getFile(@PathVariable String name) { - final String utf8 = "UTF-8"; - final String iso88591 = "ISO-8859-1"; - InputStream fis = null; - OutputStream toClient = null; - try { - // 获得文件 - File file = dbFileService.getFile(name); - // 取得文件名 - String filename = file.getName(); - // 以流的形式下载文件 - fis = new BufferedInputStream(new FileInputStream(file)); - byte[] buffer = new byte[fis.available()]; - fis.read(buffer); - // 清空response - response.reset(); - // 设置response的Header - response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes(utf8), iso88591)); - response.addHeader("Content-Length", "" + file.length()); - toClient = new BufferedOutputStream(response.getOutputStream()); - response.setContentType("application/octet-stream"); - toClient.write(buffer); - toClient.flush(); - } catch (Exception e) { - throw new SystemRuntimeException(e); - } finally { - try { - if (fis != null) { - fis.close(); - } - if (toClient != null) { - toClient.close(); - } - } catch (Exception e) { - throw new SystemRuntimeException(e); - } - } - } - - /** - * 获取文件信息 - * - * @param name name - * @return WopiFileInfo - */ - @RequestMapping("files/{name}") - public WopiFileInfo getFileInfo(@PathVariable String name) { - //定义返回值 - WopiFileInfo info = null; - //获得文件 - File file = dbFileService.getFile(name); - //判空 - if (file.exists()) { - //实例化 - info = new WopiFileInfo(); - //填充文件信息 - info.setBaseFileName(file.getName()); - info.setSize(file.length()); - info.setOwnerId("admin"); - info.setVersion(file.lastModified()); - info.setSha256(getHash256(file)); - info.setAllowExternalMarketplace(false); - info.setUserCanWrite(false); - info.setSupportsUpdate(false); - info.setSupportsLocks(false); - info.setDisablePrint(true); - info.setDisableTranslation(true); - info.setUserCanNotWriteRelative(true); - } - //返回 - return info; - } - - /** - * 获取文件的SHA-256值 - * - * @param file file - * @return String - */ - private String getHash256(File file) { - String value = ""; - // 获取hash值 - try { - byte[] buffer = new byte[1024]; - int numRead; - InputStream fis = new FileInputStream(file); - //如果想使用SHA-1或SHA-256,则传入SHA-1,SHA-256 - MessageDigest complete = MessageDigest.getInstance("SHA-256"); - do { - //从文件读到buffer - numRead = fis.read(buffer); - if (numRead > 0) { - //用读到的字节进行MD5的计算,第二个参数是偏移量 - complete.update(buffer, 0, numRead); - } - } while (numRead != -1); - - fis.close(); - value = new String(Base64.encodeBase64(complete.digest())); - } catch (Exception e) { - throw new SystemRuntimeException(e); - } - return value; - } - -}