diff --git a/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java b/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java index 8824da3e23..a86e4bbea9 100644 --- a/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java +++ b/core/core-backend/src/main/java/io/dataease/chart/server/ChartDataServer.java @@ -5,6 +5,7 @@ import io.dataease.api.chart.ChartDataApi; import io.dataease.api.chart.dto.ViewDetailField; import io.dataease.api.chart.request.ChartExcelRequest; import io.dataease.api.chart.request.ChartExcelRequestInner; +import io.dataease.auth.DeLinkPermit; import io.dataease.chart.constant.ChartConstants; import io.dataease.chart.manage.ChartDataManage; import io.dataease.constant.AuthConstant; @@ -76,6 +77,7 @@ public class ChartDataServer implements ChartDataApi { return Math.toIntExact(Math.min(f2CLicLimitedManage.checkDatasetLimit(), limit)); } + @DeLinkPermit("#p0.sceneId") @Override public ChartViewDTO getData(ChartViewDTO chartViewDTO) throws Exception { try { diff --git a/core/core-backend/src/main/java/io/dataease/config/DeMvcConfig.java b/core/core-backend/src/main/java/io/dataease/config/DeMvcConfig.java index a21c988dc5..3a06641dd3 100644 --- a/core/core-backend/src/main/java/io/dataease/config/DeMvcConfig.java +++ b/core/core-backend/src/main/java/io/dataease/config/DeMvcConfig.java @@ -1,7 +1,10 @@ package io.dataease.config; import io.dataease.constant.AuthConstant; +import io.dataease.share.interceptor.LinkInterceptor; +import jakarta.annotation.Resource; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -11,6 +14,8 @@ import static io.dataease.utils.StaticResourceUtils.ensureSuffix; @Configuration public class DeMvcConfig implements WebMvcConfigurer { + @Resource + private LinkInterceptor linkInterceptor; /** * Configuring static resource path @@ -33,4 +38,9 @@ public class DeMvcConfig implements WebMvcConfigurer { registry.addResourceHandler(geoUrlPattern).addResourceLocations(geoDir); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(linkInterceptor).addPathPatterns("/**"); + } } diff --git a/core/core-backend/src/main/java/io/dataease/share/interceptor/DeLinkAop.java b/core/core-backend/src/main/java/io/dataease/share/interceptor/DeLinkAop.java new file mode 100644 index 0000000000..2a99251b02 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/share/interceptor/DeLinkAop.java @@ -0,0 +1,110 @@ +package io.dataease.share.interceptor; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.Verification; +import io.dataease.auth.DeLinkPermit; +import io.dataease.constant.AuthConstant; +import io.dataease.exception.DEException; +import io.dataease.share.manage.XpackShareManage; +import io.dataease.share.util.LinkTokenUtil; +import io.dataease.utils.LogUtil; +import io.dataease.utils.ServletUtils; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.Objects; + +@Aspect +@Component +public class DeLinkAop { + + private static final String PARAM_VARIABLE_PREFIX = "p"; + private static final String SPRING_EL_FLAG = "#"; + + private final ExpressionParser parser = new SpelExpressionParser(); + + @Resource + private XpackShareManage xpackShareManage; + + @Around(value = "@annotation(io.dataease.auth.DeLinkPermit)") + public Object logAround(ProceedingJoinPoint point) throws Throwable { + Object[] params = point.getArgs(); + String linkToken = ServletUtils.getHead(AuthConstant.LINK_TOKEN_KEY); + if (StringUtils.isNotBlank(linkToken)) { + MethodSignature ms = (MethodSignature) point.getSignature(); + Method method = ms.getMethod(); + DeLinkPermit deLinkPermit = method.getAnnotation(DeLinkPermit.class); + String value = deLinkPermit.value(); + if (StringUtils.isBlank(value)) { + value = SPRING_EL_FLAG + PARAM_VARIABLE_PREFIX + "0"; + } + Long id = getExpression(params, value); + DecodedJWT jwt = JWT.decode(linkToken); + Long resourceId = jwt.getClaim("resourceId").asLong(); + if (!id.equals(resourceId)) { + DEException.throwException("link token invalid"); + return false; + } + + Long uid = jwt.getClaim("uid").asLong(); + String secret = xpackShareManage.queryPwd(resourceId, uid); + if (StringUtils.isBlank(secret)) { + secret = LinkTokenUtil.defaultPwd; + } + Algorithm algorithm = Algorithm.HMAC256(secret); + Verification verification = JWT.require(algorithm); + JWTVerifier verifier = verification.build(); + DecodedJWT decode = JWT.decode(linkToken); + algorithm.verify(decode); + verifier.verify(linkToken); + } + try { + return point.proceed(params); + } catch (Exception e) { + LogUtil.error(e.getMessage()); + throw e; + } + } + + public Long getExpression(Object[] params, String expression) { + StandardEvaluationContext context = buildContext(params); + Object o = resolveValue(expression, context); + if (ObjectUtils.isNotEmpty(o)) return Long.parseLong(o.toString()); + return null; + } + + private StandardEvaluationContext buildContext(Object[] params) { + StandardEvaluationContext context = new StandardEvaluationContext(); + if (params != null && params.length == 1) { + context.setRootObject(params[0]); + } + for (int i = 0; i < Objects.requireNonNull(params).length; i++) { + Object paramValue = params[i]; + context.setVariable(PARAM_VARIABLE_PREFIX + i, paramValue); + } + return context; + } + + private Object resolveValue(String exp, EvaluationContext context) { + if (StringUtils.contains(exp, SPRING_EL_FLAG)) { + Expression expression = parser.parseExpression(exp); + return expression.getValue(context); + } + return exp; + } +} diff --git a/core/core-backend/src/main/java/io/dataease/share/interceptor/LinkInterceptor.java b/core/core-backend/src/main/java/io/dataease/share/interceptor/LinkInterceptor.java new file mode 100644 index 0000000000..f73eaf66f6 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/share/interceptor/LinkInterceptor.java @@ -0,0 +1,54 @@ +package io.dataease.share.interceptor; + +import io.dataease.auth.DeLinkPermit; +import io.dataease.constant.AuthConstant; +import io.dataease.exception.DEException; +import io.dataease.utils.ServletUtils; +import io.dataease.utils.WhitelistUtils; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.Arrays; +import java.util.List; + + +@Component +public class LinkInterceptor implements HandlerInterceptor { + + private final static String whiteListText = "/user/ipInfo, /apisix/check"; + + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String linkToken = ServletUtils.getHead(AuthConstant.LINK_TOKEN_KEY); + if (linkToken == null) { + return true; + } + if (handler instanceof HandlerMethod handlerMethod) { + DeLinkPermit deLinkPermit = handlerMethod.getMethodAnnotation(DeLinkPermit.class); + if (deLinkPermit == null) { + + List whiteList = Arrays.stream(StringUtils.split(whiteListText, ",")).map(String::trim).toList(); + String requestURI = ServletUtils.request().getRequestURI(); + if (StringUtils.startsWith(requestURI, WhitelistUtils.getContextPath())) { + requestURI = requestURI.replaceFirst(WhitelistUtils.getContextPath(), ""); + } + if (StringUtils.startsWith(requestURI, AuthConstant.DE_API_PREFIX)) { + requestURI = requestURI.replaceFirst(AuthConstant.DE_API_PREFIX, ""); + } + boolean valid = whiteList.contains(requestURI); + if (!valid) { + DEException.throwException("分享链接Token不支持访问当前url[" + requestURI + "]"); + } + return true; + } + } + return true; + } + + +} diff --git a/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java b/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java index 79f6b06b58..60aeaf8f32 100644 --- a/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java +++ b/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java @@ -64,6 +64,15 @@ public class XpackShareManage { return xpackShareMapper.selectOne(queryWrapper); } + public String queryPwd(Long resourceId, Long userId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("creator", userId); + queryWrapper.eq("resource_id", resourceId); + XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper); + if (ObjectUtils.isEmpty(xpackShare)) return null; + return xpackShare.getPwd(); + } + @Transactional public void switcher(Long resourceId) { XpackShare originData = queryByResource(resourceId); diff --git a/core/core-backend/src/main/java/io/dataease/share/util/LinkTokenUtil.java b/core/core-backend/src/main/java/io/dataease/share/util/LinkTokenUtil.java index e460e9361e..37adf92631 100644 --- a/core/core-backend/src/main/java/io/dataease/share/util/LinkTokenUtil.java +++ b/core/core-backend/src/main/java/io/dataease/share/util/LinkTokenUtil.java @@ -9,7 +9,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.Date; public class LinkTokenUtil { - private static final String defaultPwd = "link-pwd-fit2cloud"; + public static final String defaultPwd = "link-pwd-fit2cloud"; public static String generate(Long uid, Long resourceId, Long exp, String pwd, Long oid) { pwd = StringUtils.isBlank(pwd) ? defaultPwd : pwd; Algorithm algorithm = Algorithm.HMAC256(pwd); diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/DataVisualizationServer.java b/core/core-backend/src/main/java/io/dataease/visualization/server/DataVisualizationServer.java index 7bf017ba72..1395bef676 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/DataVisualizationServer.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/DataVisualizationServer.java @@ -11,6 +11,7 @@ import io.dataease.api.visualization.request.DataVisualizationBaseRequest; import io.dataease.api.visualization.request.VisualizationAppExportRequest; import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest; import io.dataease.api.visualization.vo.*; +import io.dataease.auth.DeLinkPermit; import io.dataease.chart.dao.auto.entity.CoreChartView; import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper; import io.dataease.chart.manage.ChartDataManage; @@ -142,6 +143,7 @@ public class DataVisualizationServer implements DataVisualizationApi { } } + @DeLinkPermit("#p0.id") @DeLog(id = "#p0.id", ot = LogOT.READ, stExp = "#p0.busiFlag") @Override @XpackInteract(value = "dataVisualizationServer", original = true) diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkJumpService.java b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkJumpService.java index b01bb82a5b..459bec6710 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkJumpService.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkJumpService.java @@ -7,6 +7,7 @@ import io.dataease.api.visualization.dto.VisualizationLinkJumpInfoDTO; import io.dataease.api.visualization.request.VisualizationLinkJumpBaseRequest; import io.dataease.api.visualization.response.VisualizationLinkJumpBaseResponse; import io.dataease.api.visualization.vo.VisualizationViewTableVO; +import io.dataease.auth.DeLinkPermit; import io.dataease.chart.dao.auto.entity.CoreChartView; import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper; import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO; @@ -67,6 +68,7 @@ public class VisualizationLinkJumpService implements VisualizationLinkJumpApi { return extVisualizationLinkageMapper.queryTableFieldWithViewId(viewId); } + @DeLinkPermit //获取仪表板的跳转信息 @Override public VisualizationLinkJumpBaseResponse queryVisualizationJumpInfo(Long dvId) { diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkageService.java b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkageService.java index e7af4e5562..8f256d9eaf 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkageService.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationLinkageService.java @@ -6,6 +6,7 @@ import io.dataease.api.visualization.dto.LinkageInfoDTO; import io.dataease.api.visualization.dto.VisualizationLinkageDTO; import io.dataease.api.visualization.request.VisualizationLinkageRequest; import io.dataease.api.visualization.vo.VisualizationLinkageFieldVO; +import io.dataease.auth.DeLinkPermit; import io.dataease.chart.dao.auto.entity.CoreChartView; import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper; import io.dataease.utils.BeanUtils; @@ -108,6 +109,7 @@ public class VisualizationLinkageService implements VisualizationLinkageApi { return new BaseRspModel(); } + @DeLinkPermit @Override public Map> getVisualizationAllLinkageInfo(Long dvId) { List info = extVisualizationLinkageMapper.getPanelAllLinkageInfo(dvId); diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationOuterParamsService.java b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationOuterParamsService.java index ddf4c31d4d..b3f4bdf018 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationOuterParamsService.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/VisualizationOuterParamsService.java @@ -2,13 +2,13 @@ package io.dataease.visualization.server; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.fasterxml.jackson.core.type.TypeReference; -import com.ibm.icu.impl.coll.CollationLoader; import io.dataease.api.dataset.vo.CoreDatasetGroupVO; import io.dataease.api.dataset.vo.CoreDatasetTableFieldVO; import io.dataease.api.visualization.VisualizationOuterParamsApi; import io.dataease.api.visualization.dto.VisualizationOuterParamsDTO; import io.dataease.api.visualization.dto.VisualizationOuterParamsInfoDTO; import io.dataease.api.visualization.response.VisualizationOuterParamsBaseResponse; +import io.dataease.auth.DeLinkPermit; import io.dataease.dataset.dao.auto.entity.CoreDatasetTable; import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableMapper; import io.dataease.engine.constant.DeTypeConstants; @@ -29,7 +29,10 @@ import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; /** @@ -98,6 +101,7 @@ public class VisualizationOuterParamsService implements VisualizationOuterParams } + @DeLinkPermit @Override public VisualizationOuterParamsBaseResponse getOuterParamsInfo(String visualizationId) { List result = extOuterParamsMapper.getVisualizationOuterParamsInfo(visualizationId); diff --git a/sdk/common/src/main/java/io/dataease/auth/DeLinkPermit.java b/sdk/common/src/main/java/io/dataease/auth/DeLinkPermit.java new file mode 100644 index 0000000000..46eba5e5d2 --- /dev/null +++ b/sdk/common/src/main/java/io/dataease/auth/DeLinkPermit.java @@ -0,0 +1,12 @@ +package io.dataease.auth; + +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DeLinkPermit { + + String value() default ""; + +}