forked from github/dataease
fix(仪表板): 分享-token数据安全
This commit is contained in:
parent
f78ec4b4fa
commit
340734510e
@ -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 {
|
||||
|
@ -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("/**");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<String> 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -64,6 +64,15 @@ public class XpackShareManage {
|
||||
return xpackShareMapper.selectOne(queryWrapper);
|
||||
}
|
||||
|
||||
public String queryPwd(Long resourceId, Long userId) {
|
||||
QueryWrapper<XpackShare> 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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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<String, List<String>> getVisualizationAllLinkageInfo(Long dvId) {
|
||||
List<LinkageInfoDTO> info = extVisualizationLinkageMapper.getPanelAllLinkageInfo(dvId);
|
||||
|
@ -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<VisualizationOuterParamsInfoDTO> result = extOuterParamsMapper.getVisualizationOuterParamsInfo(visualizationId);
|
||||
|
12
sdk/common/src/main/java/io/dataease/auth/DeLinkPermit.java
Normal file
12
sdk/common/src/main/java/io/dataease/auth/DeLinkPermit.java
Normal file
@ -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 "";
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user