diff --git a/backend/src/main/java/io/metersphere/controller/ResourceController.java b/backend/src/main/java/io/metersphere/controller/ResourceController.java index 07535d1834..bc2eec6040 100644 --- a/backend/src/main/java/io/metersphere/controller/ResourceController.java +++ b/backend/src/main/java/io/metersphere/controller/ResourceController.java @@ -25,9 +25,11 @@ public class ResourceController { return resourceService.getMdImage(fileName); } - @GetMapping(value = "/md/get/url") - public ResponseEntity getFileByUrl(@RequestParam ("url") String url, @RequestParam (value = "platform", required = false) String platform) { - return resourceService.getMdImageByUrl(url, platform); + @GetMapping(value = "/md/get/path") + public ResponseEntity getFileByPath(@RequestParam ("path") String path, + @RequestParam (value = "platform") String platform, + @RequestParam (value = "workspaceId") String workspaceId) { + return resourceService.getMdImageByPath(path, platform, workspaceId); } @GetMapping(value = "/ui/get") diff --git a/backend/src/main/java/io/metersphere/service/ResourceService.java b/backend/src/main/java/io/metersphere/service/ResourceService.java index a46bf8d79c..eff3357f3f 100644 --- a/backend/src/main/java/io/metersphere/service/ResourceService.java +++ b/backend/src/main/java/io/metersphere/service/ResourceService.java @@ -3,15 +3,12 @@ package io.metersphere.service; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.FileUtils; import io.metersphere.commons.utils.LogUtil; -import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.request.MdUploadRequest; import io.metersphere.i18n.Translator; import io.metersphere.track.issue.IssueFactory; import io.metersphere.track.request.testcase.IssuesRequest; -import org.apache.commons.lang3.StringUtils; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -99,21 +96,14 @@ public class ResourceService { * http 代理 * 如果当前访问地址是 https,直接访问 http 的图片资源 * 由于浏览器的安全机制,http 会被转成 https - * @param url + * @param path * @param platform * @return */ - public ResponseEntity getMdImageByUrl(String url, String platform) { - if (url.contains("md/get/url")) { - MSException.throwException(Translator.get("invalid_parameter")); - } - if (StringUtils.isNotBlank(platform)) { - IssuesRequest issuesRequest = new IssuesRequest(); - issuesRequest.setProjectId(SessionUtils.getCurrentProjectId()); - issuesRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); - return IssueFactory.createPlatform(platform, issuesRequest) - .proxyForGet(url, byte[].class); - } - return restTemplate.exchange(url, HttpMethod.GET, null, byte[].class); + public ResponseEntity getMdImageByPath(String path, String platform, String workspaceId) { + IssuesRequest issuesRequest = new IssuesRequest(); + issuesRequest.setWorkspaceId(workspaceId); + return IssueFactory.createPlatform(platform, issuesRequest) + .proxyForGet(path, byte[].class); } } diff --git a/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java b/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java index d1e0824086..7cb3e95c64 100644 --- a/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java @@ -38,6 +38,8 @@ import org.springframework.util.MultiValueMap; import java.io.File; import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.Function; import java.util.regex.Matcher; @@ -70,6 +72,8 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform { protected AttachmentService attachmentService; protected AttachmentModuleRelationMapper attachmentModuleRelationMapper; + public static final String PROXY_PATH = "/resource/md/get/path?platform=%s&workspaceId=%s&path=%s"; + public String getKey() { return key; } @@ -101,6 +105,10 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform { this.attachmentModuleRelationMapper = CommonBeanFactory.getBean(AttachmentModuleRelationMapper.class); } + protected String getProxyPath(String path) { + return String.format(PROXY_PATH, this.key, this.workspaceId, URLEncoder.encode(path, StandardCharsets.UTF_8)); + } + protected String getPlatformConfig(String platform) { IntegrationRequest request = new IntegrationRequest(); if (StringUtils.isBlank(workspaceId)) { @@ -355,7 +363,7 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform { while (matcher.find()) { try { String path = matcher.group(2); - if (!path.contains("/resource/md/get/url")) { + if (!path.contains("/resource/md/get/url") && !path.contains("/resource/md/get/path")) { if (path.contains("/resource/md/get/")) { // 兼容旧数据 String name = path.substring(path.indexOf("/resource/md/get/") + 17); files.add(new File(FileUtils.MD_IMAGE_DIR + "/" + name)); diff --git a/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java b/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java index b6c65ffb3f..97fa351666 100644 --- a/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java @@ -111,10 +111,10 @@ public interface IssuesPlatform { /** * Get请求的代理 - * @param url + * @param path * @return */ - ResponseEntity proxyForGet(String url, Class responseEntityClazz); + ResponseEntity proxyForGet(String path, Class responseEntityClazz); /** * 同步MS缺陷附件到第三方平台 diff --git a/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java b/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java index 524031f023..df986af548 100644 --- a/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java @@ -30,9 +30,9 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.client.HttpClientErrorException; import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -126,10 +126,13 @@ public class JiraPlatform extends AbstractIssuePlatform { // 解析标签内容 String name = getHyperLinkPathForImg("\\!\\[(.*?)\\]", StringEscapeUtils.unescapeJava(splitStr)); String path = getHyperLinkPathForImg("\\|(.*?)\\)", splitStr); - path = "/resource/md/get/url?platform=Jira&url=" + URLEncoder.encode(path, StandardCharsets.UTF_8); - - // 解析标签内容为图片超链接格式,进行替换 - description = description.replace(splitStr, "\n\n![" + name + "](" + path + ")"); + try { + path = getProxyPath(new URI(path).getPath()); + // 解析标签内容为图片超链接格式,进行替换 + description = description.replace(splitStr, "\n\n![" + name + "](" + path + ")"); + } catch (URISyntaxException e) { + LogUtil.error(e); + } } description = description.replace(splitStr, StringEscapeUtils.unescapeJava(splitStr.replace("MS附件:", ""))); } @@ -154,14 +157,17 @@ public class JiraPlatform extends AbstractIssuePlatform { JSONObject attachment = attachments.getJSONObject(i); String filename = attachment.getString("filename"); String content = attachment.getString("content"); - content = "/resource/md/get/url?platform=Jira&url=" + URLEncoder.encode(content, StandardCharsets.UTF_8); - - if (StringUtils.contains(attachment.getString("mimeType"), "image")) { - String contentUrl = "![" + filename + "](" + content + ")"; - fileContentMap.put(filename, contentUrl); - } else { - String contentUrl = "附件[" + filename + "]下载地址:" + content; - fileContentMap.put(filename, contentUrl); + try { + content = getProxyPath(new URI(content).getPath()); + if (StringUtils.contains(attachment.get("mimeType").toString(), "image")) { + String contentUrl = "![" + filename + "](" + content + ")"; + fileContentMap.put(filename, contentUrl); + } else { + String contentUrl = "附件[" + filename + "]下载地址:" + content; + fileContentMap.put(filename, contentUrl); + } + } catch (URISyntaxException e) { + e.printStackTrace(); } } } @@ -851,8 +857,8 @@ public class JiraPlatform extends AbstractIssuePlatform { return false; } - public ResponseEntity proxyForGet(String url, Class responseEntityClazz) { - return jiraClientV2.proxyForGet(url, responseEntityClazz); + public ResponseEntity proxyForGet(String path, Class responseEntityClazz) { + return jiraClientV2.proxyForGet(path, responseEntityClazz); } @Override diff --git a/backend/src/main/java/io/metersphere/track/issue/ZentaoPlatform.java b/backend/src/main/java/io/metersphere/track/issue/ZentaoPlatform.java index 47b8696624..224ad56a7d 100644 --- a/backend/src/main/java/io/metersphere/track/issue/ZentaoPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/ZentaoPlatform.java @@ -453,7 +453,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform { while (matcher.find()) { // get file name String originSubUrl = matcher.group(1); - if (originSubUrl.contains("/url?url=")) { + if (originSubUrl.contains("/url?url=") || originSubUrl.contains("/path?")) { String path = URLDecoder.decode(originSubUrl, StandardCharsets.UTF_8); String fileName; if (path.indexOf("fileID") > 0) { @@ -525,11 +525,18 @@ public class ZentaoPlatform extends AbstractIssuePlatform { } } else { name = name.replaceAll("&", "&"); - path = zentaoClient.getBaseUrl() + path.replaceAll("&", "&"); + path = path.replaceAll("&", "&"); } - // 专业版格式有差异,解析完会出现两个 /pro,去掉一个 - path.replace("/pro/pro", "/pro"); - path = "/resource/md/get/url?url=" + URLEncoder.encode(path, StandardCharsets.UTF_8); + StringBuilder stringBuilder = new StringBuilder(); + for (String item : path.split("&")) { + // 去掉多余的参数 + if (!StringUtils.containsAny(item, "platform", "workspaceId")) { + stringBuilder.append(item); + stringBuilder.append("&"); + } + } + path = getProxyPath(stringBuilder.toString()); + // 图片与描述信息之间需换行,否则无法预览图片 result = "\n\n![" + name + "](" + path + ")"; } @@ -637,4 +644,9 @@ public class ZentaoPlatform extends AbstractIssuePlatform { } return platformStatusDTOS; } + + @Override + public ResponseEntity proxyForGet(String path, Class responseEntityClazz) { + return zentaoClient.proxyForGet(path, responseEntityClazz); + } } diff --git a/backend/src/main/java/io/metersphere/track/issue/client/BaseClient.java b/backend/src/main/java/io/metersphere/track/issue/client/BaseClient.java index d1fa8a3833..d40af6b64b 100644 --- a/backend/src/main/java/io/metersphere/track/issue/client/BaseClient.java +++ b/backend/src/main/java/io/metersphere/track/issue/client/BaseClient.java @@ -6,6 +6,7 @@ import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.EncryptUtils; import io.metersphere.commons.utils.EnvProxySelector; import io.metersphere.commons.utils.LogUtil; +import org.apache.commons.lang3.StringUtils; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; @@ -17,6 +18,8 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import javax.net.ssl.SSLContext; +import java.net.URI; +import java.net.URISyntaxException; import java.security.cert.X509Certificate; import java.util.Arrays; @@ -70,4 +73,16 @@ public abstract class BaseClient { protected Object getResultForObject(Class clazz,ResponseEntity response) { return JSONObject.parseObject(getResult(response), clazz); } + + public void validateProxyUrl(String url, String ...path) { + try { + if (!StringUtils.containsAny(new URI(url).getPath(), path)) { + // 只允许访问图片 + MSException.throwException("illegal path"); + } + } catch (URISyntaxException e) { + LogUtil.error(e); + MSException.throwException("illegal path"); + } + } } diff --git a/backend/src/main/java/io/metersphere/track/issue/client/JiraAbstractClient.java b/backend/src/main/java/io/metersphere/track/issue/client/JiraAbstractClient.java index ed256ce6dc..c2039fe236 100644 --- a/backend/src/main/java/io/metersphere/track/issue/client/JiraAbstractClient.java +++ b/backend/src/main/java/io/metersphere/track/issue/client/JiraAbstractClient.java @@ -294,8 +294,10 @@ public abstract class JiraAbstractClient extends BaseClient { } } - public ResponseEntity proxyForGet(String url, Class responseEntityClazz) { - LogUtil.info("jira proxyForGet: " + url); + public ResponseEntity proxyForGet(String path, Class responseEntityClazz) { + LogUtil.info("jira proxyForGet: " + path); + String url = this.ENDPOINT + path; + validateProxyUrl(url, "/secure/attachment", "/attachment/content"); return restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), responseEntityClazz); } } diff --git a/backend/src/main/java/io/metersphere/track/issue/client/ZentaoClient.java b/backend/src/main/java/io/metersphere/track/issue/client/ZentaoClient.java index 2e52b8f4c4..13f5ff9b7c 100644 --- a/backend/src/main/java/io/metersphere/track/issue/client/ZentaoClient.java +++ b/backend/src/main/java/io/metersphere/track/issue/client/ZentaoClient.java @@ -15,6 +15,8 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Map; public abstract class ZentaoClient extends BaseClient { @@ -248,4 +250,19 @@ public abstract class ZentaoClient extends BaseClient { null, byte[].class, fileId, sessionId); return response.getBody(); } + + public ResponseEntity proxyForGet(String path, Class responseEntityClazz) { + LogUtil.info("zentao proxyForGet: " + path); + String url = this.ENDPOINT + path; + try { + if (!StringUtils.containsAny(new URI(url).getPath(), "/index.php", "/file-read-")) { + // 只允许访问图片 + MSException.throwException("illegal path"); + } + } catch (URISyntaxException e) { + LogUtil.error(e); + MSException.throwException("illegal path"); + } + return restTemplate.exchange(url, HttpMethod.GET, null, responseEntityClazz); + } } diff --git a/backend/src/main/java/io/metersphere/track/issue/client/ZentaoGetClient.java b/backend/src/main/java/io/metersphere/track/issue/client/ZentaoGetClient.java index 9671b0d257..904753c58e 100644 --- a/backend/src/main/java/io/metersphere/track/issue/client/ZentaoGetClient.java +++ b/backend/src/main/java/io/metersphere/track/issue/client/ZentaoGetClient.java @@ -15,7 +15,7 @@ public class ZentaoGetClient extends ZentaoClient { private static final String STORY_GET="&module=story&methodName=getProductStories¶ms=productID={key}&t=json&zentaosid="; private static final String USER_GET="&module=user&methodName=getList&t=json&zentaosid="; private static final String BUILDS_GET="&module=build&methodName=getProductBuildPairs&productID={0}&zentaosid={1}"; - private static final String FILE_UPLOAD="&module=file&methodName=saveUpload¶ms=objectType={1},objectID={2},zentaosid={3}"; + private static final String FILE_UPLOAD="&module=file&methodName=saveUpload¶ms=objectType={1},objectID={2}&t=json&zentaosid={3}"; private static final String FILE_DELETE="/?m=file&f=delete&t=json&fileID={1}&confirm=yes&zentaosid={2}"; private static final String FILE_DOWNLOAD="/?m=file&f=download&t=json&fileID={1}&mouse=click&zentaosid={2}"; private static final String CREATE_META_DATA="?m=bug&f=create&productID={0}&t=json&zentaosid={1}";