diff --git a/pom.xml b/pom.xml index 6a8c566a..8a410659 100644 --- a/pom.xml +++ b/pom.xml @@ -10,36 +10,36 @@ 21 - 4.4.6 + 4.4.11 3.5.6 - 5.2.2 + 5.2.5 1.0.6 - 1.4.20 + 1.4.21 7.5.5 - 3.22.0 + 4.0.0 16.02-2.01 1.0 2.7.7 1.4.2 5.17.2 - 3.0.2 + 3.0.6 1.4.0 3.0.4 0.2.1 - 1.5.2 - 4.1.2-1.5.2 - 0.3.6-1.5.1 - 4.2.1-1.5.2 - 5.5.13.3 - 3.1 - 23.9 + 1.5.12 + 4.11.0-1.5.12 + 0.3.30-1.5.12 + 7.1.1-1.5.12 + 5.5.13.4 + 5.6 + 25.10 1.70 1.0.3 - 4.5.14 + 4.5.16 - 1.5.0 - 3.9.0 - 3.13.0 + 1.11.0 + 3.12.0 + 3.20.0 ${java.version} diff --git a/server/src/main/java/cn/keking/config/ConfigConstants.java b/server/src/main/java/cn/keking/config/ConfigConstants.java index 69fd600a..5c742f4d 100644 --- a/server/src/main/java/cn/keking/config/ConfigConstants.java +++ b/server/src/main/java/cn/keking/config/ConfigConstants.java @@ -18,88 +18,28 @@ public class ConfigConstants { public static final String BEAN_NAME = "configConstants"; static { - //pdfbox兼容低版本jdk + // PDFBox兼容低版本JDK System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider"); } - private static Boolean cacheEnabled; - private static String[] simTexts = {}; - private static String[] medias = {}; - private static String[] convertMedias = {}; - private static String mediaConvertDisable; - private static String officePreviewType; - private static String officePreviewSwitchDisabled; - private static String ftpUsername; - private static String ftpPassword; - 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 trustHostSet; - private static CopyOnWriteArraySet notTrustHostSet; - private static String pdfPresentationModeDisable; - private static String pdfDisableEditing; - private static String pdfOpenFileDisable; - private static String pdfPrintDisable; - private static String pdfDownloadDisable; - private static String pdfBookmarkDisable; - private static Boolean fileUploadDisable; - private static String tifPreviewType; - private static String beian; - private static String[] prohibit = {}; - private static String size; - private static String password; - private static int pdf2JpgDpi; - private static String officeTypeWeb; - private static String cadPreviewType; - private static Boolean deleteSourceFile; - private static Boolean deleteCaptcha; - private static String officePageRange; - private static String officeWatermark; - private static String officeQuality; - private static String officeMaxImageResolution; - private static Boolean officeExportBookmarks; - private static Boolean officeExportNotes; - private static Boolean officeDocumentOpenPasswords; - private static String cadTimeout; - private static int cadThread; - private static String homePageNumber; - private static String homePagination; - private static String homePageSize; - private static String homeSearch; - private static int pdfTimeout; - private static int pdfTimeout80; - private static int pdfTimeout200; - private static int pdfThread; + // ================================================== + // 常量定义区 + // ================================================== + // 缓存配置常量 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,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd,xbrl"; public static final String DEFAULT_MEDIA_TYPE = "mp3,wav,mp4,flv"; - public static final String DEFAULT_OFFICE_PREVIEW_TYPE = "image"; - public static final String DEFAULT_OFFICE_PREVIEW_SWITCH_DISABLED = "false"; - 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_VALUE = "default"; - public static final String DEFAULT_PDF_PRESENTATION_MODE_DISABLE = "true"; - public static final String DEFAULT_PDF_OPEN_FILE_DISABLE = "true"; - public static final String DEFAULT_PDF_PRINT_DISABLE = "true"; - public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true"; - public static final String DEFAULT_PDF_BOOKMARK_DISABLE = "true"; - public static final String DEFAULT_PDF_DISABLE_EDITING = "true"; - public static final String DEFAULT_FILE_UPLOAD_DISABLE = "false"; + public static final String DEFAULT_PROHIBIT = "exe,dll"; public static final String DEFAULT_TIF_PREVIEW_TYPE = "tif"; public static final String DEFAULT_CAD_PREVIEW_TYPE = "pdf"; - public static final String DEFAULT_BEIAN = "无"; - public static final String DEFAULT_SIZE = "500MB"; - public static final String DEFAULT_PROHIBIT = "exe,dll"; - public static final String DEFAULT_PASSWORD = "123456"; - public static final String DEFAULT_PDF2_JPG_DPI = "105"; + + // Office配置常量 + public static final String DEFAULT_OFFICE_PREVIEW_TYPE = "image"; + public static final String DEFAULT_OFFICE_PREVIEW_SWITCH_DISABLED = "false"; public static final String DEFAULT_OFFICE_TYPE_WEB = "web"; - public static final String DEFAULT_DELETE_SOURCE_FILE = "true"; - public static final String DEFAULT_DELETE_CAPTCHA = "false"; - public static final String DEFAULT_CAD_TIMEOUT = "90"; - public static final String DEFAULT_CAD_THREAD = "5"; public static final String DEFAULT_OFFICE_PAQERANQE = "false"; public static final String DEFAULT_OFFICE_WATERMARK = "false"; public static final String DEFAULT_OFFICE_QUALITY = "80"; @@ -107,19 +47,391 @@ public class ConfigConstants { public static final String DEFAULT_OFFICE_EXPORTBOOKMARKS = "true"; public static final String DEFAULT_OFFICE_EXPORTNOTES = "true"; public static final String DEFAULT_OFFICE_EOCUMENTOPENPASSWORDS = "true"; - public static final String DEFAULT_HOME_PAGENUMBER = "1"; - public static final String DEFAULT_HOME_PAGINATION = "true"; - public static final String DEFAULT_HOME_PAGSIZE = "15"; - public static final String DEFAULT_HOME_SEARCH = "true"; + + // FTP配置常量 + 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_VALUE = "default"; + + // PDF配置常量 + public static final String DEFAULT_PDF_PRESENTATION_MODE_DISABLE = "true"; + public static final String DEFAULT_PDF_OPEN_FILE_DISABLE = "true"; + public static final String DEFAULT_PDF_PRINT_DISABLE = "true"; + public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true"; + public static final String DEFAULT_PDF_BOOKMARK_DISABLE = "true"; + public static final String DEFAULT_PDF_DISABLE_EDITING = "true"; + public static final String DEFAULT_PDF2_JPG_DPI = "105"; public static final String DEFAULT_PDF_TIMEOUT = "90"; public static final String DEFAULT_PDF_TIMEOUT80 = "180"; public static final String DEFAULT_PDF_TIMEOUT200 = "300"; public static final String DEFAULT_PDF_THREAD = "5"; + // CAD配置常量 + public static final String DEFAULT_CAD_TIMEOUT = "90"; + public static final String DEFAULT_CAD_THREAD = "5"; + + // 文件操作配置常量 + public static final String DEFAULT_FILE_UPLOAD_DISABLE = "false"; + public static final String DEFAULT_DELETE_SOURCE_FILE = "true"; + public static final String DEFAULT_DELETE_CAPTCHA = "false"; + public static final String DEFAULT_SIZE = "500MB"; + public static final String DEFAULT_PASSWORD = "123456"; + + // 首页配置常量 + public static final String DEFAULT_BEIAN = "无"; + public static final String DEFAULT_HOME_PAGENUMBER = "1"; + public static final String DEFAULT_HOME_PAGINATION = "true"; + public static final String DEFAULT_HOME_PAGSIZE = "15"; + public static final String DEFAULT_HOME_SEARCH = "true"; + + // 权限配置常量 + public static final String DEFAULT_KEY = "false"; + public static final String DEFAULT_PICTURES_PREVIEW = "true"; + public static final String DEFAULT_GET_CORS_FILE = "true"; + public static final String DEFAULT_ADD_TASK = "true"; + public static final String DEFAULT_AES_KEY= "1234567890123456"; + + // UserAgent配置常量 + public static final String DEFAULT_USER_AGENT = "false"; + + // Basic认证配置常量 + public static final String DEFAULT_BASIC_NAME = ""; + + // ================================================== + // 配置变量定义区(按功能分类) + // ================================================== + + // 1. 缓存配置 + private static Boolean cacheEnabled; + + // 2. 文件类型配置 + private static String[] simTexts = {}; + private static String[] medias = {}; + private static String[] convertMedias = {}; + private static String[] prohibit = {}; + private static String mediaConvertDisable; + private static String tifPreviewType; + private static String cadPreviewType; + + // 3. Office配置 + private static String officePreviewType; + private static String officePreviewSwitchDisabled; + private static String officeTypeWeb; + private static String officePageRange; + private static String officeWatermark; + private static String officeQuality; + private static String officeMaxImageResolution; + private static Boolean officeExportBookmarks; + private static Boolean officeExportNotes; + private static Boolean officeDocumentOpenPasswords; + + // 4. FTP配置 + private static String ftpUsername; + private static String ftpPassword; + private static String ftpControlEncoding; + + // 5. 路径配置 + private static String fileDir = ConfigUtils.getHomePath() + File.separator + "file" + File.separator; + private static String localPreviewDir; + private static String baseUrl; + + // 6. 安全配置 + private static CopyOnWriteArraySet trustHostSet; + private static CopyOnWriteArraySet notTrustHostSet; + + // 7. PDF配置 + private static String pdfPresentationModeDisable; + private static String pdfDisableEditing; + private static String pdfOpenFileDisable; + private static String pdfPrintDisable; + private static String pdfDownloadDisable; + private static String pdfBookmarkDisable; + private static int pdf2JpgDpi; + private static int pdfTimeout; + private static int pdfTimeout80; + private static int pdfTimeout200; + private static int pdfThread; + + // 8. CAD配置 + private static String cadTimeout; + private static int cadThread; + + // 9. 文件操作配置 + private static Boolean fileUploadDisable; + private static String size; + private static String password; + private static Boolean deleteSourceFile; + private static Boolean deleteCaptcha; + + // 10. 首页配置 + private static String beian; + private static String homePageNumber; + private static String homePagination; + private static String homePageSize; + private static String homeSearch; + + // 11. 权限配置 + private static String key; + private static boolean picturesPreview; + private static boolean getCorsFile; + private static boolean addTask; + private static String aesKey; + + // 12. UserAgent配置 + private static String userAgent; + + // 13. Basic认证配置 + private static String basicName; + + // ================================================== + // 获取方法(按功能分类) + // ================================================== + + // 1. 缓存配置获取方法 public static Boolean isCacheEnabled() { return cacheEnabled; } + // 2. 文件类型配置获取方法 + public static String[] getSimText() { + return simTexts; + } + + public static String[] getMedia() { + return medias; + } + + public static String[] getConvertMedias() { + return convertMedias; + } + + public static String getMediaConvertDisable() { + return mediaConvertDisable; + } + + public static String getTifPreviewType() { + return tifPreviewType; + } + + public static String[] getProhibit() { + return prohibit; + } + + // 3. Office配置获取方法 + public static String getOfficePreviewType() { + return officePreviewType; + } + + public static String getOfficePreviewSwitchDisabled() { + return officePreviewSwitchDisabled; + } + + public static String getOfficeTypeWeb() { + return officeTypeWeb; + } + + public static String getOfficePageRange() { + return officePageRange; + } + + public static String getOfficeWatermark() { + return officeWatermark; + } + + public static String getOfficeQuality() { + return officeQuality; + } + + public static String getOfficeMaxImageResolution() { + return officeMaxImageResolution; + } + + public static Boolean getOfficeExportBookmarks() { + return officeExportBookmarks; + } + + public static Boolean getOfficeExportNotes() { + return officeExportNotes; + } + + public static Boolean getOfficeDocumentOpenPasswords() { + return officeDocumentOpenPasswords; + } + + // 4. FTP配置获取方法 + public static String getFtpUsername() { + return ftpUsername; + } + + public static String getFtpPassword() { + return ftpPassword; + } + + public static String getFtpControlEncoding() { + return ftpControlEncoding; + } + + // 5. 路径配置获取方法 + public static String getBaseUrl() { + return baseUrl; + } + + public static String getFileDir() { + return fileDir; + } + + public static String getLocalPreviewDir() { + return localPreviewDir; + } + + // 6. 安全配置获取方法 + public static Set getTrustHostSet() { + return trustHostSet; + } + + public static Set getNotTrustHostSet() { + return notTrustHostSet; + } + + // 7. PDF配置获取方法 + public static String getPdfPresentationModeDisable() { + return pdfPresentationModeDisable; + } + + public static String getPdfOpenFileDisable() { + return pdfOpenFileDisable; + } + + public static String getPdfPrintDisable() { + return pdfPrintDisable; + } + + public static String getPdfDownloadDisable() { + return pdfDownloadDisable; + } + + public static String getPdfBookmarkDisable() { + return pdfBookmarkDisable; + } + + public static String getPdfDisableEditing() { + return pdfDisableEditing; + } + + public static int getPdf2JpgDpi() { + return pdf2JpgDpi; + } + + public static int getPdfTimeout() { + return pdfTimeout; + } + + public static int getPdfTimeout80() { + return pdfTimeout80; + } + + public static int getPdfTimeout200() { + return pdfTimeout200; + } + + public static int getPdfThread() { + return pdfThread; + } + + // 8. CAD配置获取方法 + public static String getCadPreviewType() { + return cadPreviewType; + } + + public static String getCadTimeout() { + return cadTimeout; + } + + public static int getCadThread() { + return cadThread; + } + + // 9. 文件操作配置获取方法 + public static Boolean getFileUploadDisable() { + return fileUploadDisable; + } + + public static String maxSize() { + return size; + } + + public static String getPassword() { + return password; + } + + public static Boolean getDeleteSourceFile() { + return deleteSourceFile; + } + + public static Boolean getDeleteCaptcha() { + return deleteCaptcha; + } + + // 10. 首页配置获取方法 + public static String getBeian() { + return beian; + } + + public static String getHomePageNumber() { + return homePageNumber; + } + + public static String getHomePagination() { + return homePagination; + } + + public static String getHomePageSize() { + return homePageSize; + } + + public static String getHomeSearch() { + return homeSearch; + } + + // 11. 权限配置获取方法 + public static String getKey() { + return key; + } + + public static boolean getPicturesPreview() { + return picturesPreview; + } + + public static boolean getGetCorsFile() { + return getCorsFile; + } + + public static boolean getAddTask() { + return addTask; + } + + public static String getaesKey() { + return aesKey; + } + + // 12. UserAgent配置获取方法 + public static String getUserAgent() { + return userAgent; + } + + // 13. Basic认证配置获取方法 + public static String getBasicName() { + return basicName; + } + + // ================================================== + // Setter方法(按功能分类) + // ================================================== + + // 1. 缓存配置Setter方法 @Value("${cache.enabled:true}") public void setCacheEnabled(String cacheEnabled) { setCacheEnabledValueValue(Boolean.parseBoolean(cacheEnabled)); @@ -129,10 +441,7 @@ public class ConfigConstants { ConfigConstants.cacheEnabled = cacheEnabled; } - public static String[] getSimText() { - return simTexts; - } - + // 2. 文件类型配置Setter方法 @Value("${simText:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd,xbrl}") public void setSimText(String simText) { String[] simTextArr = simText.split(","); @@ -143,10 +452,6 @@ public class ConfigConstants { ConfigConstants.simTexts = simText; } - public static String[] getMedia() { - return medias; - } - @Value("${media:mp3,wav,mp4,flv}") public void setMedia(String media) { String[] mediaArr = media.split(","); @@ -157,10 +462,6 @@ public class ConfigConstants { ConfigConstants.medias = Media; } - public static String[] getConvertMedias() { - return convertMedias; - } - @Value("${convertMedias:avi,mov,wmv,mkv,3gp,rm}") public void setConvertMedias(String convertMedia) { String[] mediaArr = convertMedia.split(","); @@ -171,11 +472,6 @@ public class ConfigConstants { ConfigConstants.convertMedias = ConvertMedia; } - public static String getMediaConvertDisable() { - return mediaConvertDisable; - } - - @Value("${media.convert.disable:true}") public void setMediaConvertDisable(String mediaConvertDisable) { setMediaConvertDisableValue(mediaConvertDisable); @@ -185,10 +481,35 @@ public class ConfigConstants { ConfigConstants.mediaConvertDisable = mediaConvertDisable; } - public static String getOfficePreviewType() { - return officePreviewType; + @Value("${tif.preview.type:tif}") + public void setTifPreviewType(String tifPreviewType) { + setTifPreviewTypeValue(tifPreviewType); } + public static void setTifPreviewTypeValue(String tifPreviewType) { + ConfigConstants.tifPreviewType = tifPreviewType; + } + + @Value("${cad.preview.type:svg}") + public void setCadPreviewType(String cadPreviewType) { + setCadPreviewTypeValue(cadPreviewType); + } + + public static void setCadPreviewTypeValue(String cadPreviewType) { + ConfigConstants.cadPreviewType = cadPreviewType; + } + + @Value("${prohibit:exe,dll}") + public void setProhibit(String prohibit) { + String[] prohibitArr = prohibit.split(","); + setProhibitValue(prohibitArr); + } + + public static void setProhibitValue(String[] prohibit) { + ConfigConstants.prohibit = prohibit; + } + + // 3. Office配置Setter方法 @Value("${office.preview.type:image}") public void setOfficePreviewType(String officePreviewType) { setOfficePreviewTypeValue(officePreviewType); @@ -198,10 +519,88 @@ public class ConfigConstants { ConfigConstants.officePreviewType = officePreviewType; } - public static String getFtpUsername() { - return ftpUsername; + @Value("${office.preview.switch.disabled:true}") + public void setOfficePreviewSwitchDisabled(String officePreviewSwitchDisabled) { + ConfigConstants.officePreviewSwitchDisabled = officePreviewSwitchDisabled; } + public static void setOfficePreviewSwitchDisabledValue(String officePreviewSwitchDisabled) { + ConfigConstants.officePreviewSwitchDisabled = officePreviewSwitchDisabled; + } + + @Value("${office.type.web:web}") + public void setOfficeTypeWeb(String officeTypeWeb) { + setOfficeTypeWebValue(officeTypeWeb); + } + + public static void setOfficeTypeWebValue(String officeTypeWeb) { + ConfigConstants.officeTypeWeb = officeTypeWeb; + } + + @Value("${office.pagerange:false}") + public void setOfficePageRange(String officePageRange) { + setOfficePageRangeValue(officePageRange); + } + + public static void setOfficePageRangeValue(String officePageRange) { + ConfigConstants.officePageRange = officePageRange; + } + + @Value("${office.watermark:false}") + public void setOfficeWatermark(String officeWatermark) { + setOfficeWatermarkValue(officeWatermark); + } + + public static void setOfficeWatermarkValue(String officeWatermark) { + ConfigConstants.officeWatermark = officeWatermark; + } + + @Value("${office.quality:80}") + public void setOfficeQuality(String officeQuality) { + setOfficeQualityValue(officeQuality); + } + + public static void setOfficeQualityValue(String officeQuality) { + ConfigConstants.officeQuality = officeQuality; + } + + @Value("${office.maximageresolution:150}") + public void setOfficeMaxImageResolution(String officeMaxImageResolution) { + setOfficeMaxImageResolutionValue(officeMaxImageResolution); + } + + public static void setOfficeMaxImageResolutionValue(String officeMaxImageResolution) { + ConfigConstants.officeMaxImageResolution = officeMaxImageResolution; + } + + @Value("${office.exportbookmarks:true}") + public void setOfficeExportBookmarks(Boolean officeExportBookmarks) { + setOfficeExportBookmarksValue(officeExportBookmarks); + } + + public static void setOfficeExportBookmarksValue(Boolean officeExportBookmarks) { + ConfigConstants.officeExportBookmarks = officeExportBookmarks; + } + + @Value("${office.exportnotes:true}") + public void setExportNotes(Boolean officeExportNotes) { + setOfficeExportNotesValue(officeExportNotes); + } + + public static void setOfficeExportNotesValue(Boolean officeExportNotes) { + ConfigConstants.officeExportNotes = officeExportNotes; + } + + @Value("${office.documentopenpasswords:true}") + public void setDocumentOpenPasswords(Boolean officeDocumentOpenPasswords) { + setOfficeDocumentOpenPasswordsValue(officeDocumentOpenPasswords); + } + + public static void setOfficeDocumentOpenPasswordsValue(Boolean officeDocumentOpenPasswords) { + ConfigConstants.officeDocumentOpenPasswords = officeDocumentOpenPasswords; + } + + // 4. FTP配置Setter方法 @Value("${ftp.username:}") public void setFtpUsername(String ftpUsername) { setFtpUsernameValue(ftpUsername); @@ -211,10 +610,6 @@ public class ConfigConstants { ConfigConstants.ftpUsername = ftpUsername; } - public static String getFtpPassword() { - return ftpPassword; - } - @Value("${ftp.password:}") public void setFtpPassword(String ftpPassword) { setFtpPasswordValue(ftpPassword); @@ -224,10 +619,6 @@ public class ConfigConstants { ConfigConstants.ftpPassword = ftpPassword; } - public static String getFtpControlEncoding() { - return ftpControlEncoding; - } - @Value("${ftp.control.encoding:UTF-8}") public void setFtpControlEncoding(String ftpControlEncoding) { setFtpControlEncodingValue(ftpControlEncoding); @@ -237,10 +628,7 @@ public class ConfigConstants { ConfigConstants.ftpControlEncoding = ftpControlEncoding; } - public static String getBaseUrl() { - return baseUrl; - } - + // 5. 路径配置Setter方法 @Value("${base.url:default}") public void setBaseUrl(String baseUrl) { setBaseUrlValue(baseUrl); @@ -250,10 +638,6 @@ public class ConfigConstants { ConfigConstants.baseUrl = baseUrl; } - public static String getFileDir() { - return fileDir; - } - @Value("${file.dir:default}") public void setFileDir(String fileDir) { setFileDirValue(fileDir); @@ -268,10 +652,6 @@ public class ConfigConstants { } } - public static String getLocalPreviewDir() { - return localPreviewDir; - } - @Value("${local.preview.dir:default}") public void setLocalPreviewDir(String localPreviewDir) { setLocalPreviewDirValue(localPreviewDir); @@ -286,12 +666,13 @@ public class ConfigConstants { ConfigConstants.localPreviewDir = localPreviewDir; } + // 6. 安全配置Setter方法 @Value("${trust.host:default}") public void setTrustHost(String trustHost) { setTrustHostSet(getHostValue(trustHost)); } - public static void setTrustHostValue(String trustHost){ + public static void setTrustHostValue(String trustHost) { setTrustHostSet(getHostValue(trustHost)); } @@ -300,7 +681,7 @@ public class ConfigConstants { setNotTrustHostSet(getHostValue(notTrustHost)); } - public static void setNotTrustHostValue(String notTrustHost){ + public static void setNotTrustHostValue(String notTrustHost) { setNotTrustHostSet(getHostValue(notTrustHost)); } @@ -314,27 +695,15 @@ public class ConfigConstants { } } - public static Set getTrustHostSet() { - return trustHostSet; - } - private static void setTrustHostSet(CopyOnWriteArraySet trustHostSet) { ConfigConstants.trustHostSet = trustHostSet; } - public static Set getNotTrustHostSet() { - return notTrustHostSet; - } - public static void setNotTrustHostSet(CopyOnWriteArraySet notTrustHostSet) { ConfigConstants.notTrustHostSet = notTrustHostSet; } - - public static String getPdfPresentationModeDisable() { - return pdfPresentationModeDisable; - } - + // 7. PDF配置Setter方法 @Value("${pdf.presentationMode.disable:true}") public void setPdfPresentationModeDisable(String pdfPresentationModeDisable) { setPdfPresentationModeDisableValue(pdfPresentationModeDisable); @@ -344,10 +713,6 @@ public class ConfigConstants { ConfigConstants.pdfPresentationModeDisable = pdfPresentationModeDisable; } - public static String getPdfOpenFileDisable() { - return pdfOpenFileDisable; - } - @Value("${pdf.openFile.disable:true}") public void setPdfOpenFileDisable(String pdfOpenFileDisable) { setPdfOpenFileDisableValue(pdfOpenFileDisable); @@ -357,10 +722,6 @@ public class ConfigConstants { ConfigConstants.pdfOpenFileDisable = pdfOpenFileDisable; } - public static String getPdfPrintDisable() { - return pdfPrintDisable; - } - @Value("${pdf.print.disable:true}") public void setPdfPrintDisable(String pdfPrintDisable) { setPdfPrintDisableValue(pdfPrintDisable); @@ -370,10 +731,6 @@ public class ConfigConstants { ConfigConstants.pdfPrintDisable = pdfPrintDisable; } - public static String getPdfDownloadDisable() { - return pdfDownloadDisable; - } - @Value("${pdf.download.disable:true}") public void setPdfDownloadDisable(String pdfDownloadDisable) { setPdfDownloadDisableValue(pdfDownloadDisable); @@ -383,10 +740,6 @@ public class ConfigConstants { ConfigConstants.pdfDownloadDisable = pdfDownloadDisable; } - public static String getPdfBookmarkDisable() { - return pdfBookmarkDisable; - } - @Value("${pdf.bookmark.disable:true}") public void setPdfBookmarkDisable(String pdfBookmarkDisable) { setPdfBookmarkDisableValue(pdfBookmarkDisable); @@ -396,11 +749,6 @@ public class ConfigConstants { ConfigConstants.pdfBookmarkDisable = pdfBookmarkDisable; } - - public static String getPdfDisableEditing() { - return pdfDisableEditing; - } - @Value("${pdf.disable.editing:true}") public void setpdfDisableEditing(String pdfDisableEditing) { setPdfDisableEditingValue(pdfDisableEditing); @@ -410,91 +758,6 @@ public class ConfigConstants { ConfigConstants.pdfDisableEditing = pdfDisableEditing; } - public static String getOfficePreviewSwitchDisabled() { - return officePreviewSwitchDisabled; - } - - @Value("${office.preview.switch.disabled:true}") - public void setOfficePreviewSwitchDisabled(String officePreviewSwitchDisabled) { - ConfigConstants.officePreviewSwitchDisabled = officePreviewSwitchDisabled; - } - - public static void setOfficePreviewSwitchDisabledValue(String officePreviewSwitchDisabled) { - ConfigConstants.officePreviewSwitchDisabled = officePreviewSwitchDisabled; - } - - public static Boolean getFileUploadDisable() { - return fileUploadDisable; - } - - @Value("${file.upload.disable:true}") - public void setFileUploadDisable(Boolean fileUploadDisable) { - setFileUploadDisableValue(fileUploadDisable); - } - - public static void setFileUploadDisableValue(Boolean fileUploadDisable) { - ConfigConstants.fileUploadDisable = fileUploadDisable; - } - - - public static String getTifPreviewType() { - return tifPreviewType; - } - - @Value("${tif.preview.type:tif}") - public void setTifPreviewType(String tifPreviewType) { - setTifPreviewTypeValue(tifPreviewType); - } - - public static void setTifPreviewTypeValue(String tifPreviewType) { - ConfigConstants.tifPreviewType = tifPreviewType; - } - - public static String[] getProhibit() { - return prohibit; - } - - @Value("${prohibit:exe,dll}") - public void setProhibit(String prohibit) { - String[] prohibitArr = prohibit.split(","); - setProhibitValue(prohibitArr); - } - - public static void setProhibitValue(String[] prohibit) { - ConfigConstants.prohibit = prohibit; - } - - public static String maxSize() { - return size; - } - - @Value("${spring.servlet.multipart.max-file-size:500MB}") - public void setSize(String size) { - setSizeValue(size); - } - - public static void setSizeValue(String size) { - ConfigConstants.size = size; - } - - public static String getPassword() { - return password; - } - - @Value("${delete.password:123456}") - public void setPassword(String password) { - setPasswordValue(password); - } - - public static void setPasswordValue(String password) { - ConfigConstants.password = password; - } - - - public static int getPdf2JpgDpi() { - return pdf2JpgDpi; - } - @Value("${pdf2jpg.dpi:105}") public void pdf2JpgDpi(int pdf2JpgDpi) { setPdf2JpgDpiValue(pdf2JpgDpi); @@ -504,98 +767,6 @@ public class ConfigConstants { ConfigConstants.pdf2JpgDpi = pdf2JpgDpi; } - public static String getOfficeTypeWeb() { - return officeTypeWeb; - } - - @Value("${office.type.web:web}") - public void setOfficeTypeWeb(String officeTypeWeb) { - setOfficeTypeWebValue(officeTypeWeb); - } - - public static void setOfficeTypeWebValue(String officeTypeWeb) { - ConfigConstants.officeTypeWeb = officeTypeWeb; - } - - - public static Boolean getDeleteSourceFile() { - return deleteSourceFile; - } - - @Value("${delete.source.file:true}") - public void setDeleteSourceFile(Boolean deleteSourceFile) { - setDeleteSourceFileValue(deleteSourceFile); - } - - public static void setDeleteSourceFileValue(Boolean deleteSourceFile) { - ConfigConstants.deleteSourceFile = deleteSourceFile; - } - - public static Boolean getDeleteCaptcha() { - return deleteCaptcha; - } - - @Value("${delete.captcha:false}") - public void setDeleteCaptcha(Boolean deleteCaptcha) { - setDeleteCaptchaValue(deleteCaptcha); - } - - public static void setDeleteCaptchaValue(Boolean deleteCaptcha) { - ConfigConstants.deleteCaptcha = deleteCaptcha; - } - - /** - * 以下为cad转换模块设置 - */ - - public static String getCadPreviewType() { - return cadPreviewType; - } - - @Value("${cad.preview.type:svg}") - public void setCadPreviewType(String cadPreviewType) { - setCadPreviewTypeValue(cadPreviewType); - } - - public static void setCadPreviewTypeValue(String cadPreviewType) { - ConfigConstants.cadPreviewType = cadPreviewType; - } - - - public static String getCadTimeout() { - return cadTimeout; - } - - @Value("${cad.timeout:90}") - public void setCadTimeout(String cadTimeout) { - setCadTimeoutValue(cadTimeout); - } - - public static void setCadTimeoutValue(String cadTimeout) { - ConfigConstants.cadTimeout = cadTimeout; - } - - - public static int getCadThread() { - return cadThread; - } - - @Value("${cad.thread:5}") - public void setCadThread(int cadThread) { - setCadThreadValue(cadThread); - } - - public static void setCadThreadValue(int cadThread) { - ConfigConstants.cadThread = cadThread; - } - - /** - * 以下为pdf转换模块设置 - */ - public static int getPdfTimeout() { - return pdfTimeout; - } - @Value("${pdf.timeout:90}") public void setPdfTimeout(int pdfTimeout) { setPdfTimeoutValue(pdfTimeout); @@ -605,11 +776,6 @@ public class ConfigConstants { ConfigConstants.pdfTimeout = pdfTimeout; } - - public static int getPdfTimeout80() { - return pdfTimeout80; - } - @Value("${pdf.timeout80:180}") public void setPdfTimeout80(int pdfTimeout80) { setPdfTimeout80Value(pdfTimeout80); @@ -619,12 +785,6 @@ public class ConfigConstants { ConfigConstants.pdfTimeout80 = pdfTimeout80; } - - - public static int getPdfTimeout200() { - return pdfTimeout200; - } - @Value("${pdf.timeout200:300}") public void setPdfTimeout200(int pdfTimeout200) { setPdfTimeout200Value(pdfTimeout200); @@ -634,11 +794,6 @@ public class ConfigConstants { ConfigConstants.pdfTimeout200 = pdfTimeout200; } - - public static int getPdfThread() { - return pdfThread; - } - @Value("${pdf.thread:5}") public void setPdfThread(int pdfThread) { setPdfThreadValue(pdfThread); @@ -648,109 +803,72 @@ public class ConfigConstants { ConfigConstants.pdfThread = pdfThread; } - /** - * 以下为OFFICE转换模块设置 - */ - - public static String getOfficePageRange() { - return officePageRange; + // 8. CAD配置Setter方法 + @Value("${cad.timeout:90}") + public void setCadTimeout(String cadTimeout) { + setCadTimeoutValue(cadTimeout); } - @Value("${office.pagerange:false}") - public void setOfficePageRange(String officePageRange) { - setOfficePageRangeValue(officePageRange); + public static void setCadTimeoutValue(String cadTimeout) { + ConfigConstants.cadTimeout = cadTimeout; } - public static void setOfficePageRangeValue(String officePageRange) { - ConfigConstants.officePageRange = officePageRange; + @Value("${cad.thread:5}") + public void setCadThread(int cadThread) { + setCadThreadValue(cadThread); } - public static String getOfficeWatermark() { - return officeWatermark; + public static void setCadThreadValue(int cadThread) { + ConfigConstants.cadThread = cadThread; } - @Value("${office.watermark:false}") - public void setOfficeWatermark(String officeWatermark) { - setOfficeWatermarkValue(officeWatermark); + // 9. 文件操作配置Setter方法 + @Value("${file.upload.disable:true}") + public void setFileUploadDisable(Boolean fileUploadDisable) { + setFileUploadDisableValue(fileUploadDisable); } - public static void setOfficeWatermarkValue(String officeWatermark) { - ConfigConstants.officeWatermark = officeWatermark; + public static void setFileUploadDisableValue(Boolean fileUploadDisable) { + ConfigConstants.fileUploadDisable = fileUploadDisable; } - public static String getOfficeQuality() { - return officeQuality; + @Value("${spring.servlet.multipart.max-file-size:500MB}") + public void setSize(String size) { + setSizeValue(size); } - @Value("${office.quality:80}") - public void setOfficeQuality(String officeQuality) { - setOfficeQualityValue(officeQuality); + public static void setSizeValue(String size) { + ConfigConstants.size = size; } - public static void setOfficeQualityValue(String officeQuality) { - ConfigConstants.officeQuality = officeQuality; + @Value("${delete.password:123456}") + public void setPassword(String password) { + setPasswordValue(password); } - public static String getOfficeMaxImageResolution() { - return officeMaxImageResolution; + public static void setPasswordValue(String password) { + ConfigConstants.password = password; } - @Value("${office.maximageresolution:150}") - public void setOfficeMaxImageResolution(String officeMaxImageResolution) { - setOfficeMaxImageResolutionValue(officeMaxImageResolution); + @Value("${delete.source.file:true}") + public void setDeleteSourceFile(Boolean deleteSourceFile) { + setDeleteSourceFileValue(deleteSourceFile); } - public static void setOfficeMaxImageResolutionValue(String officeMaxImageResolution) { - ConfigConstants.officeMaxImageResolution = officeMaxImageResolution; + public static void setDeleteSourceFileValue(Boolean deleteSourceFile) { + ConfigConstants.deleteSourceFile = deleteSourceFile; } - public static Boolean getOfficeExportBookmarks() { - return officeExportBookmarks; + @Value("${delete.captcha:false}") + public void setDeleteCaptcha(Boolean deleteCaptcha) { + setDeleteCaptchaValue(deleteCaptcha); } - @Value("${office.exportbookmarks:true}") - public void setOfficeExportBookmarks(Boolean officeExportBookmarks) { - setOfficeExportBookmarksValue(officeExportBookmarks); - } - - public static void setOfficeExportBookmarksValue(Boolean officeExportBookmarks) { - ConfigConstants.officeExportBookmarks = officeExportBookmarks; - } - - public static Boolean getOfficeExportNotes() { - return officeExportNotes; - } - - @Value("${office.exportnotes:true}") - public void setExportNotes(Boolean officeExportNotes) { - setOfficeExportNotesValue(officeExportNotes); - } - - public static void setOfficeExportNotesValue(Boolean officeExportNotes) { - ConfigConstants.officeExportNotes = officeExportNotes; - } - - public static Boolean getOfficeDocumentOpenPasswords() { - return officeDocumentOpenPasswords; - } - - @Value("${office.documentopenpasswords:true}") - public void setDocumentOpenPasswords(Boolean officeDocumentOpenPasswords) { - setOfficeDocumentOpenPasswordsValue(officeDocumentOpenPasswords); - } - - public static void setOfficeDocumentOpenPasswordsValue(Boolean officeDocumentOpenPasswords) { - ConfigConstants.officeDocumentOpenPasswords = officeDocumentOpenPasswords; - } - - /** - * 以下为首页显示 - */ - - public static String getBeian() { - return beian; + public static void setDeleteCaptchaValue(Boolean deleteCaptcha) { + ConfigConstants.deleteCaptcha = deleteCaptcha; } + // 10. 首页配置Setter方法 @Value("${beian:default}") public void setBeian(String beian) { setBeianValue(beian); @@ -760,11 +878,6 @@ public class ConfigConstants { ConfigConstants.beian = beian; } - - public static String getHomePageNumber() { - return homePageNumber; - } - @Value("${home.pagenumber:1}") public void setHomePageNumber(String homePageNumber) { setHomePageNumberValue(homePageNumber); @@ -774,10 +887,6 @@ public class ConfigConstants { ConfigConstants.homePageNumber = homePageNumber; } - public static String getHomePagination() { - return homePagination; - } - @Value("${home.pagination:true}") public void setHomePagination(String homePagination) { setHomePaginationValue(homePagination); @@ -787,10 +896,6 @@ public class ConfigConstants { ConfigConstants.homePagination = homePagination; } - public static String getHomePageSize() { - return homePageSize; - } - @Value("${home.pagesize:15}") public void setHomePageSize(String homePageSize) { setHomePageSizeValue(homePageSize); @@ -800,10 +905,6 @@ public class ConfigConstants { ConfigConstants.homePageSize = homePageSize; } - public static String getHomeSearch() { - return homeSearch; - } - @Value("${home.search:1}") public void setHomeSearch(String homeSearch) { setHomeSearchValue(homeSearch); @@ -813,4 +914,69 @@ public class ConfigConstants { ConfigConstants.homeSearch = homeSearch; } -} + // 11. 权限配置Setter方法 + @Value("${kk.Key:}") + public void setKey(String key) { + setKeyValue(key); + } + + public static void setKeyValue(String key) { + ConfigConstants.key = key; + } + + @Value("${kk.Picturespreview:true}") + public void setPicturesPreview(String picturesPreview) { + setPicturesPreviewValue(Boolean.parseBoolean(picturesPreview)); + } + + public static void setPicturesPreviewValue(boolean picturesPreview) { + ConfigConstants.picturesPreview = picturesPreview; + } + + @Value("${kk.Getcorsfile:true}") + public void setGetCorsFile(String getCorsFile) { + setGetCorsFileValue(Boolean.parseBoolean(getCorsFile)); + } + + public static void setGetCorsFileValue(boolean getCorsFile) { + ConfigConstants.getCorsFile = getCorsFile; + } + + @Value("${kk.addTask:true}") + public void setAddTask(String addTask) { + setAddTaskValue(Boolean.parseBoolean(addTask)); + } + + public static void setAddTaskValue(boolean addTask) { + ConfigConstants.addTask = addTask; + } + + @Value("${ase.key:1234567890123456}") + public void setaesKey(String aesKey) { + setaesKeyValue(aesKey); + } + + public static void setaesKeyValue(String aesKey) { + ConfigConstants.aesKey = aesKey; + } + + // 12. UserAgent配置Setter方法 + @Value("${useragent:false}") + public void setUserAgent(String userAgent) { + setUserAgentValue(userAgent); + } + + public static void setUserAgentValue(String userAgent) { + ConfigConstants.userAgent = userAgent; + } + + // 13. Basic认证配置Setter方法 + @Value("${basic.name:}") + public void setBasicName(String basicName) { + setBasicNameValue(basicName); + } + + public static void setBasicNameValue(String basicName) { + ConfigConstants.basicName = basicName; + } +} \ No newline at end of file diff --git a/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java b/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java index d9a11f73..f6deaa38 100644 --- a/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java +++ b/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java @@ -6,213 +6,370 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; +import java.nio.file.*; import java.util.Properties; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * @auther: chenjh * @time: 2019/4/10 16:16 - * @description 每隔1s读取并更新一次配置文件 + * @description 使用 WatchService 监听配置文件变化,实现事件驱动的配置更新 */ @Component public class ConfigRefreshComponent { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigRefreshComponent.class); + private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + private final ExecutorService watchServiceExecutor = Executors.newSingleThreadExecutor(); + private final Object lock = new Object(); + + // 防抖延迟时间(单位:秒) + private static final long DEBOUNCE_DELAY_SECONDS = 5; + + private ScheduledFuture scheduledReloadTask; + private WatchService watchService; + private volatile boolean running = true; + @PostConstruct - void refresh() { - Thread configRefreshThread = new Thread(new ConfigRefreshThread()); - configRefreshThread.start(); + void init() { + // 初始化时立即加载一次配置 + loadConfig(); + + // 启动监听线程 + watchServiceExecutor.submit(this::watchConfigFile); } - static class ConfigRefreshThread implements Runnable { - @Override - public void run() { + @PreDestroy + void destroy() { + running = false; + watchServiceExecutor.shutdownNow(); + scheduler.shutdownNow(); + + if (watchService != null) { + try { + watchService.close(); + } catch (IOException e) { + LOGGER.warn("关闭 WatchService 时发生异常", e); + } + } + } + + /** + * 监听配置文件变化 + */ + private void watchConfigFile() { + try { + String configFilePath = ConfigUtils.getCustomizedConfigPath(); + Path configPath = Paths.get(configFilePath); + Path configDir = configPath.getParent(); + + if (configDir == null) { + LOGGER.error("配置文件路径无效,无法获取父目录: {}", configFilePath); + return; + } + + watchService = FileSystems.getDefault().newWatchService(); + + // 注册监听目录的修改事件 + configDir.register(watchService, + StandardWatchEventKinds.ENTRY_MODIFY, + StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_DELETE); + + LOGGER.info("开始监听配置文件变化,配置文件: {}", configFilePath); + + while (running && !Thread.currentThread().isInterrupted()) { + try { + WatchKey key = watchService.take(); + + for (WatchEvent event : key.pollEvents()) { + WatchEvent.Kind kind = event.kind(); + Path changedPath = (Path) event.context(); + + // 检查是否是目标配置文件的变化 + if (changedPath.equals(configPath.getFileName())) { + handleConfigChange(kind); + } + } + + if (!key.reset()) { + LOGGER.warn("WatchKey 无法重置,监听可能已失效"); + break; + } + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.info("配置文件监听线程被中断"); + break; + } catch (ClosedWatchServiceException e) { + LOGGER.info("WatchService 已关闭"); + break; + } + } + } catch (IOException e) { + LOGGER.error("初始化配置文件监听失败", e); + } finally { + LOGGER.info("配置文件监听线程结束"); + } + } + + /** + * 处理配置文件变化事件 + */ + private void handleConfigChange(WatchEvent.Kind kind) { + if (kind == StandardWatchEventKinds.ENTRY_DELETE) { + LOGGER.warn("配置文件被删除,停止监听"); + running = false; + return; + } + + if (kind == StandardWatchEventKinds.ENTRY_MODIFY || + kind == StandardWatchEventKinds.ENTRY_CREATE) { + + // 使用防抖机制:取消之前的任务,重新调度新任务 + synchronized (lock) { + if (scheduledReloadTask != null && !scheduledReloadTask.isDone()) { + scheduledReloadTask.cancel(false); + } + + scheduledReloadTask = scheduler.schedule(() -> { + try { + LOGGER.info("检测到配置文件变化,重新加载配置"); + loadConfig(); + } catch (Exception e) { + LOGGER.error("重新加载配置失败", e); + } + }, DEBOUNCE_DELAY_SECONDS, TimeUnit.SECONDS); + } + } + } + + /** + * 加载配置文件 + */ + private void loadConfig() { + synchronized (lock) { try { Properties properties = new Properties(); - String text; - String media; - boolean cacheEnabled; - String[] textArray; - String[] mediaArray; - String officePreviewType; - String officePreviewSwitchDisabled; - String ftpUsername; - String ftpPassword; - String ftpControlEncoding; String configFilePath = ConfigUtils.getCustomizedConfigPath(); - String baseUrl; - String trustHost; - String notTrustHost; - String pdfPresentationModeDisable; - String pdfOpenFileDisable; - String pdfPrintDisable; - String pdfDownloadDisable; - String pdfBookmarkDisable; - String pdfDisableEditing; - boolean fileUploadDisable; - String tifPreviewType; - String prohibit; - String[] prohibitArray; - String beian; - String size; - String password; - int pdf2JpgDpi; - String officeTypeWeb; - String cadPreviewType; - boolean deleteSourceFile; - boolean deleteCaptcha; - String officPageRange; - String officWatermark; - String officQuality; - String officMaxImageResolution; - boolean officExportBookmarks; - boolean officeExportNotes; - boolean officeDocumentOpenPasswords; - String cadTimeout; - int cadThread; - String homePageNumber; - String homePagination; - String homePageSize; - String homeSearch; - int pdfTimeout; - int pdfTimeout80; - int pdfTimeout200; - int pdfThread; - while (true) { - FileReader fileReader = new FileReader(configFilePath); - BufferedReader bufferedReader = new BufferedReader(fileReader); + + // 检查文件是否存在 + Path configPath = Paths.get(configFilePath); + if (!Files.exists(configPath)) { + LOGGER.warn("配置文件不存在: {}", configFilePath); + return; + } + + try (BufferedReader bufferedReader = new BufferedReader(new FileReader(configFilePath))) { properties.load(bufferedReader); ConfigUtils.restorePropertiesFromEnvFormat(properties); - cacheEnabled = Boolean.parseBoolean(properties.getProperty("cache.enabled", ConfigConstants.DEFAULT_CACHE_ENABLED)); - text = properties.getProperty("simText", ConfigConstants.DEFAULT_TXT_TYPE); - media = properties.getProperty("media", ConfigConstants.DEFAULT_MEDIA_TYPE); - officePreviewType = properties.getProperty("office.preview.type", ConfigConstants.DEFAULT_OFFICE_PREVIEW_TYPE); - officePreviewSwitchDisabled = properties.getProperty("office.preview.switch.disabled", ConfigConstants.DEFAULT_OFFICE_PREVIEW_SWITCH_DISABLED); - ftpUsername = properties.getProperty("ftp.username", ConfigConstants.DEFAULT_FTP_USERNAME); - ftpPassword = properties.getProperty("ftp.password", ConfigConstants.DEFAULT_FTP_PASSWORD); - ftpControlEncoding = properties.getProperty("ftp.control.encoding", ConfigConstants.DEFAULT_FTP_CONTROL_ENCODING); - textArray = text.split(","); - mediaArray = media.split(","); - baseUrl = properties.getProperty("base.url", ConfigConstants.DEFAULT_VALUE); - trustHost = properties.getProperty("trust.host", ConfigConstants.DEFAULT_VALUE); - notTrustHost = properties.getProperty("not.trust.host", ConfigConstants.DEFAULT_VALUE); - pdfPresentationModeDisable = properties.getProperty("pdf.presentationMode.disable", ConfigConstants.DEFAULT_PDF_PRESENTATION_MODE_DISABLE); - pdfOpenFileDisable = properties.getProperty("pdf.openFile.disable", ConfigConstants.DEFAULT_PDF_OPEN_FILE_DISABLE); - pdfPrintDisable = properties.getProperty("pdf.print.disable", ConfigConstants.DEFAULT_PDF_PRINT_DISABLE); - pdfDownloadDisable = properties.getProperty("pdf.download.disable", ConfigConstants.DEFAULT_PDF_DOWNLOAD_DISABLE); - pdfBookmarkDisable = properties.getProperty("pdf.bookmark.disable", ConfigConstants.DEFAULT_PDF_BOOKMARK_DISABLE); - pdfDisableEditing = properties.getProperty("pdf.disable.editing", ConfigConstants.DEFAULT_PDF_DISABLE_EDITING); - fileUploadDisable = Boolean.parseBoolean(properties.getProperty("file.upload.disable", ConfigConstants.DEFAULT_FILE_UPLOAD_DISABLE)); - tifPreviewType = properties.getProperty("tif.preview.type", ConfigConstants.DEFAULT_TIF_PREVIEW_TYPE); - cadPreviewType = properties.getProperty("cad.preview.type", ConfigConstants.DEFAULT_CAD_PREVIEW_TYPE); - size = properties.getProperty("spring.servlet.multipart.max-file-size", ConfigConstants.DEFAULT_SIZE); - beian = properties.getProperty("beian", ConfigConstants.DEFAULT_BEIAN); - prohibit = properties.getProperty("prohibit", ConfigConstants.DEFAULT_PROHIBIT); - password = properties.getProperty("delete.password", ConfigConstants.DEFAULT_PASSWORD); - pdf2JpgDpi = Integer.parseInt(properties.getProperty("pdf2jpg.dpi", ConfigConstants.DEFAULT_PDF2_JPG_DPI)); - officeTypeWeb = properties.getProperty("office.type.web", ConfigConstants.DEFAULT_OFFICE_TYPE_WEB); - deleteSourceFile = Boolean.parseBoolean(properties.getProperty("delete.source.file", ConfigConstants.DEFAULT_DELETE_SOURCE_FILE)); - deleteCaptcha = Boolean.parseBoolean(properties.getProperty("delete.captcha", ConfigConstants.DEFAULT_DELETE_CAPTCHA)); - officPageRange = properties.getProperty("office.pagerange", ConfigConstants.DEFAULT_OFFICE_PAQERANQE); - officWatermark = properties.getProperty("office.watermark", ConfigConstants.DEFAULT_OFFICE_WATERMARK); - officQuality = properties.getProperty("office.quality", ConfigConstants.DEFAULT_OFFICE_QUALITY); - officMaxImageResolution = properties.getProperty("office.maximageresolution", ConfigConstants.DEFAULT_OFFICE_MAXIMAQERESOLUTION); - officExportBookmarks = Boolean.parseBoolean(properties.getProperty("office.exportbookmarks", ConfigConstants.DEFAULT_OFFICE_EXPORTBOOKMARKS)); - officeExportNotes = Boolean.parseBoolean(properties.getProperty("office.exportnotes", ConfigConstants.DEFAULT_OFFICE_EXPORTNOTES)); - officeDocumentOpenPasswords = Boolean.parseBoolean(properties.getProperty("office.documentopenpasswords", ConfigConstants.DEFAULT_OFFICE_EOCUMENTOPENPASSWORDS)); - cadTimeout = properties.getProperty("cad.timeout", ConfigConstants.DEFAULT_CAD_TIMEOUT); - homePageNumber = properties.getProperty("home.pagenumber", ConfigConstants.DEFAULT_HOME_PAGENUMBER); - homePagination = properties.getProperty("home.pagination", ConfigConstants.DEFAULT_HOME_PAGINATION); - homePageSize = properties.getProperty("home.pagesize", ConfigConstants.DEFAULT_HOME_PAGSIZE); - homeSearch = properties.getProperty("home.search", ConfigConstants.DEFAULT_HOME_SEARCH); - cadThread = Integer.parseInt(properties.getProperty("cad.thread", ConfigConstants.DEFAULT_CAD_THREAD)); - pdfTimeout = Integer.parseInt(properties.getProperty("pdf.timeout", ConfigConstants.DEFAULT_PDF_TIMEOUT)); - pdfTimeout80 = Integer.parseInt(properties.getProperty("pdf.timeout80", ConfigConstants.DEFAULT_PDF_TIMEOUT80)); - pdfTimeout200 = Integer.parseInt(properties.getProperty("pdf.timeout200", ConfigConstants.DEFAULT_PDF_TIMEOUT200)); - pdfThread = Integer.parseInt(properties.getProperty("pdf.thread", ConfigConstants.DEFAULT_PDF_THREAD)); - prohibitArray = prohibit.split(","); - ConfigConstants.setCacheEnabledValueValue(cacheEnabled); - ConfigConstants.setSimTextValue(textArray); - ConfigConstants.setMediaValue(mediaArray); - ConfigConstants.setOfficePreviewTypeValue(officePreviewType); - ConfigConstants.setFtpUsernameValue(ftpUsername); - ConfigConstants.setFtpPasswordValue(ftpPassword); - ConfigConstants.setFtpControlEncodingValue(ftpControlEncoding); - ConfigConstants.setBaseUrlValue(baseUrl); - ConfigConstants.setTrustHostValue(trustHost); - ConfigConstants.setNotTrustHostValue(notTrustHost); - ConfigConstants.setOfficePreviewSwitchDisabledValue(officePreviewSwitchDisabled); - ConfigConstants.setPdfPresentationModeDisableValue(pdfPresentationModeDisable); - ConfigConstants.setPdfOpenFileDisableValue(pdfOpenFileDisable); - ConfigConstants.setPdfPrintDisableValue(pdfPrintDisable); - ConfigConstants.setPdfDownloadDisableValue(pdfDownloadDisable); - ConfigConstants.setPdfBookmarkDisableValue(pdfBookmarkDisable); - ConfigConstants.setPdfDisableEditingValue(pdfDisableEditing); - ConfigConstants.setFileUploadDisableValue(fileUploadDisable); - ConfigConstants.setTifPreviewTypeValue(tifPreviewType); - ConfigConstants.setCadPreviewTypeValue(cadPreviewType); - ConfigConstants.setBeianValue(beian); - ConfigConstants.setSizeValue(size); - ConfigConstants.setProhibitValue(prohibitArray); - ConfigConstants.setPasswordValue(password); - ConfigConstants.setPdf2JpgDpiValue(pdf2JpgDpi); - ConfigConstants.setOfficeTypeWebValue(officeTypeWeb); - ConfigConstants.setOfficePageRangeValue(officPageRange); - ConfigConstants.setOfficeWatermarkValue(officWatermark); - ConfigConstants.setOfficeQualityValue(officQuality); - ConfigConstants.setOfficeMaxImageResolutionValue(officMaxImageResolution); - ConfigConstants.setOfficeExportBookmarksValue(officExportBookmarks); - ConfigConstants.setOfficeExportNotesValue(officeExportNotes); - ConfigConstants.setOfficeDocumentOpenPasswordsValue(officeDocumentOpenPasswords); - ConfigConstants.setDeleteSourceFileValue(deleteSourceFile); - ConfigConstants.setDeleteCaptchaValue(deleteCaptcha); - ConfigConstants.setCadTimeoutValue(cadTimeout); - ConfigConstants.setCadThreadValue(cadThread); - ConfigConstants.setHomePageNumberValue(homePageNumber); - ConfigConstants.setHomePaginationValue(homePagination); - ConfigConstants.setHomePageSizeValue(homePageSize); - ConfigConstants.setHomeSearchValue(homeSearch); - ConfigConstants.setPdfTimeoutValue(pdfTimeout); - ConfigConstants.setPdfTimeout80Value(pdfTimeout80); - ConfigConstants.setPdfTimeout200Value(pdfTimeout200); - ConfigConstants.setPdfThreadValue(pdfThread); + // 解析并设置配置项 + updateConfigConstants(properties); setWatermarkConfig(properties); - bufferedReader.close(); - fileReader.close(); - TimeUnit.SECONDS.sleep(1); + + LOGGER.info("配置文件重新加载完成"); } - } catch (IOException | InterruptedException e) { + + } catch (IOException e) { LOGGER.error("读取配置文件异常", e); } } - - private void setWatermarkConfig(Properties properties) { - String watermarkTxt = properties.getProperty("watermark.txt", WatermarkConfigConstants.DEFAULT_WATERMARK_TXT); - String watermarkXSpace = properties.getProperty("watermark.x.space", WatermarkConfigConstants.DEFAULT_WATERMARK_X_SPACE); - String watermarkYSpace = properties.getProperty("watermark.y.space", WatermarkConfigConstants.DEFAULT_WATERMARK_Y_SPACE); - String watermarkFont = properties.getProperty("watermark.font", WatermarkConfigConstants.DEFAULT_WATERMARK_FONT); - String watermarkFontsize = properties.getProperty("watermark.fontsize", WatermarkConfigConstants.DEFAULT_WATERMARK_FONTSIZE); - String watermarkColor = properties.getProperty("watermark.color", WatermarkConfigConstants.DEFAULT_WATERMARK_COLOR); - String watermarkAlpha = properties.getProperty("watermark.alpha", WatermarkConfigConstants.DEFAULT_WATERMARK_ALPHA); - String watermarkWidth = properties.getProperty("watermark.width", WatermarkConfigConstants.DEFAULT_WATERMARK_WIDTH); - String watermarkHeight = properties.getProperty("watermark.height", WatermarkConfigConstants.DEFAULT_WATERMARK_HEIGHT); - String watermarkAngle = properties.getProperty("watermark.angle", WatermarkConfigConstants.DEFAULT_WATERMARK_ANGLE); - WatermarkConfigConstants.setWatermarkTxtValue(watermarkTxt); - WatermarkConfigConstants.setWatermarkXSpaceValue(watermarkXSpace); - WatermarkConfigConstants.setWatermarkYSpaceValue(watermarkYSpace); - WatermarkConfigConstants.setWatermarkFontValue(watermarkFont); - WatermarkConfigConstants.setWatermarkFontsizeValue(watermarkFontsize); - WatermarkConfigConstants.setWatermarkColorValue(watermarkColor); - WatermarkConfigConstants.setWatermarkAlphaValue(watermarkAlpha); - WatermarkConfigConstants.setWatermarkWidthValue(watermarkWidth); - WatermarkConfigConstants.setWatermarkHeightValue(watermarkHeight); - WatermarkConfigConstants.setWatermarkAngleValue(watermarkAngle); - - } } -} + /** + * 更新配置常量 + */ + private void updateConfigConstants(Properties properties) { + // 1. 缓存配置 + boolean cacheEnabled = Boolean.parseBoolean(properties.getProperty("cache.enabled", ConfigConstants.DEFAULT_CACHE_ENABLED)); + + // 2. 文件类型配置 + String text = properties.getProperty("simText", ConfigConstants.DEFAULT_TXT_TYPE); + String media = properties.getProperty("media", ConfigConstants.DEFAULT_MEDIA_TYPE); + String tifPreviewType = properties.getProperty("tif.preview.type", ConfigConstants.DEFAULT_TIF_PREVIEW_TYPE); + String cadPreviewType = properties.getProperty("cad.preview.type", ConfigConstants.DEFAULT_CAD_PREVIEW_TYPE); + String prohibit = properties.getProperty("prohibit", ConfigConstants.DEFAULT_PROHIBIT); + + String[] textArray = text.split(","); + String[] mediaArray = media.split(","); + String[] prohibitArray = prohibit.split(","); + + // 3. Office配置 + String officePreviewType = properties.getProperty("office.preview.type", ConfigConstants.DEFAULT_OFFICE_PREVIEW_TYPE); + String officePreviewSwitchDisabled = properties.getProperty("office.preview.switch.disabled", ConfigConstants.DEFAULT_OFFICE_PREVIEW_SWITCH_DISABLED); + String officeTypeWeb = properties.getProperty("office.type.web", ConfigConstants.DEFAULT_OFFICE_TYPE_WEB); + String officPageRange = properties.getProperty("office.pagerange", ConfigConstants.DEFAULT_OFFICE_PAQERANQE); + String officWatermark = properties.getProperty("office.watermark", ConfigConstants.DEFAULT_OFFICE_WATERMARK); + String officQuality = properties.getProperty("office.quality", ConfigConstants.DEFAULT_OFFICE_QUALITY); + String officMaxImageResolution = properties.getProperty("office.maximageresolution", ConfigConstants.DEFAULT_OFFICE_MAXIMAQERESOLUTION); + boolean officExportBookmarks = Boolean.parseBoolean(properties.getProperty("office.exportbookmarks", ConfigConstants.DEFAULT_OFFICE_EXPORTBOOKMARKS)); + boolean officeExportNotes = Boolean.parseBoolean(properties.getProperty("office.exportnotes", ConfigConstants.DEFAULT_OFFICE_EXPORTNOTES)); + boolean officeDocumentOpenPasswords = Boolean.parseBoolean(properties.getProperty("office.documentopenpasswords", ConfigConstants.DEFAULT_OFFICE_EOCUMENTOPENPASSWORDS)); + + // 4. FTP配置 + String ftpUsername = properties.getProperty("ftp.username", ConfigConstants.DEFAULT_FTP_USERNAME); + String ftpPassword = properties.getProperty("ftp.password", ConfigConstants.DEFAULT_FTP_PASSWORD); + String ftpControlEncoding = properties.getProperty("ftp.control.encoding", ConfigConstants.DEFAULT_FTP_CONTROL_ENCODING); + + // 5. 路径配置 + String baseUrl = properties.getProperty("base.url", ConfigConstants.DEFAULT_VALUE); + + // 6. 安全配置 + String trustHost = properties.getProperty("trust.host", ConfigConstants.DEFAULT_VALUE); + String notTrustHost = properties.getProperty("not.trust.host", ConfigConstants.DEFAULT_VALUE); + + // 7. PDF配置 + String pdfPresentationModeDisable = properties.getProperty("pdf.presentationMode.disable", ConfigConstants.DEFAULT_PDF_PRESENTATION_MODE_DISABLE); + String pdfOpenFileDisable = properties.getProperty("pdf.openFile.disable", ConfigConstants.DEFAULT_PDF_OPEN_FILE_DISABLE); + String pdfPrintDisable = properties.getProperty("pdf.print.disable", ConfigConstants.DEFAULT_PDF_PRINT_DISABLE); + String pdfDownloadDisable = properties.getProperty("pdf.download.disable", ConfigConstants.DEFAULT_PDF_DOWNLOAD_DISABLE); + String pdfBookmarkDisable = properties.getProperty("pdf.bookmark.disable", ConfigConstants.DEFAULT_PDF_BOOKMARK_DISABLE); + String pdfDisableEditing = properties.getProperty("pdf.disable.editing", ConfigConstants.DEFAULT_PDF_DISABLE_EDITING); + int pdf2JpgDpi = Integer.parseInt(properties.getProperty("pdf2jpg.dpi", ConfigConstants.DEFAULT_PDF2_JPG_DPI)); + int pdfTimeout = Integer.parseInt(properties.getProperty("pdf.timeout", ConfigConstants.DEFAULT_PDF_TIMEOUT)); + int pdfTimeout80 = Integer.parseInt(properties.getProperty("pdf.timeout80", ConfigConstants.DEFAULT_PDF_TIMEOUT80)); + int pdfTimeout200 = Integer.parseInt(properties.getProperty("pdf.timeout200", ConfigConstants.DEFAULT_PDF_TIMEOUT200)); + int pdfThread = Integer.parseInt(properties.getProperty("pdf.thread", ConfigConstants.DEFAULT_PDF_THREAD)); + + // 8. CAD配置 + String cadTimeout = properties.getProperty("cad.timeout", ConfigConstants.DEFAULT_CAD_TIMEOUT); + int cadThread = Integer.parseInt(properties.getProperty("cad.thread", ConfigConstants.DEFAULT_CAD_THREAD)); + + // 9. 文件操作配置 + boolean fileUploadDisable = Boolean.parseBoolean(properties.getProperty("file.upload.disable", ConfigConstants.DEFAULT_FILE_UPLOAD_DISABLE)); + String size = properties.getProperty("spring.servlet.multipart.max-file-size", ConfigConstants.DEFAULT_SIZE); + String password = properties.getProperty("delete.password", ConfigConstants.DEFAULT_PASSWORD); + boolean deleteSourceFile = Boolean.parseBoolean(properties.getProperty("delete.source.file", ConfigConstants.DEFAULT_DELETE_SOURCE_FILE)); + boolean deleteCaptcha = Boolean.parseBoolean(properties.getProperty("delete.captcha", ConfigConstants.DEFAULT_DELETE_CAPTCHA)); + + // 10. 首页配置 + String beian = properties.getProperty("beian", ConfigConstants.DEFAULT_BEIAN); + String homePageNumber = properties.getProperty("home.pagenumber", ConfigConstants.DEFAULT_HOME_PAGENUMBER); + String homePagination = properties.getProperty("home.pagination", ConfigConstants.DEFAULT_HOME_PAGINATION); + String homePageSize = properties.getProperty("home.pagesize", ConfigConstants.DEFAULT_HOME_PAGSIZE); + String homeSearch = properties.getProperty("home.search", ConfigConstants.DEFAULT_HOME_SEARCH); + + // 11. 权限配置 + String key = properties.getProperty("kk.Key", ConfigConstants.DEFAULT_KEY); + boolean picturesPreview = Boolean.parseBoolean(properties.getProperty("kk.Picturespreview", ConfigConstants.DEFAULT_PICTURES_PREVIEW)); + boolean getCorsFile = Boolean.parseBoolean(properties.getProperty("kk.Getcorsfile", ConfigConstants.DEFAULT_GET_CORS_FILE)); + boolean addTask = Boolean.parseBoolean(properties.getProperty("kk.addTask", ConfigConstants.DEFAULT_ADD_TASK)); + String aesKey = properties.getProperty("ase.key", ConfigConstants.DEFAULT_AES_KEY); + // 12. UserAgent配置 + String userAgent = properties.getProperty("useragent", ConfigConstants.DEFAULT_USER_AGENT); + + // 13. Basic认证配置 + String basicName = properties.getProperty("basic.name", ConfigConstants.DEFAULT_BASIC_NAME); + + // 设置配置值 + // 1. 缓存配置 + ConfigConstants.setCacheEnabledValueValue(cacheEnabled); + + // 2. 文件类型配置 + ConfigConstants.setSimTextValue(textArray); + ConfigConstants.setMediaValue(mediaArray); + ConfigConstants.setTifPreviewTypeValue(tifPreviewType); + ConfigConstants.setCadPreviewTypeValue(cadPreviewType); + ConfigConstants.setProhibitValue(prohibitArray); + + // 3. Office配置 + ConfigConstants.setOfficePreviewTypeValue(officePreviewType); + ConfigConstants.setOfficePreviewSwitchDisabledValue(officePreviewSwitchDisabled); + ConfigConstants.setOfficeTypeWebValue(officeTypeWeb); + ConfigConstants.setOfficePageRangeValue(officPageRange); + ConfigConstants.setOfficeWatermarkValue(officWatermark); + ConfigConstants.setOfficeQualityValue(officQuality); + ConfigConstants.setOfficeMaxImageResolutionValue(officMaxImageResolution); + ConfigConstants.setOfficeExportBookmarksValue(officExportBookmarks); + ConfigConstants.setOfficeExportNotesValue(officeExportNotes); + ConfigConstants.setOfficeDocumentOpenPasswordsValue(officeDocumentOpenPasswords); + + // 4. FTP配置 + ConfigConstants.setFtpUsernameValue(ftpUsername); + ConfigConstants.setFtpPasswordValue(ftpPassword); + ConfigConstants.setFtpControlEncodingValue(ftpControlEncoding); + + // 5. 路径配置 + ConfigConstants.setBaseUrlValue(baseUrl); + + // 6. 安全配置 + ConfigConstants.setTrustHostValue(trustHost); + ConfigConstants.setNotTrustHostValue(notTrustHost); + + // 7. PDF配置 + ConfigConstants.setPdfPresentationModeDisableValue(pdfPresentationModeDisable); + ConfigConstants.setPdfOpenFileDisableValue(pdfOpenFileDisable); + ConfigConstants.setPdfPrintDisableValue(pdfPrintDisable); + ConfigConstants.setPdfDownloadDisableValue(pdfDownloadDisable); + ConfigConstants.setPdfBookmarkDisableValue(pdfBookmarkDisable); + ConfigConstants.setPdfDisableEditingValue(pdfDisableEditing); + ConfigConstants.setPdf2JpgDpiValue(pdf2JpgDpi); + ConfigConstants.setPdfTimeoutValue(pdfTimeout); + ConfigConstants.setPdfTimeout80Value(pdfTimeout80); + ConfigConstants.setPdfTimeout200Value(pdfTimeout200); + ConfigConstants.setPdfThreadValue(pdfThread); + + // 8. CAD配置 + ConfigConstants.setCadTimeoutValue(cadTimeout); + ConfigConstants.setCadThreadValue(cadThread); + + // 9. 文件操作配置 + ConfigConstants.setFileUploadDisableValue(fileUploadDisable); + ConfigConstants.setSizeValue(size); + ConfigConstants.setPasswordValue(password); + ConfigConstants.setDeleteSourceFileValue(deleteSourceFile); + ConfigConstants.setDeleteCaptchaValue(deleteCaptcha); + + // 10. 首页配置 + ConfigConstants.setBeianValue(beian); + ConfigConstants.setHomePageNumberValue(homePageNumber); + ConfigConstants.setHomePaginationValue(homePagination); + ConfigConstants.setHomePageSizeValue(homePageSize); + ConfigConstants.setHomeSearchValue(homeSearch); + + // 11. 权限配置 + ConfigConstants.setKeyValue(key); + ConfigConstants.setPicturesPreviewValue(picturesPreview); + ConfigConstants.setGetCorsFileValue(getCorsFile); + ConfigConstants.setAddTaskValue(addTask); + ConfigConstants.setaesKeyValue(aesKey); + + // 12. UserAgent配置 + ConfigConstants.setUserAgentValue(userAgent); + + // 13. Basic认证配置 + ConfigConstants.setBasicNameValue(basicName); + } + + /** + * 设置水印配置 + */ + private void setWatermarkConfig(Properties properties) { + String watermarkTxt = properties.getProperty("watermark.txt", WatermarkConfigConstants.DEFAULT_WATERMARK_TXT); + String watermarkXSpace = properties.getProperty("watermark.x.space", WatermarkConfigConstants.DEFAULT_WATERMARK_X_SPACE); + String watermarkYSpace = properties.getProperty("watermark.y.space", WatermarkConfigConstants.DEFAULT_WATERMARK_Y_SPACE); + String watermarkFont = properties.getProperty("watermark.font", WatermarkConfigConstants.DEFAULT_WATERMARK_FONT); + String watermarkFontsize = properties.getProperty("watermark.fontsize", WatermarkConfigConstants.DEFAULT_WATERMARK_FONTSIZE); + String watermarkColor = properties.getProperty("watermark.color", WatermarkConfigConstants.DEFAULT_WATERMARK_COLOR); + String watermarkAlpha = properties.getProperty("watermark.alpha", WatermarkConfigConstants.DEFAULT_WATERMARK_ALPHA); + String watermarkWidth = properties.getProperty("watermark.width", WatermarkConfigConstants.DEFAULT_WATERMARK_WIDTH); + String watermarkHeight = properties.getProperty("watermark.height", WatermarkConfigConstants.DEFAULT_WATERMARK_HEIGHT); + String watermarkAngle = properties.getProperty("watermark.angle", WatermarkConfigConstants.DEFAULT_WATERMARK_ANGLE); + + WatermarkConfigConstants.setWatermarkTxtValue(watermarkTxt); + WatermarkConfigConstants.setWatermarkXSpaceValue(watermarkXSpace); + WatermarkConfigConstants.setWatermarkYSpaceValue(watermarkYSpace); + WatermarkConfigConstants.setWatermarkFontValue(watermarkFont); + WatermarkConfigConstants.setWatermarkFontsizeValue(watermarkFontsize); + WatermarkConfigConstants.setWatermarkColorValue(watermarkColor); + WatermarkConfigConstants.setWatermarkAlphaValue(watermarkAlpha); + WatermarkConfigConstants.setWatermarkWidthValue(watermarkWidth); + WatermarkConfigConstants.setWatermarkHeightValue(watermarkHeight); + WatermarkConfigConstants.setWatermarkAngleValue(watermarkAngle); + } +} \ No newline at end of file diff --git a/server/src/main/java/cn/keking/utils/AESUtil.java b/server/src/main/java/cn/keking/utils/AESUtil.java new file mode 100644 index 00000000..abd5b958 --- /dev/null +++ b/server/src/main/java/cn/keking/utils/AESUtil.java @@ -0,0 +1,79 @@ +package cn.keking.utils; + +import cn.keking.config.ConfigConstants; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * AES加密解密工具类(目前AES比DES和DES3更安全,速度更快,对称加密一般采用AES) + */ +public class AESUtil { + private static final String aesKey = ConfigConstants.getaesKey(); + + /** + * AES解密 + */ + public static String AesDecrypt(String url) { + if (!aesKey(aesKey)) { + return null; + } + try { + byte[] raw = aesKey.getBytes(StandardCharsets.UTF_8); + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + byte[] encrypted1 = Base64.getDecoder().decode(url);//先用base64解密 + byte[] original = cipher.doFinal(encrypted1); + return new String(original, StandardCharsets.UTF_8); + } catch (Exception e) { + if (e.getMessage().contains("Given final block not properly padded. Such issues can arise if a bad key is used during decryption")) { + return "Keyerror"; + }else if (e.getMessage().contains("Input byte array has incorrect ending byte")) { + return "byteerror"; + }else if (e.getMessage().contains("Illegal base64 character")) { + return "base64error"; + }else if (e.getMessage().contains("Input length must be multiple of 16 when decrypting with padded cipher")) { + return "byteerror"; + }else { + System.out.println("ace错误:"+e); + return null; + } + } + } + + /** + * AES加密 + */ + public static String aesEncrypt(String url) { + if (!aesKey(aesKey)) { + return null; + } + try { + byte[] raw = aesKey.getBytes(StandardCharsets.UTF_8); + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式" + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + byte[] encrypted = cipher.doFinal(url.getBytes(StandardCharsets.UTF_8)); + return new String(Base64.getEncoder().encode(encrypted));//此处使用BASE64做转码功能,同时能起到2次加密的作用。 + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static boolean aesKey(String aesKey) { + if (aesKey == null) { + System.out.print("Key为空null"); + return false; + } + // 判断Key是否为16位 + if (aesKey.length() != 16) { + System.out.print("Key长度不是16位"); + return false; + } + return true; + } +} diff --git a/server/src/main/java/cn/keking/utils/DownloadUtils.java b/server/src/main/java/cn/keking/utils/DownloadUtils.java index c42a53f6..391cd609 100644 --- a/server/src/main/java/cn/keking/utils/DownloadUtils.java +++ b/server/src/main/java/cn/keking/utils/DownloadUtils.java @@ -4,11 +4,10 @@ import cn.keking.config.ConfigConstants; import cn.keking.model.FileAttribute; import cn.keking.model.ReturnResponse; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; import io.mola.galimatias.GalimatiasParseException; import org.apache.commons.io.FileUtils; -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; -import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; @@ -22,12 +21,13 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.Map; import java.util.UUID; -import static cn.keking.utils.KkFileUtils.isFtpUrl; -import static cn.keking.utils.KkFileUtils.isHttpUrl; +import static cn.keking.utils.KkFileUtils.*; /** * @author yudian-it @@ -39,6 +39,7 @@ public class DownloadUtils { private static final String URL_PARAM_FTP_USERNAME = "ftp.username"; private static final String URL_PARAM_FTP_PASSWORD = "ftp.password"; private static final String URL_PARAM_FTP_CONTROL_ENCODING = "ftp.control.encoding"; + private static final String URL_PARAM_FTP_PORT = "ftp.control.port"; private static final RestTemplate restTemplate = new RestTemplate(); private static final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); private static final ObjectMapper mapper = new ObjectMapper(); @@ -53,7 +54,6 @@ public class DownloadUtils { // 忽略ssl证书 String urlStr = null; try { - SslUtils.ignoreSsl(); urlStr = fileAttribute.getUrl().replaceAll("\\+", "%20").replaceAll(" ", "%20"); } catch (Exception e) { logger.error("忽略SSL证书异常:", e); @@ -90,17 +90,18 @@ public class DownloadUtils { if (!fileAttribute.getSkipDownLoad()) { if (isHttpUrl(url)) { File realFile = new File(realPath); - factory.setConnectionRequestTimeout(2000); //设置超时时间 - factory.setConnectTimeout(10000); - factory.setReadTimeout(72000); - HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new DefaultRedirectStrategy()).build(); - factory.setHttpClient(httpClient); //加入重定向方法 + CloseableHttpClient httpClient = SslUtils.createHttpClientIgnoreSsl(); + factory.setHttpClient(httpClient); restTemplate.setRequestFactory(factory); RequestCallback requestCallback = request -> { request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)); + WebUtils.applyBasicAuthHeaders(request.getHeaders(), fileAttribute); String proxyAuthorization = fileAttribute.getKkProxyAuthorization(); if(StringUtils.hasText(proxyAuthorization)){ - Map proxyAuthorizationMap = mapper.readValue(proxyAuthorization, Map.class); + Map proxyAuthorizationMap = mapper.readValue( + proxyAuthorization, + TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String.class) + ); proxyAuthorizationMap.forEach((key, value) -> request.getHeaders().set(key, value)); } }; @@ -110,16 +111,19 @@ public class DownloadUtils { return null; }); } catch (Exception e) { - response.setCode(1); - response.setContent(null); - response.setMsg("下载失败:" + e); - return response; + response.setCode(1); + response.setContent(null); + response.setMsg("下载失败:" + e); + return response; } } else if (isFtpUrl(url)) { String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME); String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD); String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING); - FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding); + String ftpport = WebUtils.getUrlParameterReg(realPath, URL_PARAM_FTP_PORT); + FtpUtils.download(fileAttribute.getUrl(),ftpport, realPath, ftpUsername, ftpPassword, ftpControlEncoding); + } else if (isFileUrl(url)) { // 添加对file协议的支持 + handleFileProtocol(url, realPath); } else { response.setCode(1); response.setMsg("url不能识别url" + urlStr); @@ -138,10 +142,70 @@ public class DownloadUtils { response.setMsg(e.getMessage()); } return response; + } catch (Exception e) { + throw new RuntimeException(e); } } + // 处理file协议的文件下载 + private static void handleFileProtocol(URL url, String targetPath) throws IOException { + File sourceFile = new File(url.getPath()); + if (!sourceFile.exists()) { + throw new FileNotFoundException("本地文件不存在: " + url.getPath()); + } + if (!sourceFile.isFile()) { + throw new IOException("路径不是文件: " + url.getPath()); + } + + File targetFile = new File(targetPath); + + // 判断源文件和目标文件是否是同一个文件(防止自身复制覆盖) + if (isSameFile(sourceFile, targetFile)) { + // 如果是同一个文件,直接返回,不执行复制操作 + logger.info("源文件和目标文件相同,跳过复制: {}", sourceFile.getAbsolutePath()); + return; + } + + // 确保目标目录存在 + File parentDir = targetFile.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs(); + } + + // 复制文件 + Files.copy(sourceFile.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + /** + * 判断两个文件是否是同一个文件 + * 通过比较规范化路径来避免符号链接、相对路径等问题 + */ + private static boolean isSameFile(File file1, File file2) { + try { + // 使用规范化路径比较,可以处理符号链接、相对路径等情况 + String canonicalPath1 = file1.getCanonicalPath(); + String canonicalPath2 = file2.getCanonicalPath(); + + // 如果是Windows系统,忽略路径大小写 + if (isWindows()) { + return canonicalPath1.equalsIgnoreCase(canonicalPath2); + } + return canonicalPath1.equals(canonicalPath2); + } catch (IOException e) { + // 如果获取规范化路径失败,使用绝对路径比较 + logger.warn("无法获取文件的规范化路径,使用绝对路径比较: {}, {}", file1.getAbsolutePath(), file2.getAbsolutePath()); + + String absolutePath1 = file1.getAbsolutePath(); + String absolutePath2 = file2.getAbsolutePath(); + + if (isWindows()) { + return absolutePath1.equalsIgnoreCase(absolutePath2); + } + return absolutePath1.equals(absolutePath2); + } + } + /** * 获取真实文件绝对路径 * diff --git a/server/src/main/java/cn/keking/utils/FtpUtils.java b/server/src/main/java/cn/keking/utils/FtpUtils.java index 8a736fd4..abceb78d 100644 --- a/server/src/main/java/cn/keking/utils/FtpUtils.java +++ b/server/src/main/java/cn/keking/utils/FtpUtils.java @@ -3,57 +3,227 @@ package cn.keking.utils; import cn.keking.config.ConfigConstants; import org.apache.commons.lang3.StringUtils; import org.apache.commons.net.ftp.FTPClient; -import org.apache.commons.net.ftp.FTPReply; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.FilterInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Objects; /** * @auther: chenjh * @since: 2019/6/18 14:36 */ public class FtpUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(FtpUtils.class); - public static FTPClient connect(String host, int port, String username, String password, String controlEncoding) throws IOException { + /** + * 从FTP服务器下载文件到本地 + */ + public static void download(String ftpUrl, String ftpport, String localFilePath, + String ftpUsername, String ftpPassword, + String ftpControlEncoding) throws IOException { + // 获取FTP连接信息 + FtpConnectionInfo connectionInfo = parseFtpConnectionInfo(ftpUrl, ftpport, ftpUsername, ftpPassword, ftpControlEncoding); + + LOGGER.debug("FTP下载 - url:{}, host:{}, port:{}, username:{}, 保存路径:{}", + ftpUrl, connectionInfo.host, connectionInfo.port, connectionInfo.username, localFilePath); + + FTPClient ftpClient = connect(connectionInfo.host, connectionInfo.port, + connectionInfo.username, connectionInfo.password, + connectionInfo.controlEncoding); + + try { + // 设置被动模式 + ftpClient.enterLocalPassiveMode(); + + // 获取文件输入流 + String encodedFilePath = new String( + connectionInfo.remoteFilePath.getBytes(connectionInfo.controlEncoding), + StandardCharsets.ISO_8859_1 + ); + + // 方法1:直接下载文件到本地 + try (OutputStream outputStream = Files.newOutputStream(Paths.get(localFilePath))) { + boolean downloadResult = ftpClient.retrieveFile(encodedFilePath, outputStream); + LOGGER.debug("FTP下载结果: {}", downloadResult); + if (!downloadResult) { + throw new IOException("FTP文件下载失败,返回码: " + ftpClient.getReplyCode()); + } + } + } finally { + closeFtpClient(ftpClient); + } + } + + /** + * 预览FTP文件 - 返回输入流(调用者需要关闭流) + */ + public static InputStream preview(String ftpUrl, String ftpport, String localFilePath, + String ftpUsername, String ftpPassword, + String ftpControlEncoding) throws IOException { + // 获取FTP连接信息 + FtpConnectionInfo connectionInfo = parseFtpConnectionInfo(ftpUrl, ftpport, ftpUsername, ftpPassword, ftpControlEncoding); + + LOGGER.debug("FTP预览 - url:{}, host:{}, port:{}, username:{}", + ftpUrl, connectionInfo.host, connectionInfo.port, connectionInfo.username); + + FTPClient ftpClient = connect(connectionInfo.host, connectionInfo.port, + connectionInfo.username, connectionInfo.password, + connectionInfo.controlEncoding); + + try { + // 设置被动模式 + ftpClient.enterLocalPassiveMode(); + + // 获取文件输入流 + String encodedFilePath = new String( + connectionInfo.remoteFilePath.getBytes(connectionInfo.controlEncoding), + StandardCharsets.ISO_8859_1 + ); + + // 获取文件输入流 + InputStream inputStream = ftpClient.retrieveFileStream(encodedFilePath); + + if (inputStream == null) { + closeFtpClient(ftpClient); + throw new IOException("无法获取FTP文件流,可能文件不存在或无权限"); + } + + // 包装输入流,在流关闭时自动断开FTP连接 + return new FtpAutoCloseInputStream(inputStream, ftpClient); + + } catch (IOException e) { + // 发生异常时确保关闭连接 + closeFtpClient(ftpClient); + throw e; + } + } + + /** + * 解析FTP连接信息(抽取公共逻辑) + */ + private static FtpConnectionInfo parseFtpConnectionInfo(String ftpUrl, String ftpport, + String ftpUsername, String ftpPassword, + String ftpControlEncoding) throws IOException { + FtpConnectionInfo info = new FtpConnectionInfo(); + + // 从配置获取默认连接参数 + String basic = ConfigConstants.getFtpUsername(); + if (!StringUtils.isEmpty(basic) && !Objects.equals(basic, "false")) { + String[] params = WebUtils.namePass(ftpUrl, basic); + if (params != null && params.length >= 5) { + info.port = Integer.parseInt(params[1]); + info.username = params[2]; + info.password = params[3]; + info.controlEncoding = params[4]; + } + } + + // 使用传入参数覆盖默认值 + if (!StringUtils.isEmpty(ftpport)) { + info.port = Integer.parseInt(ftpport); + } + if (!StringUtils.isEmpty(ftpUsername)) { + info.username = ftpUsername; + } + if (!StringUtils.isEmpty(ftpPassword)) { + info.password = ftpPassword; + } + if (!StringUtils.isEmpty(ftpControlEncoding)) { + info.controlEncoding = ftpControlEncoding; + } + + // 设置默认值 + if (info.port == 0) { + info.port = 21; + } + if (StringUtils.isEmpty(info.controlEncoding)) { + info.controlEncoding = "UTF-8"; + } + // 解析URL + try { + URI uri = new URI(ftpUrl); + info.host = uri.getHost(); + info.remoteFilePath = uri.getPath(); + } catch (URISyntaxException e) { + throw new IOException("无效的FTP URL: " + ftpUrl, e); + } + return info; + } + + /** + * FTP连接信息对象 + */ + private static class FtpConnectionInfo { + String host; + int port = 21; + String username; + String password; + String controlEncoding = "UTF-8"; + String remoteFilePath; + } + + /** + * 自动关闭FTP连接的输入流包装类 + */ + private static class FtpAutoCloseInputStream extends FilterInputStream { + private final FTPClient ftpClient; + + protected FtpAutoCloseInputStream(InputStream in, FTPClient ftpClient) { + super(in); + this.ftpClient = ftpClient; + } + + @Override + public void close() throws IOException { + try { + super.close(); + // 确保FTP命令完成 + if (ftpClient != null) { + ftpClient.completePendingCommand(); + } + } finally { + closeFtpClient(ftpClient); + } + } + } + + /** + * 安全关闭FTP连接 + */ + private static void closeFtpClient(FTPClient ftpClient) { + if (ftpClient != null && ftpClient.isConnected()) { + try { + ftpClient.logout(); + ftpClient.disconnect(); + } catch (IOException e) { + LOGGER.warn("关闭FTP连接时发生异常", e); + } + } + } + + /** + * 连接FTP服务器 + */ + private static FTPClient connect(String host, int port, String username, + String password, String controlEncoding) throws IOException { FTPClient ftpClient = new FTPClient(); - ftpClient.connect(host, port); - if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) { - ftpClient.login(username, password); - } - int reply = ftpClient.getReplyCode(); - if (!FTPReply.isPositiveCompletion(reply)) { - ftpClient.disconnect(); - } ftpClient.setControlEncoding(controlEncoding); + ftpClient.connect(host, port); + + if (!ftpClient.login(username, password)) { + throw new IOException("FTP登录失败,用户名或密码错误"); + } + ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); return ftpClient; } - - public static void download(String ftpUrl, String localFilePath, String ftpUsername, String ftpPassword, String ftpControlEncoding) throws IOException { - String username = StringUtils.isEmpty(ftpUsername) ? ConfigConstants.getFtpUsername() : ftpUsername; - String password = StringUtils.isEmpty(ftpPassword) ? ConfigConstants.getFtpPassword() : ftpPassword; - String controlEncoding = StringUtils.isEmpty(ftpControlEncoding) ? ConfigConstants.getFtpControlEncoding() : ftpControlEncoding; - URL url = new URL(ftpUrl); - String host = url.getHost(); - int port = (url.getPort() == -1) ? url.getDefaultPort() : url.getPort(); - String remoteFilePath = url.getPath(); - LOGGER.debug("FTP connection url:{}, username:{}, password:{}, controlEncoding:{}, localFilePath:{}", ftpUrl, username, password, controlEncoding, localFilePath); - FTPClient ftpClient = connect(host, port, username, password, controlEncoding); - OutputStream outputStream = Files.newOutputStream(Paths.get(localFilePath)); - ftpClient.enterLocalPassiveMode(); - boolean downloadResult = ftpClient.retrieveFile(new String(remoteFilePath.getBytes(controlEncoding), StandardCharsets.ISO_8859_1), outputStream); - LOGGER.debug("FTP download result {}", downloadResult); - outputStream.flush(); - outputStream.close(); - ftpClient.logout(); - ftpClient.disconnect(); - } } diff --git a/server/src/main/java/cn/keking/utils/KkFileUtils.java b/server/src/main/java/cn/keking/utils/KkFileUtils.java index 48ae8928..3a5ae259 100644 --- a/server/src/main/java/cn/keking/utils/KkFileUtils.java +++ b/server/src/main/java/cn/keking/utils/KkFileUtils.java @@ -9,9 +9,8 @@ import org.springframework.web.util.HtmlUtils; import java.io.File; import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; +import java.util.regex.Pattern; public class KkFileUtils { @@ -19,17 +18,31 @@ public class KkFileUtils { public static final String DEFAULT_FILE_ENCODING = "UTF-8"; - private static final List illegalFileStrList = new ArrayList<>(); + // 路径遍历关键字列表 + private static final Set illegalFileStrList; static { - illegalFileStrList.add("../"); - illegalFileStrList.add("./"); - illegalFileStrList.add("..\\"); - illegalFileStrList.add(".\\"); - illegalFileStrList.add("\\.."); - illegalFileStrList.add("\\."); - illegalFileStrList.add(".."); - illegalFileStrList.add("..."); + Set set = new HashSet<>(); + + // 基本路径遍历 + Collections.addAll(set, "../", "./", "..\\", ".\\", "\\..", "\\.", "..", "...", "....", "....."); + + // URL编码的路径遍历 + Collections.addAll(set, "%2e%2e%2f", "%2e%2e/", "..%2f", "%2e%2e%5c", "%2e%2e\\", "..%5c", + "%252e%252e%252f", "%252e%252e/", "..%252f"); + + // Unicode编码绕过 + Collections.addAll(set, "\\u002e\\u002e\\u002f", "\\U002e\\U002e\\U002f", + "\u00c0\u00ae\u00c0\u00ae", "\u00c1\u009c\u00c1\u009c"); + + // 特殊分隔符 + Collections.addAll(set, "|..|", "|../|", "|..\\|"); + + // Windows特殊路径 + Collections.addAll(set, "\\\\?\\", "\\\\.\\"); + + // 转换为不可变集合 + illegalFileStrList = Collections.unmodifiableSet(set); } /** @@ -68,7 +81,22 @@ public class KkFileUtils { * @return 是否http */ public static boolean isHttpUrl(URL url) { - return url.getProtocol().toLowerCase().startsWith("file") || url.getProtocol().toLowerCase().startsWith("http"); + return url.getProtocol().toLowerCase().startsWith("http") || url.getProtocol().toLowerCase().startsWith("https"); + } + + /** + * 判断url是否是file资源 + * + */ + public static boolean isFileUrl(URL url) { + return url.getProtocol().toLowerCase().startsWith("file"); + } + + /** + * 判断当前操作系统是否为Windows + */ + static boolean isWindows() { + return System.getProperty("os.name").toLowerCase().contains("windows"); } /** diff --git a/server/src/main/java/cn/keking/utils/SslUtils.java b/server/src/main/java/cn/keking/utils/SslUtils.java index d42b88ab..f28dad6e 100644 --- a/server/src/main/java/cn/keking/utils/SslUtils.java +++ b/server/src/main/java/cn/keking/utils/SslUtils.java @@ -1,42 +1,90 @@ package cn.keking.utils; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; +import org.apache.hc.core5.http.io.SocketConfig; +import org.apache.hc.core5.util.Timeout; + import javax.net.ssl.*; -import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** - * @author 鞠玉果 + * @author 高雄 */ public class SslUtils { - private static void trustAllHttpsCertificates() throws Exception { - TrustManager[] trustAllCerts = new TrustManager[1]; - TrustManager tm = new miTM(); - trustAllCerts[0] = tm; - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, trustAllCerts, null); - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - } + /** + * 创建忽略SSL验证的HttpClient(适用于HttpClient 5.6) + */ + public static CloseableHttpClient createHttpClientIgnoreSsl() throws Exception { + // 创建自定义的SSL上下文 + SSLContext sslContext = createIgnoreVerifySSL(); - static class miTM implements TrustManager, X509TrustManager { - public X509Certificate[] getAcceptedIssuers() { - return null; - } + // 使用SSLConnectionSocketFactoryBuilder构建SSL连接工厂 + DefaultClientTlsStrategy tlsStrategy = new DefaultClientTlsStrategy( + sslContext, NoopHostnameVerifier.INSTANCE); - public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { - } + // 使用新的PoolingHttpClientConnectionManagerBuilder构建连接管理器 + // 使用连接管理器构建器 + PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() + .setTlsSocketStrategy(tlsStrategy) + .setDefaultSocketConfig(SocketConfig.custom() + .setSoTimeout(Timeout.ofSeconds(10)) + .build()) + .build(); - public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { - } + // 配置连接池参数 + connectionManager.setMaxTotal(200); + connectionManager.setDefaultMaxPerRoute(20); + + // 配置请求参数 + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(Timeout.ofSeconds(10)) + .setResponseTimeout(Timeout.ofSeconds(72)) + .setConnectionRequestTimeout(Timeout.ofSeconds(2)) + .setRedirectsEnabled(true) + .setMaxRedirects(5) + .build(); + + return HttpClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig) + .setRedirectStrategy(DefaultRedirectStrategy.INSTANCE) + .build(); } /** - * 忽略HTTPS请求的SSL证书,必须在openConnection之前调用 + * 创建忽略SSL验证的SSLContext */ - public static void ignoreSsl() throws Exception { - HostnameVerifier hv = (urlHostName, session) -> true; - trustAllHttpsCertificates(); - HttpsURLConnection.setDefaultHostnameVerifier(hv); - } + private static SSLContext createIgnoreVerifySSL() throws Exception { + // 使用TLSv1.2或TLSv1.3 + SSLContext sc = SSLContext.getInstance("TLSv1.2"); -} + // 实现一个X509TrustManager,忽略所有证书验证 + X509TrustManager trustManager = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + // 信任所有客户端证书 + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + // 信任所有服务器证书 + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + }; + + sc.init(null, new TrustManager[]{trustManager}, new java.security.SecureRandom()); + return sc; + } +} \ No newline at end of file diff --git a/server/src/main/java/cn/keking/utils/WebUtils.java b/server/src/main/java/cn/keking/utils/WebUtils.java index f62862cf..bb6e3b69 100644 --- a/server/src/main/java/cn/keking/utils/WebUtils.java +++ b/server/src/main/java/cn/keking/utils/WebUtils.java @@ -1,10 +1,14 @@ package cn.keking.utils; +import cn.keking.config.ConfigConstants; +import cn.keking.model.FileAttribute; import io.mola.galimatias.GalimatiasParseException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.util.ObjectUtils; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.util.HtmlUtils; @@ -17,8 +21,7 @@ import java.net.URL; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,6 +33,18 @@ public class WebUtils { private static final Logger LOGGER = LoggerFactory.getLogger(WebUtils.class); private static final String BASE64_MSG = "base64"; + private static final String URL_PARAM_BASIC_NAME = "basic.name"; + private static final String URL_PARAM_BASIC_PASS = "basic.pass"; + private static final Map ERROR_MESSAGES = Map.of( + "base64", "KK提醒您:接入方法错误未使用BASE64", + "base641", "KK提醒您:BASE64解码异常,确认是否正确使用BASE64编码", + "Keyerror", "KK提醒您:AES解码错误,请检测你的秘钥是否正确", + "base64error", "KK提醒您:你选用的是ASE加密,实际用了BASE64加密接入", + "byteerror", "KK提醒您:解码异常,检测你接入方法是否正确" + ); + + private static final String EMPTY_URL_MSG = "KK提醒您:地址不能为空"; + private static final String INVALID_URL_MSG = "KK提醒您:请正确使用URL(必须包括https ftp file 协议)"; /** * 获取标准的URL * @@ -234,17 +249,18 @@ public class WebUtils { String urls = request.getParameter("urls"); String currentUrl = request.getParameter("currentUrl"); String urlPath = request.getParameter("urlPath"); + String encryption = request.getParameter("encryption"); if (StringUtils.isNotBlank(url)) { - return decodeUrl(url); + return decodeUrl(url,encryption); } if (StringUtils.isNotBlank(currentUrl)) { - return decodeUrl(currentUrl); + return decodeUrl(currentUrl,encryption); } if (StringUtils.isNotBlank(urlPath)) { - return decodeUrl(urlPath); + return decodeUrl(urlPath,encryption); } if (StringUtils.isNotBlank(urls)) { - urls = decodeUrl(urls); + urls = decodeUrl(urls,encryption); String[] images = urls.split("\\|"); return images[0]; } @@ -268,13 +284,20 @@ public class WebUtils { * * aHR0cHM6Ly9maWxlLmtla2luZy5jbi9kZW1vL%2BS4reaWhy5wcHR4 -> https://file.keking.cn/demo/%E4%B8%AD%E6%96%87.pptx -> https://file.keking.cn/demo/中文.pptx */ - public static String decodeUrl(String source) { - String url = decodeBase64String(source, StandardCharsets.UTF_8); - if (! StringUtils.isNotBlank(url)){ - return null; + public static String decodeUrl(String source,String encryption) { + String url; + if(ObjectUtils.isEmpty(encryption) || Objects.equals(ConfigConstants.getaesKey(), "false")){ + encryption = "base64"; + } + if(Objects.equals(encryption.toLowerCase(), "aes")){ + return AESUtil.AesDecrypt(source); + }else { + url = decodeBase64String(source, StandardCharsets.UTF_8); + if(!isValidUrl(url)){ + url="base641"; + } + return url; } - - return url; } /** @@ -301,6 +324,30 @@ public class WebUtils { } } + public static String urlSecurity(String url) { + + if (ObjectUtils.isEmpty(url)) { + return EMPTY_URL_MSG; + } + // 检查已知的错误类型 + String errorMsg = ERROR_MESSAGES.get(url); + if (errorMsg != null) { + return errorMsg; + } + // 验证URL格式 + if (!isValidUrl(url)) { + return INVALID_URL_MSG; + } + // file协议特殊处理 + if (url.toLowerCase().startsWith("file://")) { + // 对于本地文件,可以返回URL本身或进行特殊处理 + // 根据业务需求决定:返回URL、返回特殊标识或进行本地文件安全检查 + return url; // 或者返回特殊标识如 "file-protocol" + } + // 提取主机名 + return getHost(url); + } + /** * 获取 url 的 host * @param urlStr url @@ -371,4 +418,93 @@ public class WebUtils { } session.removeAttribute(key); } + + public static boolean validateKey(String key) { + String configKey = ConfigConstants.getKey(); + return !"false".equals(configKey) && !configKey.equals(key); + } + + public static String getContentTypeByFilename(String filename) { + String extension = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(); + switch (extension) { + case "pdf": return "application/pdf"; + case "jpg": case "jpeg": return "image/jpeg"; + case "png": return "image/png"; + case "gif": return "image/gif"; + case "svg": return "image/svg+xml"; + case "txt": return "text/plain"; + case "html": case "htm": return "text/html"; + case "xml": return "application/xml"; + case "json": return "application/json"; + default: return null; + } + } + /** + * name pass 获取用户名 和密码 + */ + public static String[] namePass(String url,String name) { + url= getHost(url); + String[] items = name.split(",\\s*"); + String toRemove = ":"; + String names = null; + String[] parts = null; + try { + for (String item : items) { + int index = item.indexOf(toRemove); + if (index != -1) { + String result = item.substring(0, index); + if (Objects.equals(result, url)) { + names = item; + } + } + } + if (names !=null){ + parts = names.split(toRemove); + } + } catch (Exception e) { + LOGGER.error("获取认证权限错误:",e); + } + return parts; + } + + /** + * 支持basic 下载方法 + */ + public static void applyBasicAuthHeaders(HttpHeaders headers, FileAttribute fileAttribute) { + String url = fileAttribute.getUrl(); + // 从配置文件读取User-Agent,如果没有配置则使用默认值 + String customUserAgent=ConfigConstants.getUserAgent(); + String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"; + if (!StringUtils.isEmpty(customUserAgent) && !Objects.equals(customUserAgent, "false")) { + userAgent = customUserAgent; + } + headers.set("User-Agent", userAgent); + // 获取用户名和密码 + String username = null; + String password = null; + // 从basic配置获取 + String basic = ConfigConstants.getBasicName(); + if (!StringUtils.isEmpty(basic) && !Objects.equals(basic, "false")) { + String[] urlUser = namePass(url, basic); + if (urlUser != null && urlUser.length >= 3) { + username = urlUser[1]; + password = urlUser[2]; + } + } + // URL参数优先 + String basicUsername = getUrlParameterReg(url, URL_PARAM_BASIC_NAME); + String basicPassword = getUrlParameterReg(url, URL_PARAM_BASIC_PASS); + + if (!StringUtils.isEmpty(basicUsername)) { + username = basicUsername; + password = basicPassword; + } + + // 设置Basic Auth + if (!StringUtils.isEmpty(username)) { + String plainCredentials = username + ":" + (password != null ? password : ""); + String base64Credentials = java.util.Base64.getEncoder().encodeToString(plainCredentials.getBytes()); + headers.set("Authorization", "Basic " + base64Credentials); + } + } } diff --git a/server/src/main/java/cn/keking/web/controller/FileController.java b/server/src/main/java/cn/keking/web/controller/FileController.java index 609d35ea..3833032c 100644 --- a/server/src/main/java/cn/keking/web/controller/FileController.java +++ b/server/src/main/java/cn/keking/web/controller/FileController.java @@ -179,7 +179,7 @@ public class FileController { return ReturnResponse.failure("文件名为空,删除失败!"); } try { - fileName = WebUtils.decodeUrl(fileName); + fileName = WebUtils.decodeUrl(fileName,"base64"); } catch (Exception ex) { String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, fileName); return ReturnResponse.failure(errorMsg + "删除失败!"); @@ -208,7 +208,7 @@ public class FileController { public Object directory(String urls) { String fileUrl; try { - fileUrl = WebUtils.decodeUrl(urls); + fileUrl = WebUtils.decodeUrl(urls,"base64"); } catch (Exception ex) { String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url"); return ReturnResponse.failure(errorMsg); diff --git a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java index 2161e23a..ea16de7f 100644 --- a/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java +++ b/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java @@ -1,18 +1,24 @@ package cn.keking.web.controller; +import cn.keking.config.ConfigConstants; import cn.keking.model.FileAttribute; import cn.keking.service.FileHandlerService; import cn.keking.service.FilePreview; import cn.keking.service.FilePreviewFactory; import cn.keking.service.cache.CacheService; import cn.keking.service.impl.OtherFilePreviewImpl; +import cn.keking.utils.FtpUtils; import cn.keking.utils.KkFileUtils; +import cn.keking.utils.SslUtils; import cn.keking.utils.WebUtils; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; import fr.opensagres.xdocreport.core.io.IOUtils; import org.apache.commons.codec.binary.Base64; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +30,7 @@ import org.springframework.ui.Model; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.RestTemplate; @@ -38,6 +45,8 @@ import java.util.List; import java.util.Map; import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE; +import static cn.keking.utils.KkFileUtils.isFtpUrl; +import static cn.keking.utils.KkFileUtils.isHttpUrl; /** * @author yudian-it @@ -45,8 +54,14 @@ import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE; @Controller public class OnlinePreviewController { - public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败,请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!"; private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class); + public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败,请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!"; + private static final String ILLEGAL_ACCESS_MSG = "访问不合法:访问密码不正确"; + private static final String INTERFACE_CLOSED_MSG = "接口关闭,禁止访问!"; + private static final String URL_PARAM_FTP_USERNAME = "ftp.username"; + private static final String URL_PARAM_FTP_PASSWORD = "ftp.password"; + private static final String URL_PARAM_FTP_CONTROL_ENCODING = "ftp.control.encoding"; + private static final String URL_PARAM_FTP_PORT = "ftp.control.port"; private final FilePreviewFactory previewFactory; private final CacheService cacheService; @@ -64,11 +79,18 @@ public class OnlinePreviewController { } @GetMapping( "/onlinePreview") - public String onlinePreview(String url, Model model, HttpServletRequest req) { - + public String onlinePreview(@RequestParam String url, + @RequestParam(required = false) String key, + @RequestParam(required = false) String encryption, + Model model, + HttpServletRequest req) { + // 验证访问权限 + if (WebUtils.validateKey(key)) { + return otherFilePreview.notSupportedFile(model, ILLEGAL_ACCESS_MSG); + } String fileUrl; try { - fileUrl = WebUtils.decodeUrl(url); + fileUrl = WebUtils.decodeUrl(url, encryption); } catch (Exception ex) { String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url"); return otherFilePreview.notSupportedFile(model, errorMsg); @@ -85,10 +107,22 @@ public class OnlinePreviewController { } @GetMapping( "/picturesPreview") - public String picturesPreview(String urls, Model model, HttpServletRequest req) { + public String picturesPreview(@RequestParam String urls, + @RequestParam(required = false) String key, + @RequestParam(required = false) String encryption, + Model model, + HttpServletRequest req) { + // 1. 验证接口是否开启 + if (!ConfigConstants.getPicturesPreview()) { + return otherFilePreview.notSupportedFile(model, INTERFACE_CLOSED_MSG); + } + //2. 验证访问权限 + if (WebUtils.validateKey(key)) { + return otherFilePreview.notSupportedFile(model, ILLEGAL_ACCESS_MSG); + } String fileUrls; try { - fileUrls = WebUtils.decodeUrl(urls); + fileUrls = WebUtils.decodeUrl(urls, encryption); // 防止XSS攻击 fileUrls = KkFileUtils.htmlEscape(fileUrls); } catch (Exception ex) { @@ -103,7 +137,7 @@ public class OnlinePreviewController { String currentUrl = req.getParameter("currentUrl"); if (StringUtils.hasText(currentUrl)) { String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl)); - decodedCurrentUrl = KkFileUtils.htmlEscape(decodedCurrentUrl); // 防止XSS攻击 + decodedCurrentUrl = KkFileUtils.htmlEscape(decodedCurrentUrl); // 防止XSS攻击 model.addAttribute("currentUrl", decodedCurrentUrl); } else { model.addAttribute("currentUrl", imgUrls.get(0)); @@ -119,35 +153,50 @@ public class OnlinePreviewController { * @param response response */ @GetMapping("/getCorsFile") - public void getCorsFile(String urlPath, HttpServletResponse response,FileAttribute fileAttribute) throws IOException { + public void getCorsFile(@RequestParam String urlPath, + @RequestParam(required = false) String key, + HttpServletResponse response, + FileAttribute fileAttribute) throws Exception { + + // 1. 验证接口是否开启 + if (!ConfigConstants.getGetCorsFile()) { + logger.info("接口关闭,禁止访问!,url:{}", urlPath); + return; + } + //2. 验证访问权限 + if (WebUtils.validateKey(key)) { + logger.info("访问不合法:访问密码不正确!,url:{}", urlPath); + return; + } URL url; try { - urlPath = WebUtils.decodeUrl(urlPath); + urlPath = WebUtils.decodeUrl(urlPath, "base64"); url = WebUtils.normalizedURL(urlPath); } catch (Exception ex) { logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath),ex); return; } assert urlPath != null; - if (!urlPath.toLowerCase().startsWith("http") && !urlPath.toLowerCase().startsWith("https") && !urlPath.toLowerCase().startsWith("ftp")) { + if (!isHttpUrl(url) && !isFtpUrl(url)) { logger.info("读取跨域文件异常,可能存在非法访问,urlPath:{}", urlPath); return; } InputStream inputStream = null; logger.info("读取跨域pdf文件url:{}", urlPath); - if (!urlPath.toLowerCase().startsWith("ftp:")) { - factory.setConnectionRequestTimeout(2000); - factory.setConnectTimeout(10000); - factory.setReadTimeout(72000); - HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new DefaultRedirectStrategy()).build(); + if (!isFtpUrl(url)) { + CloseableHttpClient httpClient = SslUtils.createHttpClientIgnoreSsl(); factory.setHttpClient(httpClient); + // restTemplate.setRequestFactory(factory); restTemplate.setRequestFactory(factory); RequestCallback requestCallback = request -> { request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)); String proxyAuthorization = fileAttribute.getKkProxyAuthorization(); if(StringUtils.hasText(proxyAuthorization)){ - Map proxyAuthorizationMap = mapper.readValue(proxyAuthorization, Map.class); - proxyAuthorizationMap.forEach((key, value) -> request.getHeaders().set(key, value)); + Map proxyAuthorizationMap = mapper.readValue( + proxyAuthorization, + TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String.class) + ); + proxyAuthorizationMap.forEach((headerKey, value) -> request.getHeaders().set(headerKey, value)); } }; try { @@ -160,10 +209,16 @@ public class OnlinePreviewController { } }else{ try { - if(urlPath.contains(".svg")) { - response.setContentType("image/svg+xml"); + String filename = urlPath.substring(urlPath.lastIndexOf('/') + 1); + String contentType = WebUtils.getContentTypeByFilename(filename); + if (contentType != null) { + response.setContentType(contentType); } - inputStream = (url).openStream(); + String ftpUsername = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_USERNAME); + String ftpPassword = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_PASSWORD); + String ftpControlEncoding = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_CONTROL_ENCODING); + String support = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_PORT); + inputStream= FtpUtils.preview(urlPath,support, urlPath, ftpUsername, ftpPassword, ftpControlEncoding); IOUtils.copy(inputStream, response.getOutputStream()); } catch (IOException e) { logger.error("读取跨域文件异常,url:{}", urlPath); @@ -180,9 +235,32 @@ public class OnlinePreviewController { */ @GetMapping("/addTask") @ResponseBody - public String addQueueTask(String url) { - logger.info("添加转码队列url:{}", url); - cacheService.addQueueTask(url); + public String addQueueTask(@RequestParam String url, + @RequestParam(required = false) String key, + @RequestParam(required = false) String encryption) { + // 1. 验证接口是否开启 + if (!ConfigConstants.getAddTask()) { + String errorMsg = "接口关闭,禁止访问!"; + logger.info("{},url:{}", errorMsg, url); + return errorMsg; + } + String fileUrls; + try { + fileUrls = WebUtils.decodeUrl(url, encryption); + } catch (Exception ex) { + String errorMsg = "Url解析错误"; + logger.info("{},url:{}", errorMsg, url); + return errorMsg; + } + + //2. 验证访问权限 + if (WebUtils.validateKey(key)) { + String errorMsg = "访问不合法:访问密码不正确!"; + logger.info("{},url:{}", errorMsg, fileUrls); + return errorMsg; + } + logger.info("添加转码队列url:{}", fileUrls); + cacheService.addQueueTask(fileUrls); return "success"; } } diff --git a/server/src/main/java/cn/keking/web/filter/TrustDirFilter.java b/server/src/main/java/cn/keking/web/filter/TrustDirFilter.java index c71a21ec..f71e9fd3 100644 --- a/server/src/main/java/cn/keking/web/filter/TrustDirFilter.java +++ b/server/src/main/java/cn/keking/web/filter/TrustDirFilter.java @@ -1,6 +1,8 @@ package cn.keking.web.filter; import cn.keking.config.ConfigConstants; +import cn.keking.model.ReturnResponse; +import cn.keking.utils.KkFileUtils; import cn.keking.utils.WebUtils; import io.mola.galimatias.GalimatiasParseException; import org.jodconverter.core.util.OSUtils; @@ -11,6 +13,8 @@ import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; import jakarta.servlet.*; + +import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLDecoder; @@ -56,18 +60,42 @@ public class TrustDirFilter implements Filter { } private boolean allowPreview(String urlPath) { - //判断URL是否合法 - if(!StringUtils.hasText(urlPath) || !WebUtils.isValidUrl(urlPath)) { - return false ; + // 判断URL是否合法 + if (KkFileUtils.isIllegalFileName(urlPath) || !StringUtils.hasText(urlPath) || !WebUtils.isValidUrl(urlPath)) { + return false; } 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 (OSUtils.IS_OS_WINDOWS) { - filePath = filePath.replaceAll("/", "\\\\"); + // 将文件路径转换为File对象 + File targetFile = new File(filePath); + // 将配置目录也转换为File对象 + File fileDir = new File(ConfigConstants.getFileDir()); + File localPreviewDir = new File(ConfigConstants.getLocalPreviewDir()); + try { + // 获取规范路径(系统会自动处理大小写、符号链接、相对路径等) + String canonicalFilePath = targetFile.getCanonicalPath(); + String canonicalFileDir = fileDir.getCanonicalPath(); + String canonicalLocalPreviewDir = localPreviewDir.getCanonicalPath(); + // 检查文件是否在配置目录下 + return isSubDirectory(canonicalFileDir, canonicalFilePath) || isSubDirectory(canonicalLocalPreviewDir, canonicalFilePath); + } catch (IOException e) { + logger.warn("获取规范路径失败,使用原始路径比较", e); + // 如果获取规范路径失败,回退到原始路径比较 + String absFilePath = targetFile.getAbsolutePath(); + String absFileDir = fileDir.getAbsolutePath(); + String absLocalPreviewDir = localPreviewDir.getAbsolutePath(); + // 统一路径分隔符 + absFilePath = absFilePath.replace('\\', '/'); + absFileDir = absFileDir.replace('\\', '/'); + absLocalPreviewDir = absLocalPreviewDir.replace('\\', '/'); + // 确保目录以斜杠结尾 + if (!absFileDir.endsWith("/")) absFileDir += "/"; + if (!absLocalPreviewDir.endsWith("/")) absLocalPreviewDir += "/"; + return absFilePath.startsWith(absFileDir) || absFilePath.startsWith(absLocalPreviewDir); } - return filePath.startsWith(ConfigConstants.getFileDir()) || filePath.startsWith(ConfigConstants.getLocalPreviewDir()); } return true; } catch (IOException | GalimatiasParseException e) { @@ -75,4 +103,26 @@ public class TrustDirFilter implements Filter { return false; } } + + /** + * 检查子路径是否在父路径下(跨平台) + */ + private boolean isSubDirectory(String parentDir, String childPath) { + try { + File parent = new File(parentDir); + File child = new File(childPath); + // 获取规范路径 + String canonicalParent = parent.getCanonicalPath(); + String canonicalChild = child.getCanonicalPath(); + // 确保父目录以路径分隔符结尾 + if (!canonicalParent.endsWith(File.separator)) { + canonicalParent += File.separator; + } + // 比较路径 + return canonicalChild.startsWith(canonicalParent); + } catch (IOException e) { + logger.warn("检查子路径失败", e); + return false; + } + } } diff --git a/server/src/main/java/cn/keking/web/filter/TrustHostFilter.java b/server/src/main/java/cn/keking/web/filter/TrustHostFilter.java index e661844f..9fdc551c 100644 --- a/server/src/main/java/cn/keking/web/filter/TrustHostFilter.java +++ b/server/src/main/java/cn/keking/web/filter/TrustHostFilter.java @@ -42,9 +42,9 @@ public class TrustHostFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String url = WebUtils.getSourceUrl(request); - String host = WebUtils.getHost(url); + String host = WebUtils.urlSecurity(url); //启用地址检查 assert host != null; - if (isNotTrustHost(host)) { + if (isNotTrustHost(host)||!WebUtils.isValidUrl(url)) { String html = this.notTrustHostHtmlView.replace("${current_host}", host); response.getWriter().write(html); response.getWriter().close(); @@ -58,7 +58,6 @@ public class TrustHostFilter implements Filter { if (CollectionUtils.isNotEmpty(ConfigConstants.getNotTrustHostSet())) { return ConfigConstants.getNotTrustHostSet().contains(host); } - // 如果配置了白名单,检查是否在白名单中 if (CollectionUtils.isNotEmpty(ConfigConstants.getTrustHostSet())) { // 支持通配符 * 表示允许所有主机 diff --git a/server/src/main/resources/web/main/record.ftl b/server/src/main/resources/web/main/record.ftl index 3fdde487..aecd15ef 100644 --- a/server/src/main/resources/web/main/record.ftl +++ b/server/src/main/resources/web/main/record.ftl @@ -33,6 +33,36 @@ +
+
+

2025年12月25日,v5.0版本

+
+
+
+

优化

+ 1. 优化 OFD 移动端预览 页面不自适应
+ 2. 更新 xlsx 前端解析组件,加速解析速度
+ 3. 升级 CAD 组件
+ 4. office 功能调整,支持批注、转换页码限制、生成水印等功能
+ 5. 升级 markdown 组件
+ 6. 升级 dcm 解析组件
+ 7. 升级 PDF.JS 解析组件
+ 8. 更换视频播放插件为 ckplayer
+ 9. tif 解析更加智能化,支持被修改的图片格式
+ 10. 针对大小文本文件检测字符编码的正确率,处理并发隐患
+ 11. 重构下载文件的代码,新增通用的文件服务器认证访问的设计
+ 12. 更新 bootstrap 组件,并精简掉不需要的文件
+ 13. 更新 epub 版本,优化 epub 显示效果
+ 14. 解决定时清除缓存时,对于多媒体类型文件,只删除了磁盘缓存文件
+ 15. 自动检测已安装 Office 组件,增加 LibreOffice 7.5 & 7.6 版本默认路径
+ 16. 修改 drawio 默认为预览模式
+ 17. 新增 PDF 线程管理、超时管理、内存缓存管理,更新 PDF 解析组件版本
+ 18. 优化 Dockerfile,支持真正的跨平台构建镜像
+
+
+
+
+

2025年01月16日,v4.4.0版本