fix: 分享链接安全漏洞

This commit is contained in:
fit2cloud-chenyw
2026-05-28 17:25:52 +08:00
committed by fit2cloud-chenyw
parent 980537339d
commit c4e85a981e
4 changed files with 56 additions and 7 deletions

View File

@@ -92,6 +92,8 @@ public class ChartDataManage {
view.setChartExtRequest(chartExtRequest);
}
chartViewManege.checkLinkChart(view);
//excel导出如果是从仪表板获取图表数据则仪表板的查询模式查询结果的数量覆盖图表对应的属性
if (view.getIsExcelExport()) {
view.setResultMode(ChartConstants.VIEW_RESULT_MODE.CUSTOM);

View File

@@ -1,5 +1,7 @@
package io.dataease.chart.manage;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -10,6 +12,7 @@ import io.dataease.chart.dao.auto.entity.CoreChartView;
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
import io.dataease.chart.dao.ext.entity.ChartBasePO;
import io.dataease.chart.dao.ext.mapper.ExtChartViewMapper;
import io.dataease.constant.AuthConstant;
import io.dataease.constant.CommonConstants;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableField;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableFieldMapper;
@@ -34,6 +37,7 @@ import io.dataease.utils.BeanUtils;
import io.dataease.utils.IDUtils;
import io.dataease.utils.JsonUtil;
import io.dataease.utils.LogUtil;
import io.dataease.utils.ServletUtils;
import io.dataease.visualization.dao.auto.entity.DataVisualizationInfo;
import io.dataease.visualization.dao.auto.entity.SnapshotCoreChartView;
import io.dataease.visualization.dao.auto.mapper.DataVisualizationInfoMapper;
@@ -159,6 +163,32 @@ public class ChartViewManege {
return dto;
}
public void checkLinkChart(ChartViewDTO view) {
Long resourceId = view.getSceneId();
Long viewId = view.getId();
Long tableId = view.getTableId();
String linkToken = ServletUtils.getHead(AuthConstant.LINK_TOKEN_KEY);
if (StringUtils.isBlank(linkToken)) {
return;
}
DecodedJWT jwt = JWT.decode(linkToken);
Long tokenResourceId = jwt.getClaim("resourceId").asLong();
if (!tokenResourceId.equals(resourceId)) {
DEException.throwException("超出分享链接权限");
}
CoreChartView chartView = coreChartViewMapper.selectById(viewId);
if (chartView == null) {
DEException.throwException(Translator.get("i18n_chart_delete"));
}
if (!chartView.getTableId().equals(tableId)) {
DEException.throwException("超出分享链接权限");
}
}
/**
* sceneId 为仪表板或者数据大屏id
*/

View File

@@ -261,15 +261,18 @@ public class XpackShareManage {
vo.setInIframeError(false);
return vo;
}
String defaultPwd = shareSecretManage.getDefaultPwd();
String secret = StringUtils.isBlank(xpackShare.getPwd()) ? defaultPwd : xpackShare.getPwd();
String linkToken = LinkTokenUtil.generate(xpackShare.getCreator(), xpackShare.getResourceId(), xpackShare.getExp(), secret, xpackShare.getOid());
HttpServletResponse response = ServletUtils.response();
response.addHeader(AuthConstant.LINK_TOKEN_KEY, linkToken);
Integer type = xpackShare.getType();
String typeText = (ObjectUtils.isNotEmpty(type) && type == 1) ? "dashboard" : "dataV";
TicketValidVO validVO = shareTicketManage.validateTicket(request.getTicket(), xpackShare);
return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp(xpackShare), pwdValid(xpackShare, request.getCiphertext()), typeText, inIframeError, false, true, validVO);
boolean linkExp = linkExp(xpackShare);
boolean pwdValid = pwdValid(xpackShare, request.getCiphertext());
if (!linkExp && pwdValid && validVO.isTicketValid() && !validVO.isTicketExp()) {
generateLinkToken(xpackShare);
}
return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp, pwdValid, typeText, inIframeError, false, true, validVO);
}
private boolean linkExp(XpackShare xpackShare) {
@@ -307,7 +310,20 @@ public class XpackShareManage {
QueryWrapper<XpackShare> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("uuid", uuid);
XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper);
return StringUtils.equals(xpackShare.getUuid(), uuid) && StringUtils.equals(xpackShare.getPwd(), pwd);
boolean valid = StringUtils.equals(xpackShare.getUuid(), uuid) && StringUtils.equals(xpackShare.getPwd(), pwd);
if (valid) {
generateLinkToken(xpackShare);
}
return valid;
}
private void generateLinkToken(XpackShare xpackShare) {
String defaultPwd = shareSecretManage.getDefaultPwd();
String secret = StringUtils.isBlank(xpackShare.getPwd()) ? defaultPwd : xpackShare.getPwd();
String linkToken = LinkTokenUtil.generate(xpackShare.getCreator(), xpackShare.getResourceId(), xpackShare.getExp(), secret, xpackShare.getOid());
HttpServletResponse response = ServletUtils.response();
assert response != null;
response.addHeader(AuthConstant.LINK_TOKEN_KEY, linkToken);
}
public Map<String, String> queryRelationByUserId(Long uid) {

View File

@@ -74,6 +74,7 @@ public class WhitelistUtils {
|| StringUtils.startsWithAny(requestURI, "/static-resource/")
|| StringUtils.startsWithAny(requestURI, "/appearance/image/")
|| StringUtils.startsWithAny(requestURI, "/share/proxyInfo")
|| StringUtils.startsWithAny(requestURI, "/share/validate")
|| StringUtils.startsWithAny(requestURI, "/xpackComponent/content")
|| StringUtils.startsWithAny(requestURI, "/xpackComponent/pluginStaticInfo")
|| StringUtils.startsWithAny(requestURI, "/geo/")