diff --git a/pom.xml b/pom.xml index cfa7552bf..a57fc28e9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,14 +6,14 @@ com.ruoyi ruoyi - 3.2.0 + 3.2.1 ruoyi http://www.ruoyi.vip 若依管理系统 - 3.2.0 + 3.2.1 UTF-8 UTF-8 1.8 diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index da3aa62bb..3257d8cf7 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.2.0 + 3.2.1 4.0.0 jar diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java index f02b6d3e4..e0c728fac 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @@ -41,17 +42,15 @@ public class CommonController { try { - if (!FileUtils.isValidFilename(fileName)) + if (!FileUtils.checkAllowDownload(fileName)) { throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); } String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); String filePath = RuoYiConfig.getDownloadPath() + fileName; - response.setCharacterEncoding("utf-8"); - response.setContentType("multipart/form-data"); - response.setHeader("Content-Disposition", - "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, realFileName)); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); FileUtils.writeBytes(filePath, response.getOutputStream()); if (delete) { @@ -92,18 +91,28 @@ public class CommonController * 本地资源通用下载 */ @GetMapping("/common/download/resource") - public void resourceDownload(String name, HttpServletRequest request, HttpServletResponse response) throws Exception + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception { - // 本地资源路径 - String localPath = RuoYiConfig.getProfile(); - // 数据库资源地址 - String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX); - // 下载名称 - String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); - response.setCharacterEncoding("utf-8"); - response.setContentType("multipart/form-data"); - response.setHeader("Content-Disposition", - "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, downloadName)); - FileUtils.writeBytes(downloadPath, response.getOutputStream()); + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = RuoYiConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("下载文件失败", e); + } } } diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index c205d3a53..394380bcc 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -3,7 +3,7 @@ ruoyi: # 名称 name: RuoYi # 版本 - version: 3.2.0 + version: 3.2.1 # 版权年份 copyrightYear: 2020 # 实例演示开关 @@ -70,7 +70,7 @@ token: secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 30 - + # MyBatis配置 # https://baomidou.com/config/ mybatis-plus: diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 46823fe71..35adaa31c 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.2.0 + 3.2.1 4.0.0 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java index 29b28e1d8..7bcaced4c 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java @@ -74,6 +74,9 @@ public class GenConstants /** 日期控件 */ public static final String HTML_DATETIME = "datetime"; + /** 上传控件 */ + public static final String HTML_UPLOAD_IMAGE = "uploadImage"; + /** 富文本控件 */ public static final String HTML_EDITOR = "editor"; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java new file mode 100644 index 000000000..65be65b4a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java @@ -0,0 +1,47 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java index d144072eb..66c9f2484 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -7,7 +7,11 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.utils.StringUtils; /** * 文件处理工具类 @@ -104,6 +108,30 @@ public class FileUtils extends org.apache.commons.io.FileUtils return filename.matches(FILENAME_PATTERN); } + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + /** * 下载文件名重新编码 * @@ -111,8 +139,7 @@ public class FileUtils extends org.apache.commons.io.FileUtils * @param fileName 文件名 * @return 编码后的文件名 */ - public static String setFileDownloadHeader(HttpServletRequest request, String fileName) - throws UnsupportedEncodingException + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException { final String agent = request.getHeader("USER-AGENT"); String filename = fileName; @@ -139,4 +166,38 @@ public class FileUtils extends org.apache.commons.io.FileUtils } return filename; } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + * @return + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.setHeader("Content-disposition", contentDispositionValue.toString()); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } } diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml index e8f79c26b..61af7c0a7 100644 --- a/ruoyi-framework/pom.xml +++ b/ruoyi-framework/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.2.0 + 3.2.1 4.0.0 diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml index 43d1245e5..58abab131 100644 --- a/ruoyi-generator/pom.xml +++ b/ruoyi-generator/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.2.0 + 3.2.1 4.0.0 diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java index a96d2733f..23b069a3c 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java @@ -59,7 +59,7 @@ public class GenTableColumn extends BaseEntity /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ private String queryType; - /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、editor富文本控件) */ + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、upload上传控件、editor富文本控件) */ private String htmlType; /** 字典类型 */ diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java index 43fc71db4..da78d0384 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java @@ -111,6 +111,11 @@ public class GenUtils { column.setHtmlType(GenConstants.HTML_SELECT); } + // 文件字段设置上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_UPLOAD_IMAGE); + } // 内容字段设置富文本控件 else if (StringUtils.endsWithIgnoreCase(columnName, "content")) { diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm index e139a1356..e43393412 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -139,6 +139,10 @@ +#elseif($column.htmlType == "uploadImage") + + + #elseif($column.htmlType == "editor") @@ -226,6 +230,12 @@ import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${Busin import Treeselect from "@riophae/vue-treeselect"; import "@riophae/vue-treeselect/dist/vue-treeselect.css"; #foreach($column in $columns) +#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "uploadImage") +import UploadImage from '@/components/UploadImage'; +#break +#end +#end +#foreach($column in $columns) #if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor") import Editor from '@/components/Editor'; #break @@ -236,6 +246,12 @@ export default { name: "${BusinessName}", components: { #foreach($column in $columns) +#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "uploadImage") + UploadImage, +#break +#end +#end +#foreach($column in $columns) #if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor") Editor, #break diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm index 129c17628..aa973b1df 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -168,6 +168,10 @@ +#elseif($column.htmlType == "uploadImage") + + + #elseif($column.htmlType == "editor") @@ -253,6 +257,12 @@ + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/index.vue b/ruoyi-ui/src/views/index.vue index c50687c3d..55f34391d 100644 --- a/ruoyi-ui/src/views/index.vue +++ b/ruoyi-ui/src/views/index.vue @@ -146,6 +146,20 @@ 更新日志 + +

    +
  1. 阻止任意文件下载漏洞
  2. +
  3. 代码生成支持上传控件
  4. +
  5. 新增图片上传组件
  6. +
  7. 调整默认首页
  8. +
  9. 升级druid到最新版本v1.2.2
  10. +
  11. mapperLocations配置支持分隔符
  12. +
  13. 权限信息调整
  14. +
  15. 调整sql默认时间
  16. +
  17. 解决代码生成没有bit类型的问题
  18. +
  19. 升级pagehelper到最新版1.3.0
  20. +
+
  1. 升级springboot版本到2.1.17 提升安全性
  2. @@ -421,7 +435,7 @@ export default { data() { return { // 版本号 - version: "3.2.0", + version: "3.2.1", }; }, methods: { diff --git a/ruoyi-ui/src/views/tool/gen/editTable.vue b/ruoyi-ui/src/views/tool/gen/editTable.vue index 34695457b..9ecab454c 100644 --- a/ruoyi-ui/src/views/tool/gen/editTable.vue +++ b/ruoyi-ui/src/views/tool/gen/editTable.vue @@ -90,6 +90,7 @@ +