From 15e1e4bff8a09d80b319940aed32be447729f0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=AB=8B=E5=AE=8F?= <2198083211@qq.com> Date: Thu, 23 Nov 2023 18:12:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(MyDynamicController=E3=80=81DatabaseManage?= =?UTF-8?q?mentController=E3=80=81Strategy):=20=E5=8A=A8=E6=80=81=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E6=8B=A6=E6=88=AA=E5=AE=9E=E7=8E=B0=E3=80=81=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=20Strate?= =?UTF-8?q?gy=20=E5=AE=9E=E7=8E=B0=E5=AF=B9=20PostgreSQL=20=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、动态路由拦截实现。 2、数据库管理模块 Strategy 实现对 PostgreSQL 数据库的支持。 3、查询数据库数据的字段名及类型(queryTableFields)适应postgresql数据库。 --- .../controller/CustomizeRouteController.java | 2 + .../DatabaseManagementController.java | 14 +- .../app/controller/ExternalAppController.java | 34 +- .../ProjectDatasourceController.java | 38 ++ .../MyDynamicController.java | 60 ++- .../app/dao/CustomizeRouteMapper.java | 23 + .../webadmin/app/dao/ExternalAppMapper.java | 10 + .../app/dao/mapper/CustomizeRouteMapper.xml | 15 + .../app/dao/mapper/ExternalAppMapper.xml | 10 + .../webadmin/app/liteFlow/node/SqlNode.java | 11 +- .../app/service/CustomizeRouteService.java | 30 ++ .../app/service/ExternalAppService.java | 21 +- .../databasemanagement/StrategyFactory.java | 1 - .../model/DatabaseManagement.java | 6 +- .../strategyImpl/BaseDataSource.java | 48 +- .../strategyImpl/DataSourceDB2.java | 3 - .../strategyImpl/DataSourceDM.java | 3 - .../strategyImpl/DataSourceDoris.java | 3 - .../strategyImpl/DataSourceHive.java | 3 - .../strategyImpl/DataSourceMySql.java | 2 - .../strategyImpl/DataSourceOracle.java | 3 - .../strategyImpl/DataSourcePostgreSQL.java | 15 +- .../strategyImpl/DataSourceSQLServer.java | 3 - .../strategyImpl/DataSourceSapHana.java | 3 - .../strategyImpl/DataSourceSyBase.java | 3 - .../strategyImpl/DataSourceTdSql.java | 3 - .../impl/CustomizeRouteServiceImpl.java | 57 ++- .../ExternalAppCustomizeRouteServiceImpl.java | 29 ++ .../service/impl/ExternalAppServiceImpl.java | 41 +- .../ApiAuthenticationInterceptor.java | 438 ++++++------------ .../supie/common/core/util/MyCommonUtil.java | 76 +++ pom.xml | 25 + 32 files changed, 649 insertions(+), 384 deletions(-) diff --git a/application-webadmin/src/main/java/supie/webadmin/app/controller/CustomizeRouteController.java b/application-webadmin/src/main/java/supie/webadmin/app/controller/CustomizeRouteController.java index 2ac1aa3..c1ca353 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/controller/CustomizeRouteController.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/controller/CustomizeRouteController.java @@ -1,6 +1,7 @@ package supie.webadmin.app.controller; import io.swagger.annotations.ApiOperation; +import org.redisson.api.RedissonClient; import supie.common.log.annotation.OperationLog; import supie.common.log.model.constant.SysOperationLogType; import com.github.pagehelper.page.PageMethod; @@ -194,6 +195,7 @@ public class CustomizeRouteController { errorMessage = "数据操作失败,删除的对象不存在,请刷新后重试!"; return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST, errorMessage); } + customizeRouteService.unregisterDynamicRouteFromRedis(originalCustomizeRoute.getUrl()); return ResponseResult.success(); } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/controller/DatabaseManagementController.java b/application-webadmin/src/main/java/supie/webadmin/app/controller/DatabaseManagementController.java index 2335f0d..f29e8d1 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/controller/DatabaseManagementController.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/controller/DatabaseManagementController.java @@ -46,7 +46,7 @@ public class DatabaseManagementController { try { Strategy strategy = strategyFactory.getStrategy( databaseManagement.getDatabaseType(), databaseManagement.getIp(), databaseManagement.getPort(), - databaseManagement.getDatabaseName(), databaseManagement.getUsername(), databaseManagement.getPassword()); + databaseManagement.getDatabase(), databaseManagement.getUser(), databaseManagement.getPassword()); strategy.closeAll(); } catch (Exception e) { log.error("数据源连接失败", e); @@ -68,8 +68,8 @@ public class DatabaseManagementController { try { Strategy strategy = strategyFactory.getStrategy( databaseManagement.getDatabaseType(), databaseManagement.getIp(), databaseManagement.getPort(), - databaseManagement.getDatabaseName(), databaseManagement.getUsername(), databaseManagement.getPassword()); - price = strategy.queryDatabaseTable(databaseManagement.getDatabaseName()); + databaseManagement.getDatabase(), databaseManagement.getUser(), databaseManagement.getPassword()); + price = strategy.queryDatabaseTable(databaseManagement.getDatabase()); strategy.closeAll(); } catch (Exception e) { log.error("数据源连接失败", e); @@ -91,8 +91,8 @@ public class DatabaseManagementController { try { Strategy strategy = strategyFactory.getStrategy( databaseManagement.getDatabaseType(), databaseManagement.getIp(), databaseManagement.getPort(), - databaseManagement.getDatabaseName(), databaseManagement.getUsername(), databaseManagement.getPassword()); - price = strategy.queryTableFields(databaseManagement.getDatabaseName(), databaseManagement.getTableName()); + databaseManagement.getDatabase(), databaseManagement.getUser(), databaseManagement.getPassword()); + price = strategy.queryTableFields(databaseManagement.getDatabase(), databaseManagement.getTable()); strategy.closeAll(); } catch (Exception e) { log.error("数据源连接失败", e); @@ -108,7 +108,7 @@ public class DatabaseManagementController { try { Strategy strategy = strategyFactory.getStrategy( databaseManagement.getDatabaseType(), databaseManagement.getIp(), databaseManagement.getPort(), - databaseManagement.getDatabaseName(), databaseManagement.getUsername(), databaseManagement.getPassword()); + databaseManagement.getDatabase(), databaseManagement.getUser(), databaseManagement.getPassword()); resultData = strategy.executeSqlList(sql); strategy.closeAll(); } catch (Exception e) { @@ -123,7 +123,7 @@ public class DatabaseManagementController { public ResponseResult> getAllDatabaseName(@MyRequestBody DatabaseManagement databaseManagement){ Strategy strategy = strategyFactory.getStrategy( databaseManagement.getDatabaseType(), databaseManagement.getIp(), databaseManagement.getPort(), - databaseManagement.getDatabaseName(), databaseManagement.getUsername(), databaseManagement.getPassword()); + databaseManagement.getDatabase(), databaseManagement.getUser(), databaseManagement.getPassword()); List resultData = strategy.queryAllDatabaseName(); strategy.closeAll(); return ResponseResult.success(resultData); diff --git a/application-webadmin/src/main/java/supie/webadmin/app/controller/ExternalAppController.java b/application-webadmin/src/main/java/supie/webadmin/app/controller/ExternalAppController.java index 445b245..d6218ca 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/controller/ExternalAppController.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/controller/ExternalAppController.java @@ -1,6 +1,7 @@ package supie.webadmin.app.controller; import io.swagger.annotations.ApiOperation; +import org.redisson.api.RedissonClient; import supie.common.log.annotation.OperationLog; import supie.common.log.model.constant.SysOperationLogType; import com.github.pagehelper.page.PageMethod; @@ -261,6 +262,10 @@ public class ExternalAppController { || !customizeRouteService.existUniqueKeyList("id", customizeRouteIdSet)) { return ResponseResult.error(ErrorCodeEnum.INVALID_RELATED_RECORD_ID); } + List customizeRouteList = customizeRouteService.queryAssociatedCustomizeRoute(externalAppId); + for (CustomizeRoute customizeRoute : customizeRouteList) { + customizeRouteService.unregisterDynamicRouteFromRedis(customizeRoute.getUrl()); + } List externalAppCustomizeRouteList = MyModelUtil.copyCollectionTo(externalAppCustomizeRouteDtoList, ExternalAppCustomizeRoute.class); externalAppService.addExternalAppCustomizeRouteList(externalAppCustomizeRouteList, externalAppId); @@ -282,7 +287,14 @@ public class ExternalAppController { if (errorMessage != null) { return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); } - ExternalAppCustomizeRoute externalAppCustomizeRoute = MyModelUtil.copyTo(externalAppCustomizeRouteDto, ExternalAppCustomizeRoute.class); + List customizeRouteList = + customizeRouteService.queryAssociatedCustomizeRoute(externalAppCustomizeRouteDto.getExternalAppId()); + customizeRouteList.add(customizeRouteService.getById(externalAppCustomizeRouteDto.getCustomizeRouteId())); + for (CustomizeRoute customizeRoute : customizeRouteList) { + customizeRouteService.unregisterDynamicRouteFromRedis(customizeRoute.getUrl()); + } + ExternalAppCustomizeRoute externalAppCustomizeRoute = + MyModelUtil.copyTo(externalAppCustomizeRouteDto, ExternalAppCustomizeRoute.class); if (!externalAppService.updateExternalAppCustomizeRoute(externalAppCustomizeRoute)) { return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); } @@ -320,6 +332,11 @@ public class ExternalAppController { @PostMapping("/deleteExternalAppCustomizeRoute") public ResponseResult deleteExternalAppCustomizeRoute( @MyRequestBody Long externalAppId, @MyRequestBody Long customizeRouteId) { + List customizeRouteList = customizeRouteService.queryAssociatedCustomizeRoute(externalAppId); + customizeRouteList.add(customizeRouteService.getById(customizeRouteId)); + for (CustomizeRoute customizeRoute : customizeRouteList) { + customizeRouteService.unregisterDynamicRouteFromRedis(customizeRoute.getUrl()); + } if (MyCommonUtil.existBlankArgument(externalAppId, customizeRouteId)) { return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); } @@ -346,10 +363,10 @@ public class ExternalAppController { } /** - * 生成 AppKey + * 生成 AppKey,并保存至数据库 */ - @ApiOperation("生成AppKey") - @GetMapping("/generateAppKey") + @ApiOperation("生成AppKey,并保存至数据库") + @PostMapping("/generateAppKey") public ResponseResult generateAppKey(@RequestParam Long externalAppId) { ExternalApp externalApp = externalAppService.getByIdWithRelation(externalAppId, MyRelationParam.full()); if (externalApp == null) { @@ -360,4 +377,13 @@ public class ExternalAppController { return ResponseResult.success(externalApp); } + /** + * 生成 AppKey + */ + @ApiOperation("生成AppKey") + @GetMapping("/generateAppKey") + public ResponseResult generateAppKey() { + return ResponseResult.success(externalAppService.generateAppKey()); + } + } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/controller/ProjectDatasourceController.java b/application-webadmin/src/main/java/supie/webadmin/app/controller/ProjectDatasourceController.java index cd22395..08554e1 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/controller/ProjectDatasourceController.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/controller/ProjectDatasourceController.java @@ -1,8 +1,11 @@ package supie.webadmin.app.controller; +import io.swagger.annotations.ApiOperation; +import jodd.util.StringUtil; import supie.common.log.annotation.OperationLog; import supie.common.log.model.constant.SysOperationLogType; import com.github.pagehelper.page.PageMethod; +import supie.webadmin.app.service.databasemanagement.StrategyFactory; import supie.webadmin.app.vo.*; import supie.webadmin.app.dto.*; import supie.webadmin.app.model.*; @@ -33,6 +36,8 @@ public class ProjectDatasourceController { @Autowired private ProjectDatasourceService projectDatasourceService; + @Autowired + private StrategyFactory strategyFactory; /** * 新增数据项目-数据源表数据。 @@ -202,4 +207,37 @@ public class ProjectDatasourceController { } return ResponseResult.success(); } + + /** + * 数据库连接测试 + * + * @param projectDatasourceDto 数据源实体类。 + * @return 返回数据连接的信息。 + */ + @ApiOperation("数据库连接测试") + @PostMapping("/connection") + public ResponseResult connection(@MyRequestBody ProjectDatasourceDto projectDatasourceDto){ + if (projectDatasourceDto == null + || StringUtil.isBlank(projectDatasourceDto.getDatasourceType()) + || StringUtil.isBlank(projectDatasourceDto.getDatasourceContent())) { + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST); + } + String datasourceType = projectDatasourceDto.getDatasourceType(); + String datasourceContent = projectDatasourceDto.getDatasourceContent(); + String ip; + String port; + String databaseName; + String userName; + String password; +// try { +// Strategy strategy = strategyFactory.getStrategy( +// datasourceType, ip, port, databaseName, userName, password); +// strategy.closeAll(); +// } catch (Exception e) { +// log.error("数据源连接失败", e); +// return ResponseResult.error("500", "连接失败:"+e.getMessage()); +// } + return ResponseResult.success("连接成功!"); + } + } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/controller/dynamicRoutingAPI/MyDynamicController.java b/application-webadmin/src/main/java/supie/webadmin/app/controller/dynamicRoutingAPI/MyDynamicController.java index 7815975..78b89d4 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/controller/dynamicRoutingAPI/MyDynamicController.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/controller/dynamicRoutingAPI/MyDynamicController.java @@ -2,7 +2,6 @@ package supie.webadmin.app.controller.dynamicRoutingAPI; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import lombok.Data; import lombok.extern.slf4j.Slf4j; @@ -50,8 +49,14 @@ public class MyDynamicController { public ResponseResult executeSql(@RequestBody Map params, HttpServletRequest request) { String url = request.getRequestURI(); RBucket customizeRouteData = redissonClient.getBucket("CustomizeRoute:" + url); - CustomizeRoute customizeRoute = JSONUtil.toBean(customizeRouteData.get(), CustomizeRoute.class); - customizeRouteData.delete(); + if (!customizeRouteData.isExists()) { + return ResponseResult.error(ErrorCodeEnum.NO_ERROR, "当前的路由相关信息获取失败,请重试!"); + } + String customizeRouteJsonStr = customizeRouteData.get(); + CustomizeRoute customizeRoute = JSONUtil.toBean(customizeRouteJsonStr, CustomizeRoute.class); + if (customizeRoute == null) { + return ResponseResult.error(ErrorCodeEnum.NO_ERROR, "当前的路由相关信息获取失败,请联系管理员!"); + } return performCustomizeRouteBusiness(params, customizeRoute); } @@ -60,20 +65,34 @@ public class MyDynamicController { String sqlScript = customizeRoute.getSqlScript(); List parameterList = JSONUtil.toList(JSONUtil.parseArray(customizeRoute.getParameter()), Parameter.class); Set paramsKey = params.keySet(); + String defaultValue; for (Parameter parameter : parameterList) { - if (parameter.getRequired() && !paramsKey.contains(parameter.getName())) { - return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST, "缺少[" + parameter.getName() + "]变量!"); + if (!parameter.getRequired() && !paramsKey.contains(parameter.getName()) && StrUtil.isBlank(parameter.getDefaultValue())) { + // 缺少当前变量 + return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST, "缺少必填[" + parameter.getName() + "]变量!"); +// if (parameter.getDefaultValue() == null) { +// defaultValue = "null"; +// } else if (Objects.equals(parameter.getDefaultValue(), "")) { +// defaultValue = "\"\""; +// } else { +// defaultValue = parameter.getDefaultValue(); +// } } String name = "${" + parameter.getName() + "}"; - String defaultValue = null; Object value = params.get(parameter.getName()); if (value != null) { - defaultValue = value.toString(); - } else { - if (StrUtil.isBlank(defaultValue)) { - defaultValue = parameter.getDefaultValue(); + if (Objects.equals(value, "")) { + defaultValue = "\"\""; } else { + defaultValue = value.toString(); + } + } else { + if (parameter.getDefaultValue() == null) { defaultValue = "null"; + } else if (Objects.equals(parameter.getDefaultValue(), "")) { + defaultValue = "\"\""; + } else { + defaultValue = parameter.getDefaultValue(); } } sqlScript = sqlScript.replace(name, defaultValue); @@ -86,20 +105,29 @@ public class MyDynamicController { Map resultMap = new HashMap<>(); if (paramsKey.contains("pageParam")) { MyPageParam pageParam = JSONUtil.toBean(JSONUtil.toJsonStr(params.get("pageParam")), MyPageParam.class); - PageHelper.startPage(pageParam.getPageNum(), pageParam.getPageSize()); resultData = strategy.executeSql(sqlScript, pageParam); strategy.closeAll(); if (Boolean.FALSE.equals(resultData.get("success"))) { return ResponseResult.error(ErrorCodeEnum.NO_ERROR, - "(" + resultData.get("sql").toString() + ")" + resultData.get("message").toString()); + resultData.get("sql").toString() + " ==> " + resultData.get("message").toString()); + } + // 判断属于查询还是非查询 + if (resultData.containsKey("queryResultData")) { + Map queryResultData = (Map) resultData.get("queryResultData"); + List> queryDataList = (List>) queryResultData.get("queryDataList"); + PageInfo> mapPageInfo = new PageInfo<>(queryDataList); + resultMap.put("totalCount", mapPageInfo.getTotal()); + } else if (resultData.containsKey("updateResultData")) { + } else { + // 系统错误! } - Map queryResultData = (Map) resultData.get("queryResultData"); - List> queryDataList = (List>) queryResultData.get("queryDataList"); - PageInfo> mapPageInfo = new PageInfo<>(queryDataList); - resultMap.put("totalCount", mapPageInfo.getTotal()); } else { resultData = strategy.executeSql(sqlScript, null); strategy.closeAll(); + if (Boolean.FALSE.equals(resultData.get("success"))) { + return ResponseResult.error(ErrorCodeEnum.NO_ERROR, + resultData.get("sql").toString() + " ==> " + resultData.get("message").toString()); + } } resultMap.put("url", customizeRoute.getUrl()); resultMap.put("resultData", resultData); diff --git a/application-webadmin/src/main/java/supie/webadmin/app/dao/CustomizeRouteMapper.java b/application-webadmin/src/main/java/supie/webadmin/app/dao/CustomizeRouteMapper.java index fec5661..89e3d61 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/dao/CustomizeRouteMapper.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/dao/CustomizeRouteMapper.java @@ -4,6 +4,7 @@ import supie.common.core.annotation.EnableDataPerm; import supie.common.core.base.dao.BaseDaoMapper; import supie.webadmin.app.model.CustomizeRoute; import org.apache.ibatis.annotations.Param; +import supie.webadmin.app.model.ExternalAppCustomizeRoute; import java.util.*; @@ -68,4 +69,26 @@ public interface CustomizeRouteMapper extends BaseDaoMapper { @Param("externalAppId") Long externalAppId, @Param("customizeRouteFilter") CustomizeRoute customizeRouteFilter, @Param("orderBy") String orderBy); + + /** + * 查询externalAppId关联的CustomizeRoute + * + * @param externalAppId 外部应用 ID + * @return 列表<自定义路由> + * @author 王立宏 + * @date 2023/11/22 04:32 + */ + List queryAssociatedCustomizeRoute(@Param("externalAppId") Long externalAppId); + + /** + * 通过 外部应用与自定义路由关联信息 查询自定义路由信息 + * + * @param externalAppCustomizeRouteFilter 外部应用自定义路由 + * @return 自定义路由信息集 + * @author 王立宏 + * @date 2023/11/22 05:25 + */ + List queryCustomizeRouteByExternalAppCustomizeRoute( + @Param("externalAppCustomizeRouteFilter") ExternalAppCustomizeRoute externalAppCustomizeRouteFilter); + } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/dao/ExternalAppMapper.java b/application-webadmin/src/main/java/supie/webadmin/app/dao/ExternalAppMapper.java index e932970..b9987a8 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/dao/ExternalAppMapper.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/dao/ExternalAppMapper.java @@ -47,4 +47,14 @@ public interface ExternalAppMapper extends BaseDaoMapper { */ List getExternalAppList( @Param("externalAppFilter") ExternalApp externalAppFilter, @Param("orderBy") String orderBy); + + /** + * 按 动态路由地址(URL) 查询外部应用信息 + * + * @param url 动态路由地址(URL) + * @return 列表<外部应用> + * @author 王立宏 + * @date 2023/11/22 11:11 + */ + List queryExternalAppByUrl(@Param("url") String url); } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/dao/mapper/CustomizeRouteMapper.xml b/application-webadmin/src/main/java/supie/webadmin/app/dao/mapper/CustomizeRouteMapper.xml index 758937a..a70dfaf 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/dao/mapper/CustomizeRouteMapper.xml +++ b/application-webadmin/src/main/java/supie/webadmin/app/dao/mapper/CustomizeRouteMapper.xml @@ -186,4 +186,19 @@ ORDER BY ${orderBy} + + + + diff --git a/application-webadmin/src/main/java/supie/webadmin/app/dao/mapper/ExternalAppMapper.xml b/application-webadmin/src/main/java/supie/webadmin/app/dao/mapper/ExternalAppMapper.xml index c6a073c..e8789ae 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/dao/mapper/ExternalAppMapper.xml +++ b/application-webadmin/src/main/java/supie/webadmin/app/dao/mapper/ExternalAppMapper.xml @@ -136,4 +136,14 @@ ORDER BY ${orderBy} + + diff --git a/application-webadmin/src/main/java/supie/webadmin/app/liteFlow/node/SqlNode.java b/application-webadmin/src/main/java/supie/webadmin/app/liteFlow/node/SqlNode.java index 79285f1..576aad6 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/liteFlow/node/SqlNode.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/liteFlow/node/SqlNode.java @@ -7,13 +7,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.dao.ProjectDatasourceMapper; import supie.webadmin.app.liteFlow.exception.MyLiteFlowException; -import supie.webadmin.app.liteFlow.model.DatasourceContentModel; import supie.webadmin.app.liteFlow.model.ErrorMessageModel; import supie.webadmin.app.liteFlow.model.LiteFlowNodeLogModel; import supie.webadmin.app.liteFlow.model.SqlAndShellModel; import supie.webadmin.app.model.ProjectDatasource; import supie.webadmin.app.service.databasemanagement.Strategy; import supie.webadmin.app.service.databasemanagement.StrategyFactory; +import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import java.util.List; import java.util.Map; @@ -32,7 +32,8 @@ public class SqlNode extends BaseNode { private SqlAndShellModel sqlAndShellModel; private ProjectDatasource projectDatasource = null; - private DatasourceContentModel datasourceContentModel; + private DatabaseManagement databaseManagement; + @Autowired private ProjectDatasourceMapper projectDatasourceMapper; @Autowired @@ -51,7 +52,7 @@ public class SqlNode extends BaseNode { nodeLog.add(LiteFlowNodeLogModel.error(nodeId, nodeTag, "未找到数据源!")); throw new MyLiteFlowException(new ErrorMessageModel(getClass(), "未找到数据源!")); } - datasourceContentModel = JSONUtil.toBean(projectDatasource.getDatasourceContent(), DatasourceContentModel.class); + databaseManagement = JSONUtil.toBean(projectDatasource.getDatasourceContent(), DatabaseManagement.class); } @Override @@ -60,8 +61,8 @@ public class SqlNode extends BaseNode { Strategy strategy = null; try { strategy = strategyFactory.getStrategy( - datasourceContentModel.getDatabaseType(), datasourceContentModel.getIp(), datasourceContentModel.getPort(), - datasourceContentModel.getDatabaseName(), datasourceContentModel.getUsername(), datasourceContentModel.getPassword()); + databaseManagement.getDatabaseType(), databaseManagement.getIp(), databaseManagement.getPort(), + databaseManagement.getDatabase(), databaseManagement.getUser(), databaseManagement.getPassword()); } catch (Exception e) { nodeLog.add(LiteFlowNodeLogModel.error(nodeId, nodeTag, "获取数据库连接失败!" + e)); throw new MyLiteFlowException(new ErrorMessageModel(getClass(), "获取数据库连接失败!" + e)); diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/CustomizeRouteService.java b/application-webadmin/src/main/java/supie/webadmin/app/service/CustomizeRouteService.java index 618c098..a248281 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/CustomizeRouteService.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/CustomizeRouteService.java @@ -120,4 +120,34 @@ public interface CustomizeRouteService extends IBaseService getCustomizeRouteListByExternalAppId(Long externalAppId, CustomizeRoute customizeRouteFilter, String orderBy); + + /** + * 查询externalAppId关联的CustomizeRoute + * + * @param externalAppId 外部应用 ID + * @return + * @author 王立宏 + * @date 2023/11/22 04:31 + */ + List queryAssociatedCustomizeRoute(Long externalAppId); + + /** + * 从 Redis 注销动态路由信息 + * + * @param url 原始路由 + * @author 王立宏 + * @date 2023/11/22 04:12 + */ + void unregisterDynamicRouteFromRedis(String url); + + /** + * 通过 外部应用与自定义路由关联信息 查询自定义路由信息 + * + * @param externalAppCustomizeRoute 外部应用自定义路由 + * @return 自定义路由信息集 + * @author 王立宏 + * @date 2023/11/22 05:23 + */ + List queryCustomizeRouteByExternalAppCustomizeRoute(ExternalAppCustomizeRoute externalAppCustomizeRoute); + } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/ExternalAppService.java b/application-webadmin/src/main/java/supie/webadmin/app/service/ExternalAppService.java index 745e944..7bfbe44 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/ExternalAppService.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/ExternalAppService.java @@ -113,7 +113,16 @@ public interface ExternalAppService extends IBaseService { boolean removeExternalAppCustomizeRoute(Long externalAppId, Long customizeRouteId); /** - * 生成 AppKey + * 从 Redis 注销动态路由信息 + * + * @param externalAppId 外部AppId + * @author 王立宏 + * @date 2023/11/22 04:12 + */ + void unregisterDynamicRouteFromRedis(Long externalAppId); + + /** + * 生成 AppKey,并保存至数据库 * * @param externalApp 外部应用程序信息 * @return 外部应用程序 @@ -121,4 +130,14 @@ public interface ExternalAppService extends IBaseService { * @date 2023/11/20 02:49 */ ExternalApp generateAppKey(ExternalApp externalApp); + + /** + * 生成 AppKey + * + * @return AppKey + * @author 王立宏 + * @date 2023/11/22 10:02 + */ + String generateAppKey(); + } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/StrategyFactory.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/StrategyFactory.java index 9c0c696..b2ac036 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/StrategyFactory.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/StrategyFactory.java @@ -4,7 +4,6 @@ import jodd.util.StringUtil; import org.springframework.stereotype.Component; import java.util.EnumMap; -import java.util.HashMap; import java.util.Map; /** diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/model/DatabaseManagement.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/model/DatabaseManagement.java index 70d747f..94635d1 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/model/DatabaseManagement.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/model/DatabaseManagement.java @@ -27,11 +27,11 @@ public class DatabaseManagement { @ApiModelProperty(value = "数据名(服务名-架构名)", required = true) @NotNull(message = "数据验证失败,数据名(服务名-架构名)不能为空!", groups = {UpdateGroup.class}) - String databaseName; + String database; @ApiModelProperty(value = "用户名", required = true) @NotNull(message = "数据验证失败,用户名不能为空!", groups = {UpdateGroup.class}) - String username; + String user; @ApiModelProperty(value = "密码", required = true) @NotNull(message = "数据验证失败,密码不能为空!", groups = {UpdateGroup.class}) @@ -39,5 +39,5 @@ public class DatabaseManagement { @ApiModelProperty(value = "表名", required = true) - String tableName; + String table; } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/BaseDataSource.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/BaseDataSource.java index 94232c6..466c7ce 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/BaseDataSource.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/BaseDataSource.java @@ -59,9 +59,12 @@ public class BaseDataSource { * 数据库连接对象 */ protected Connection connection = null; -// protected Statement statement = null; -// protected PreparedStatement preparedStatement = null; -// protected ResultSet resultSet = null; + + /** + * 查询所有数据库的名称 + * queryAllDatabaseName() + */ + protected String queryAllDatabaseNameSql = "SHOW DATABASES;"; /** * 连接数据库 @@ -84,7 +87,6 @@ public class BaseDataSource { * @date 2023/10/30 10:58 */ public void closeAll() { -// close(resultSet, preparedStatement, statement, connection); close(connection); } @@ -273,15 +275,17 @@ public class BaseDataSource { try { DatabaseMetaData metaData = connection.getMetaData(); ResultSet resultSet = metaData.getTables(databaseName, null, null, new String[]{"TABLE"}); - // 循环TABLE,将结果记录在Map中 while (resultSet.next()) { HashMap tableMap = new HashMap<>(); + // 获取表所属的schema + String tableSchema = resultSet.getString("TABLE_SCHEM"); // 表名 String tableName = resultSet.getString("TABLE_NAME"); // 注释 String tableComment = resultSet.getString("REMARKS"); - tableMap.put("tableName",tableName); + tableMap.put("tableSchema",tableSchema); + tableMap.put("table",tableName); tableMap.put("remarks",tableComment); tableList.add(tableMap); } @@ -299,11 +303,16 @@ public class BaseDataSource { * @return */ public List> queryTableFields(String databaseName, String tableName) { - List> resultData = null; + List> resultData; try { DatabaseMetaData metaData = connection.getMetaData(); - // 字段 - ResultSet resultSet = metaData.getColumns(databaseName, null, tableName, null); + List sqlList = StrSplitter.split(tableName, ".", 2, true, true); + String schemaPattern = null; + if (sqlList.size() == 2) { + schemaPattern = sqlList.get(0); + tableName = sqlList.get(1); + } + ResultSet resultSet = metaData.getColumns(databaseName, schemaPattern, tableName, null); resultData = new ArrayList<>(); while (resultSet.next()) { HashMap dataTypeMap = new HashMap<>(); @@ -315,17 +324,6 @@ public class BaseDataSource { int columnSize = resultSet.getInt("COLUMN_SIZE"); // 字段注释 String columnComment = resultSet.getString("REMARKS"); - // 下面是获取关于列级别的信息 - // 获取列的名称:resultSet.getString("COLUMN_NAME") - // 获取列的标签(别名):resultSet.getString("LABEL") - // 获取列的显示大小:resultSet.getInt("COLUMN_DISPLAY_SIZE") - // 获取列的数据类型的编号:resultSet.getInt("DATA_TYPE") - // 获取列的数据类型的名称:resultSet.getString("TYPE_NAME") - // 获取列的精度:resultSet.getInt("PRECISION") - // 获取列的小数位数:resultSet.getInt("SCALE") - // 获取列是否为只读:resultSet.getBoolean("IS_READONLY") - // 获取列是否自动递增:resultSet.getBoolean("IS_AUTOINCREMENT") - dataTypeMap.put("fieldName",columnName); dataTypeMap.put("typeName",dataType); dataTypeMap.put("columnSize",columnSize); @@ -339,12 +337,18 @@ public class BaseDataSource { return resultData; } + /** + * 查询可操作的所有数据库名称 + * + * @return 该账户可操作的所有数据库集 + * @author 王立宏 + * @date 2023/11/23 03:29 + */ public List queryAllDatabaseName() { List databaseNameList = new ArrayList<>(); try { Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SHOW DATABASES;"); - ResultSetMetaData metaData = resultSet.getMetaData(); + ResultSet resultSet = statement.executeQuery(queryAllDatabaseNameSql); while (resultSet.next()) { databaseNameList.add(resultSet.getString(1)); } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDB2.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDB2.java index caf4451..9d3bc33 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDB2.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDB2.java @@ -5,11 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; /** * DB2数据库操作类 diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDM.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDM.java index 6495d35..2de9b80 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDM.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDM.java @@ -5,11 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; /** * DM的数据操作类 diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDoris.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDoris.java index 161a2e9..067ca12 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDoris.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceDoris.java @@ -7,11 +7,8 @@ import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.DataBaseTypeEnum; import supie.webadmin.app.service.databasemanagement.Strategy; import supie.webadmin.app.service.databasemanagement.StrategyFactory; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; /** * 描述: diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceHive.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceHive.java index 04e5392..de9340f 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceHive.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceHive.java @@ -5,11 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; /** * Hive的数据操作类 diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceMySql.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceMySql.java index 37374cc..b10462b 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceMySql.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceMySql.java @@ -9,8 +9,6 @@ import supie.webadmin.app.service.databasemanagement.Strategy; import supie.webadmin.app.service.databasemanagement.StrategyFactory; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; /** * MySql diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceOracle.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceOracle.java index f4c9488..fd69ece 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceOracle.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceOracle.java @@ -5,11 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; /** * Oracle diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourcePostgreSQL.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourcePostgreSQL.java index 75fe251..e82b5e7 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourcePostgreSQL.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourcePostgreSQL.java @@ -5,11 +5,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; import java.util.List; -import java.util.Map; @Slf4j @Component @@ -44,4 +42,17 @@ public class DataSourcePostgreSQL extends BaseDataSource implements Strategy { initConnection(); } + /** + * 查询可操作的所有数据库名称 + * + * @return 该账户可操作的所有数据库集 + * @author 王立宏 + * @date 2023/11/23 03:29 + */ + @Override + public List queryAllDatabaseName() { + queryAllDatabaseNameSql = "SELECT datname FROM pg_database WHERE datistemplate = false;"; + return super.queryAllDatabaseName(); + } + } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSQLServer.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSQLServer.java index c74a2b8..a7b7da7 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSQLServer.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSQLServer.java @@ -5,11 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; @Slf4j @Component diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSapHana.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSapHana.java index dbb856e..72443c2 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSapHana.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSapHana.java @@ -5,11 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; @Slf4j @Component diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSyBase.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSyBase.java index 2862ae8..f682c14 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSyBase.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceSyBase.java @@ -5,11 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; @Slf4j @Component diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceTdSql.java b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceTdSql.java index 33d55f4..2354adb 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceTdSql.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/databasemanagement/strategyImpl/DataSourceTdSql.java @@ -5,11 +5,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import supie.webadmin.app.service.databasemanagement.*; -import supie.webadmin.app.service.databasemanagement.model.DatabaseManagement; import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Map; @Slf4j @Component diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/impl/CustomizeRouteServiceImpl.java b/application-webadmin/src/main/java/supie/webadmin/app/service/impl/CustomizeRouteServiceImpl.java index 3212a73..8bbe315 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/impl/CustomizeRouteServiceImpl.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/impl/CustomizeRouteServiceImpl.java @@ -2,6 +2,7 @@ package supie.webadmin.app.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import org.redisson.api.RedissonClient; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.web.bind.annotation.RequestMethod; @@ -45,6 +46,8 @@ public class CustomizeRouteServiceImpl extends BaseService private CustomizeRouteMapper customizeRouteMapper; @Autowired private IdGeneratorWrapper idGenerator; + @Autowired + private RedissonClient redissonClient; /** * 返回当前Service的主表Mapper对象。 @@ -93,10 +96,22 @@ public class CustomizeRouteServiceImpl extends BaseService @Transactional(rollbackFor = Exception.class) @Override public boolean update(CustomizeRoute customizeRoute, CustomizeRoute originalCustomizeRoute) { + // 如果就数据已经处于上线状态的,则先下线,修改成功后再上线。 + if (originalCustomizeRoute.getState() == null || originalCustomizeRoute.getState() == 1) { + // 下线路由 + unregisterApi(originalCustomizeRoute); + } + unregisterDynamicRouteFromRedis(originalCustomizeRoute.getUrl()); MyModelUtil.fillCommonsForUpdate(customizeRoute, originalCustomizeRoute); // 这里重点提示,在执行主表数据更新之前,如果有哪些字段不支持修改操作,请用原有数据对象字段替换当前数据字段。 UpdateWrapper uw = this.createUpdateQueryForNullValue(customizeRoute, customizeRoute.getId()); - return customizeRouteMapper.update(customizeRoute, uw) == 1; + boolean updateResult = customizeRouteMapper.update(customizeRoute, uw) == 1; + // 修改成功,如果新数据为上线状态则上线路由 + if (updateResult && customizeRoute.getState() != null && customizeRoute.getState() == 1) { + // 上线该路由 + registerApi(customizeRoute); + } + return updateResult; } /** @@ -175,6 +190,19 @@ public class CustomizeRouteServiceImpl extends BaseService return customizeRoute; } + /** + * 从 Redis 注销动态路由信息 + * + * @param originalUrl 原始路由 + * @author 王立宏 + * @date 2023/11/22 04:12 + */ + @Override + public void unregisterDynamicRouteFromRedis(String originalUrl) { + redissonClient.getBucket("CustomizeRoute:" + originalUrl).delete(); + redissonClient.getBucket("CustomizeRouteVerificationInfo:" + originalUrl).delete(); + } + /** * 程序启动成功后注册数据库中状态为上线的路径 * @param args @@ -270,6 +298,7 @@ public class CustomizeRouteServiceImpl extends BaseService MyModelUtil.fillCommonsForUpdate(originalCustomizeRoute, originalCustomizeRoute); UpdateWrapper uw = this.createUpdateQueryForNullValue(originalCustomizeRoute, originalCustomizeRoute.getId()); customizeRouteMapper.update(originalCustomizeRoute, uw); + unregisterDynamicRouteFromRedis(originalCustomizeRoute.getUrl()); } /** @@ -302,4 +331,30 @@ public class CustomizeRouteServiceImpl extends BaseService public List getCustomizeRouteListByExternalAppId(Long externalAppId, CustomizeRoute customizeRouteFilter, String orderBy) { return customizeRouteMapper.getCustomizeRouteListByExternalAppId(externalAppId, customizeRouteFilter, orderBy); } + + /** + * 查询externalAppId关联的CustomizeRoute + * + * @param externalAppId 外部应用 ID + * @return + * @author 王立宏 + * @date 2023/11/22 04:31 + */ + @Override + public List queryAssociatedCustomizeRoute(Long externalAppId) { + return customizeRouteMapper.queryAssociatedCustomizeRoute(externalAppId); + } + + /** + * 通过 外部应用与自定义路由关联信息 查询自定义路由信息 + * + * @param externalAppCustomizeRoute 外部应用自定义路由 + * @return 自定义路由信息集 + * @author 王立宏 + * @date 2023/11/22 05:23 + */ + @Override + public List queryCustomizeRouteByExternalAppCustomizeRoute(ExternalAppCustomizeRoute externalAppCustomizeRoute) { + return customizeRouteMapper.queryCustomizeRouteByExternalAppCustomizeRoute(externalAppCustomizeRoute); + } } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/impl/ExternalAppCustomizeRouteServiceImpl.java b/application-webadmin/src/main/java/supie/webadmin/app/service/impl/ExternalAppCustomizeRouteServiceImpl.java index 08e4b9b..38e4fce 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/impl/ExternalAppCustomizeRouteServiceImpl.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/impl/ExternalAppCustomizeRouteServiceImpl.java @@ -2,6 +2,7 @@ package supie.webadmin.app.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import org.redisson.api.RedissonClient; import supie.webadmin.app.service.*; import supie.webadmin.app.dao.*; import supie.webadmin.app.model.*; @@ -33,6 +34,10 @@ public class ExternalAppCustomizeRouteServiceImpl extends BaseService uw = this.createUpdateQueryForNullValue(externalAppCustomizeRoute, externalAppCustomizeRoute.getId()); @@ -96,6 +107,7 @@ public class ExternalAppCustomizeRouteServiceImpl extends BaseService customizeRouteList = customizeRouteService.queryCustomizeRouteByExternalAppCustomizeRoute(externalAppCustomizeRoute); + for (CustomizeRoute customizeRoute : customizeRouteList) { + String url = customizeRoute.getUrl(); + redissonClient.getBucket("CustomizeRoute:" + url).delete(); + redissonClient.getBucket("CustomizeRouteVerificationInfo:" + url).delete(); + } + } } diff --git a/application-webadmin/src/main/java/supie/webadmin/app/service/impl/ExternalAppServiceImpl.java b/application-webadmin/src/main/java/supie/webadmin/app/service/impl/ExternalAppServiceImpl.java index cf1fce9..f61f1c0 100644 --- a/application-webadmin/src/main/java/supie/webadmin/app/service/impl/ExternalAppServiceImpl.java +++ b/application-webadmin/src/main/java/supie/webadmin/app/service/impl/ExternalAppServiceImpl.java @@ -3,6 +3,7 @@ package supie.webadmin.app.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.*; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import org.redisson.api.RedissonClient; import supie.webadmin.app.service.*; import supie.webadmin.app.dao.*; import supie.webadmin.app.model.*; @@ -38,6 +39,10 @@ public class ExternalAppServiceImpl extends BaseService imple private ExternalAppCustomizeRouteService externalAppCustomizeRouteService; @Autowired private IdGeneratorWrapper idGenerator; + @Autowired + private CustomizeRouteService customizeRouteService; + @Autowired + private RedissonClient redissonClient; /** * 返回当前Service的主表Mapper对象。 @@ -86,6 +91,7 @@ public class ExternalAppServiceImpl extends BaseService imple @Transactional(rollbackFor = Exception.class) @Override public boolean update(ExternalApp externalApp, ExternalApp originalExternalApp) { + unregisterDynamicRouteFromRedis(originalExternalApp.getId()); MyModelUtil.fillCommonsForUpdate(externalApp, originalExternalApp); // 这里重点提示,在执行主表数据更新之前,如果有哪些字段不支持修改操作,请用原有数据对象字段替换当前数据字段。 UpdateWrapper uw = this.createUpdateQueryForNullValue(externalApp, externalApp.getId()); @@ -101,6 +107,7 @@ public class ExternalAppServiceImpl extends BaseService imple @Transactional(rollbackFor = Exception.class) @Override public boolean remove(Long id) { + unregisterDynamicRouteFromRedis(id); if (externalAppMapper.deleteById(id) == 0) { return false; } @@ -241,7 +248,25 @@ public class ExternalAppServiceImpl extends BaseService imple } /** - * 生成 AppKey + * 从 Redis 注销动态路由信息 + * + * @param externalAppId 外部AppId + * @author 王立宏 + * @date 2023/11/22 04:12 + */ + @Override + public void unregisterDynamicRouteFromRedis(Long externalAppId) { + // 查询externalAppId关联的CustomizeRoute + List customizeRouteList = customizeRouteService.queryAssociatedCustomizeRoute(externalAppId); + for (CustomizeRoute customizeRoute : customizeRouteList) { + String url = customizeRoute.getUrl(); + redissonClient.getBucket("CustomizeRoute:" + url).delete(); + redissonClient.getBucket("CustomizeRouteVerificationInfo:" + url).delete(); + } + } + + /** + * 生成 AppKey,并保存至数据库 * * @param externalApp 外部应用程序信息 * @return 外部应用程序 @@ -252,11 +277,23 @@ public class ExternalAppServiceImpl extends BaseService imple public ExternalApp generateAppKey(ExternalApp externalApp) { ExternalApp originalExternalApp = externalApp.clone(); // 生成AppKey,新key覆盖原有key - String appKey = ExternalApp.generateAppKey(null); + String appKey = this.generateAppKey(); externalApp.setAppKey(appKey); if (update(externalApp, originalExternalApp)) { return externalApp; } return null; } + + /** + * 生成 AppKey + * + * @return AppKey + * @author 王立宏 + * @date 2023/11/22 10:02 + */ + @Override + public String generateAppKey() { + return ExternalApp.generateAppKey(null); + } } diff --git a/application-webadmin/src/main/java/supie/webadmin/interceptor/ApiAuthenticationInterceptor.java b/application-webadmin/src/main/java/supie/webadmin/interceptor/ApiAuthenticationInterceptor.java index 1ab66d6..142aa49 100644 --- a/application-webadmin/src/main/java/supie/webadmin/interceptor/ApiAuthenticationInterceptor.java +++ b/application-webadmin/src/main/java/supie/webadmin/interceptor/ApiAuthenticationInterceptor.java @@ -1,42 +1,29 @@ package supie.webadmin.interceptor; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.text.StrFormatter; import cn.hutool.core.util.StrUtil; -import cn.hutool.http.HttpResponse; -import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.TypeReference; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import io.jsonwebtoken.Claims; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.Nullable; import org.redisson.api.RBucket; -import org.redisson.api.RSet; import org.redisson.api.RedissonClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.util.Assert; -import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; -import supie.common.core.annotation.NoAuthInterface; -import supie.common.core.cache.CacheConfig; import supie.common.core.constant.ApplicationConstant; -import supie.common.core.constant.DataPermRuleType; import supie.common.core.constant.ErrorCodeEnum; -import supie.common.core.exception.MyRuntimeException; import supie.common.core.object.ResponseResult; -import supie.common.core.object.TokenData; import supie.common.core.util.ApplicationContextHolder; -import supie.common.core.util.JwtUtil; -import supie.common.core.util.RedisKeyUtil; +import supie.common.core.util.MyCommonUtil; import supie.webadmin.app.dao.CustomizeRouteMapper; +import supie.webadmin.app.dao.ExternalAppMapper; import supie.webadmin.app.model.CustomizeRoute; +import supie.webadmin.app.model.ExternalApp; import supie.webadmin.config.ApplicationConfig; -import supie.webadmin.config.ThirdPartyAuthConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -44,7 +31,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; /** * 描述: @@ -59,84 +45,148 @@ public class ApiAuthenticationInterceptor implements HandlerInterceptor { private final ApplicationConfig appConfig = ApplicationContextHolder.getBean("applicationConfig"); - private final ThirdPartyAuthConfig thirdPartyAuthConfig = - ApplicationContextHolder.getBean("thirdPartyAuthConfig"); - private final RedissonClient redissonClient = ApplicationContextHolder.getBean(RedissonClient.class); - - private final CacheManager cacheManager = ApplicationContextHolder.getBean("caffeineCacheManager"); - + private final CustomizeRouteMapper customizeRouteMapper = ApplicationContextHolder.getBean("customizeRouteMapper"); + private final ExternalAppMapper externalAppMapper = + ApplicationContextHolder.getBean("externalAppMapper"); + + private CustomizeRoute customizeRoute = null; + private List verificationInfoList = null; + @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - // 获取当前的请求所携带的AppKey信息 - String url = request.getRequestURI(); - // 查询当前请求的路由的相关信息 - QueryWrapper customizeRouteQueryWrapper = new QueryWrapper<>(); - customizeRouteQueryWrapper.eq("url", url); - CustomizeRoute customizeRoute = customizeRouteMapper.selectOne(customizeRouteQueryWrapper); - if (customizeRoute == null) return false; -// // 判断相关权限(先查询redis,redis中没有再查询数据库中的权限,并存入redis。TODO 相应的权限有变动修改则删除redis中缓存的权限信息。) -// String customizeRouteRightKey = RedisKeyUtil.makeCustomizeRouteRightKey(customizeRoute.getId()); -// RBucket> customizeRouteRight = redissonClient.getBucket(customizeRouteRightKey); + try { + String url = request.getRequestURI(); + // 获取当前的请求所携带的AppKey信息 + String appKey = this.getTokenFromRequest(request); + List verificationInfoList = getVerificationInfoList(url); + if (StrUtil.isBlank(appKey)) { + // AppKey为空,判断该地址是否为无需验证的地址 + if (verificationInfoList == null) throw new RuntimeException("当前应用没有操作权限,请核对!"); + for (VerificationInfo verificationInfo : verificationInfoList) { + if (verificationInfo.getAuthenticationMethod() != 2) continue; + // 无认证类型 + return setCustomizeRouteDataToRedis(url); + } + throw new RuntimeException("当前应用没有操作权限,请核对!"); + } + // AppKey不为空,进行相关的合法性验证. + // 判断AppKey是否为本系统所发放 + if (!ExternalApp.verifyAppKey(appKey)) { + String warnMessage = StrFormatter.format( + "IP[{}]通过[{}]端请求[{}]动态路由接口携带了非本系统发放的AppKey({})信息!", + MyCommonUtil.getClientIpAddress(), MyCommonUtil.getDeviceTypeByUserAgent(), url, appKey); + log.warn(warnMessage); + throw new RuntimeException("所携带的验证信息不合法!"); + } + // AppKey合法,对比是否为该url所拥有的AppKey。 + for (VerificationInfo verificationInfo : verificationInfoList) { + if (verificationInfo.getAuthenticationMethod() == 1 && appKey.equals(verificationInfo.getAppKey())) { + return setCustomizeRouteDataToRedis(url); + } else if (verificationInfo.getAuthenticationMethod() == 2) { + // 无认证类型 + return setCustomizeRouteDataToRedis(url); + } + } + throw new RuntimeException("AppKey认证失败,当前AppKey无权访问该路由地址!"); + } catch (Exception e) { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + this.outputResponseMessage(response, ResponseResult.error(ErrorCodeEnum.NO_OPERATION_PERMISSION, e.getMessage())); + return false; + } + } - // 权限判断通过,自定义路由信息存储至redis,以供业务代码获取 - RBucket customizeRouteData = redissonClient.getBucket("CustomizeRoute:" + url); - customizeRouteData.set(JSONUtil.toJsonStr(customizeRoute)); - customizeRouteData.expire(30, TimeUnit.SECONDS); + /** + * 获取验证信息. + * 先从redis获取,若redis中不存在则从数据库中查找并存入redis. + * TODO 相应的权限有变动修改则删除redis中缓存的权限信息。 + * + * @param url 路由地址 + * @return 验证信息集 + * @author 王立宏 + * @date 2023/11/22 11:41 + */ + @Nullable + private List getVerificationInfoList(String url) { + if (verificationInfoList != null) return verificationInfoList; + // 获取redis中存储的信息,若redis中不存在则从数据库获取并存储至redis。 + RBucket verificationInfoListRBucket = redissonClient.getBucket("CustomizeRouteVerificationInfo:" + url); + if (!verificationInfoListRBucket.isExists()) { + // 不存在于redis中,从数据库查询相关信息并存储至redis。 + List externalAppList = externalAppMapper.queryExternalAppByUrl(url); + if (externalAppList.size() == 0) throw new RuntimeException("该路由未关联任何外部App!"); + verificationInfoList = externalAppListToVerificationInfoList(externalAppList); + verificationInfoListRBucket.set(JSONUtil.toJsonStr(verificationInfoList)); + verificationInfoListRBucket.expire(appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS); + } else { + verificationInfoList = JSONUtil.toList(verificationInfoListRBucket.get(), VerificationInfo.class); + } + return verificationInfoList; + } + + /** + * 外部应用集合 转为 验证信息集合 + * + * @param externalAppList 外部应用集 + * @return 列表<验证信息> + * @author 王立宏 + * @date 2023/11/22 11:30 + */ + private List externalAppListToVerificationInfoList(List externalAppList) { + List verificationInfoList = new ArrayList<>(); + for (ExternalApp externalApp : externalAppList) { + verificationInfoList.add( + new VerificationInfo(externalApp.getId(), externalApp.getAppKey(), externalApp.getAuthenticationMethod()) + ); + } + // 清理 + return verificationInfoList; + } + + /** + * 权限判断通过,自定义路由信息存储至redis,以供业务代码获取 + * + * @param url 路由地址 + * @author 王立宏 + * @date 2023/11/22 10:44 + */ + private Boolean setCustomizeRouteDataToRedis(String url) { + CustomizeRoute customizeRoute = getCustomizeRoute(url); + if (customizeRoute == null) throw new RuntimeException("当前的路由相关信息获取失败,请联系管理员!"); + this.customizeRoute = null; + this.verificationInfoList = null; return true; + } -// String token = this.getTokenFromRequest(request); -// boolean noLoginUrl = this.isNoAuthInterface(handler); -// // 如果接口方法标记NoAuthInterface注解,可以直接跳过Token鉴权验证,这里主要为了测试接口方便 -// if (noLoginUrl && StrUtil.isBlank(token)) { -// return true; -// } -// String appCode = this.getAppCodeFromRequest(request); -// if (StrUtil.isNotBlank(appCode)) { -// return this.handleThirdPartyRequest(appCode, token, url, response); -// } -// Claims c = JwtUtil.parseToken(token, appConfig.getTokenSigningKey()); -// if (JwtUtil.isNullOrExpired(c)) { -// // 如果免登陆接口携带的是过期的Token,这个时候直接返回给Controller即可。 -// // 这样可以规避不必要的重新登录,而对于Controller,可以将本次请求视为未登录用户的请求。 -// if (noLoginUrl) { -// return true; -// } -// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); -// this.outputResponseMessage(response, -// ResponseResult.error(ErrorCodeEnum.UNAUTHORIZED_LOGIN, "用户会话已过期或尚未登录,请重新登录!")); -// return false; -// } -// String sessionId = (String) c.get("sessionId"); -// String sessionIdKey = RedisKeyUtil.makeSessionIdKey(sessionId); -// RBucket sessionData = redissonClient.getBucket(sessionIdKey); -// TokenData tokenData = null; -// if (sessionData.isExists()) { -// tokenData = JSON.parseObject(sessionData.get(), TokenData.class); -// } -// if (tokenData == null) { -// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); -// this.outputResponseMessage(response, -// ResponseResult.error(ErrorCodeEnum.UNAUTHORIZED_LOGIN, "用户会话已失效,请重新登录!")); -// return false; -// } -// tokenData.setToken(token); -// TokenData.addToRequest(tokenData); -// // 如果url是免登陆、白名单中,则不需要进行鉴权操作 -// if (!noLoginUrl && Boolean.FALSE.equals(tokenData.getIsAdmin()) && !this.hasPermission(sessionId, url)) { -// response.setStatus(HttpServletResponse.SC_FORBIDDEN); -// this.outputResponseMessage(response, ResponseResult.error(ErrorCodeEnum.NO_OPERATION_PERMISSION)); -// return false; -// } -// if (JwtUtil.needToRefresh(c)) { -// String refreshedToken = JwtUtil.generateToken(c, appConfig.getExpiration(), appConfig.getTokenSigningKey()); -// response.addHeader(appConfig.getRefreshedTokenHeaderKey(), refreshedToken); -// } -// return true; + /** + * 查询当前请求的路由的相关信息,并存储至redis中 + * + * @param url 网址 + * @author 王立宏 + * @date 2023/11/22 10:58 + */ + private CustomizeRoute getCustomizeRoute(String url) { + if (this.customizeRoute != null) return this.customizeRoute; + // 先查询redis中是否存在。不存在则从数据库中查询,并将其存储至redis中。 + // 若 CustomizeRoute 有修改、删除则同步删除数据库中存储的数据 及其 注册的路由信息。 + RBucket customizeRouteRBucket = redissonClient.getBucket("CustomizeRoute:" + url); + if (customizeRouteRBucket.isExists()) { + // 存在于redis中,判断是否已经在当前内存中存在,不存在则从redis中取出来 + this.customizeRoute = JSONUtil.toBean(customizeRouteRBucket.get(), CustomizeRoute.class); + } else { + // 不存在于redis中,从数据库中查找到相关数据,并存储至redia中 + QueryWrapper customizeRouteQueryWrapper = new QueryWrapper<>(); + customizeRouteQueryWrapper.eq("url", url); + this.customizeRoute = customizeRouteMapper.selectOne(customizeRouteQueryWrapper); + if (this.customizeRoute == null) throw new RuntimeException("未查找到该路由下相关信息!请联系管理员"); + customizeRouteRBucket.set(JSONUtil.toJsonStr(this.customizeRoute)); + customizeRouteRBucket.expire(appConfig.getSessionExpiredSeconds(), TimeUnit.SECONDS); + } + return this.customizeRoute; } private String getTokenFromRequest(HttpServletRequest request) { @@ -150,193 +200,6 @@ public class ApiAuthenticationInterceptor implements HandlerInterceptor { return token; } - private String getAppCodeFromRequest(HttpServletRequest request) { - String token = request.getHeader("AppCode"); - if (StrUtil.isBlank(token)) { - token = request.getParameter("AppCode"); - } - return token; - } - - @SuppressWarnings("unchecked") - private boolean hasPermission(String sessionId, String url) { - // 为了提升效率,先检索Caffeine的一级缓存,如果不存在,再检索Redis的二级缓存,并将结果存入一级缓存。 - Set localPermSet; - String permKey = RedisKeyUtil.makeSessionPermIdKey(sessionId); - Cache cache = cacheManager.getCache(CacheConfig.CacheEnum.USER_PERMISSION_CACHE.name()); - Assert.notNull(cache, "Cache USER_PERMISSION_CACHE can't be NULL."); - Cache.ValueWrapper wrapper = cache.get(permKey); - if (wrapper == null) { - RSet permSet = redissonClient.getSet(permKey); - localPermSet = permSet.readAll(); - cache.put(permKey, localPermSet); - } else { - localPermSet = (Set) wrapper.get(); - } - return CollUtil.contains(localPermSet, url); - } - - private boolean isNoAuthInterface(Object handler) { - if (handler instanceof HandlerMethod) { - HandlerMethod hm = (HandlerMethod) handler; - return hm.getBeanType().getAnnotation(NoAuthInterface.class) != null - || hm.getMethodAnnotation(NoAuthInterface.class) != null; - } - return false; - } - - private boolean handleThirdPartyRequest(String appCode, String token, String url, HttpServletResponse response) { - ThirdPartyAuthConfig.AuthProperties authProps = thirdPartyAuthConfig.getApplicationMap().get(appCode); - if (authProps == null) { - String msg = StrFormatter.format("请求的 appCode[{}] 信息,在当前服务中尚未配置!", appCode); - this.outputResponseMessage(response, ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, msg)); - return false; - } - ResponseResult responseResult = this.getAndCacheThirdPartyTokenData(authProps, token); - if (!responseResult.isSuccess()) { - this.outputResponseMessage(response, - ResponseResult.error(ErrorCodeEnum.UNAUTHORIZED_LOGIN, responseResult.getErrorMessage())); - return false; - } - TokenData tokenData = responseResult.getData(); - tokenData.setAppCode(appCode); - tokenData.setSessionId(this.prependAppCode(authProps.getAppCode(), tokenData.getSessionId())); - TokenData.addToRequest(tokenData); - if (Boolean.FALSE.equals(tokenData.getIsAdmin()) - && !this.hasThirdPartyPermission(authProps, tokenData, url)) { - response.setStatus(HttpServletResponse.SC_FORBIDDEN); - this.outputResponseMessage(response, ResponseResult.error(ErrorCodeEnum.NO_OPERATION_PERMISSION)); - return false; - } - return true; - } - - private ResponseResult getAndCacheThirdPartyTokenData( - ThirdPartyAuthConfig.AuthProperties authProps, String token) { - if (authProps.getTokenExpiredSeconds() == 0) { - return this.getThirdPartyTokenData(authProps, token); - } - String tokeKey = this.prependAppCode(authProps.getAppCode(), RedisKeyUtil.makeSessionIdKey(token)); - RBucket sessionData = redissonClient.getBucket(tokeKey); - if (sessionData.isExists()) { - return ResponseResult.success(JSON.parseObject(sessionData.get(), TokenData.class)); - } - ResponseResult responseResult = this.getThirdPartyTokenData(authProps, token); - if (responseResult.isSuccess()) { - sessionData.set(JSON.toJSONString(responseResult.getData()), authProps.getTokenExpiredSeconds(), TimeUnit.SECONDS); - } - return responseResult; - } - - private String prependAppCode(String appCode, String key) { - return appCode.toUpperCase() + ":" + key; - } - - private ResponseResult getThirdPartyTokenData( - ThirdPartyAuthConfig.AuthProperties authProps, String token) { - try { - String resultData = this.invokeThirdPartyUrl(authProps.getBaseUrl() + "/getTokenData", token); - return JSON.parseObject(resultData, new TypeReference>() {}); - } catch (MyRuntimeException ex) { - return ResponseResult.error(ErrorCodeEnum.FAILED_TO_INVOKE_THIRDPARTY_URL, ex.getMessage()); - } - } - - private ResponseResult getThirdPartyPermData( - ThirdPartyAuthConfig.AuthProperties authProps, String token) { - try { - String resultData = this.invokeThirdPartyUrl(authProps.getBaseUrl() + "/getPermData", token); - return JSON.parseObject(resultData, new TypeReference>() {}); - } catch (MyRuntimeException ex) { - return ResponseResult.error(ErrorCodeEnum.FAILED_TO_INVOKE_THIRDPARTY_URL, ex.getMessage()); - } - } - - private String invokeThirdPartyUrl(String url, String token) { - Map headerMap = new HashMap<>(1); - headerMap.put("Authorization", token); - StringBuilder fullUrl = new StringBuilder(128); - fullUrl.append(url).append("?token=").append(token); - HttpResponse httpResponse = HttpUtil.createGet(fullUrl.toString()).addHeaders(headerMap).execute(); - if (!httpResponse.isOk()) { - String msg = StrFormatter.format( - "Failed to call [{}] with ERROR HTTP Status [{}] and [{}].", - url, httpResponse.getStatus(), httpResponse.body()); - log.error(msg); - throw new MyRuntimeException(msg); - } - return httpResponse.body(); - } - - @SuppressWarnings("unchecked") - private boolean hasThirdPartyPermission( - ThirdPartyAuthConfig.AuthProperties authProps, TokenData tokenData, String url) { - // 为了提升效率,先检索Caffeine的一级缓存,如果不存在,再检索Redis的二级缓存,并将结果存入一级缓存。 - String permKey = RedisKeyUtil.makeSessionPermIdKey(tokenData.getSessionId()); - Cache cache = cacheManager.getCache(CacheConfig.CacheEnum.USER_PERMISSION_CACHE.name()); - Assert.notNull(cache, "Cache USER_PERMISSION_CACHE can't be NULL"); - Cache.ValueWrapper wrapper = cache.get(permKey); - if (wrapper != null) { - Object cachedData = wrapper.get(); - if (cachedData != null) { - return ((Set) cachedData).contains(url); - } - } - Set localPermSet; - RSet permSet = redissonClient.getSet(permKey); - if (permSet.isExists()) { - localPermSet = permSet.readAll(); - cache.put(permKey, localPermSet); - return localPermSet.contains(url); - } - ResponseResult responseResult = this.getThirdPartyPermData(authProps, tokenData.getToken()); - this.cacheThirdPartyDataPermData(authProps, tokenData, responseResult.getData().getDataPerms()); - if (CollUtil.isEmpty(responseResult.getData().urlPerms)) { - return false; - } - permSet.addAll(responseResult.getData().urlPerms); - permSet.expire(authProps.getPermExpiredSeconds(), TimeUnit.SECONDS); - localPermSet = new HashSet<>(responseResult.getData().urlPerms); - cache.put(permKey, localPermSet); - return localPermSet.contains(url); - } - - private void cacheThirdPartyDataPermData( - ThirdPartyAuthConfig.AuthProperties authProps, TokenData tokenData, List dataPerms) { - if (CollUtil.isEmpty(dataPerms)) { - return; - } - Map> dataPermMap = - dataPerms.stream().collect(Collectors.groupingBy(AuthenticationInterceptor.ThirdPartyAppDataPermData::getRuleType)); - Map> normalizedDataPermMap = new HashMap<>(dataPermMap.size()); - for (Map.Entry> entry : dataPermMap.entrySet()) { - List ruleTypeDataPermDataList; - if (entry.getKey().equals(DataPermRuleType.TYPE_DEPT_AND_CHILD_DEPT)) { - ruleTypeDataPermDataList = - normalizedDataPermMap.computeIfAbsent(DataPermRuleType.TYPE_CUSTOM_DEPT_LIST, k -> new LinkedList<>()); - } else { - ruleTypeDataPermDataList = - normalizedDataPermMap.computeIfAbsent(entry.getKey(), k -> new LinkedList<>()); - } - ruleTypeDataPermDataList.addAll(entry.getValue()); - } - Map resultDataPermMap = new HashMap<>(normalizedDataPermMap.size()); - for (Map.Entry> entry : normalizedDataPermMap.entrySet()) { - if (entry.getKey().equals(DataPermRuleType.TYPE_CUSTOM_DEPT_LIST)) { - String deptIds = entry.getValue().stream() - .map(AuthenticationInterceptor.ThirdPartyAppDataPermData::getDeptIds).collect(Collectors.joining(",")); - resultDataPermMap.put(entry.getKey(), deptIds); - } else { - resultDataPermMap.put(entry.getKey(), "null"); - } - } - Map> menuDataPermMap = new HashMap<>(1); - menuDataPermMap.put(ApplicationConstant.DATA_PERM_ALL_MENU_ID, resultDataPermMap); - String dataPermSessionKey = RedisKeyUtil.makeSessionDataPermIdKey(tokenData.getSessionId()); - RBucket bucket = redissonClient.getBucket(dataPermSessionKey); - bucket.set(JSON.toJSONString(menuDataPermMap), authProps.getPermExpiredSeconds(), TimeUnit.SECONDS); - } - @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { @@ -364,28 +227,21 @@ public class ApiAuthenticationInterceptor implements HandlerInterceptor { } @Data - public static class ThirdPartyAppPermData { + @NoArgsConstructor + @AllArgsConstructor + public class VerificationInfo { /** - * 当前用户会话可访问的url接口地址列表。 + * ExternalAppId */ - private List urlPerms; - /** - * 当前用户会话的数据权限列表。 - */ - private List dataPerms; - } - - @Data - public static class ThirdPartyAppDataPermData { + private Long externalAppId; /** - * 数据权限的规则类型。需要按照橙单的约定返回。具体值可参考DataPermRuleType常量类。 + * AppKey */ - private Integer ruleType; + private String appKey; /** - * 部门Id集合,多个部门Id之间逗号分隔。 - * 注意:仅当ruleType为3、4、5时需要包含该字段值。 + * 认证方式(1:key认证。2:无认证)。 */ - private String deptIds; + private Integer authenticationMethod; } } diff --git a/common/common-core/src/main/java/supie/common/core/util/MyCommonUtil.java b/common/common-core/src/main/java/supie/common/core/util/MyCommonUtil.java index d34cf70..db11403 100644 --- a/common/common-core/src/main/java/supie/common/core/util/MyCommonUtil.java +++ b/common/common-core/src/main/java/supie/common/core/util/MyCommonUtil.java @@ -273,6 +273,82 @@ public class MyCommonUtil { return deviceType; } + /** + * 按(User-Agent)获取设备类型 + * + * @return 字符串 + * @author 王立宏 + * @date 2023/11/22 02:34 + */ + public static String getDeviceTypeByUserAgent() { + String userAgent = ContextUtil.getHttpRequest().getHeader("User-Agent"); + if (userAgent != null) { + userAgent = userAgent.toLowerCase(); + if (userAgent.contains("android")) { + return "Android"; + } else if (userAgent.contains("iphone") || userAgent.contains("ipad")) { + return "IOS"; + } else if (userAgent.contains("windows phone")) { + return "Windows Phone"; + } else if (userAgent.contains("windows")) { + return "Windows"; + } else { + return "Unknown device type"; + } + } else { + return "Unknown device type"; + } + } + + /** + * 获取客户端 IP 地址 + * + * @return IP 地址 + * @author 王立宏 + * @date 2023/11/22 02:32 + */ + public static String getClientIpAddress() { + String ipAddress = ContextUtil.getHttpRequest().getHeader("X-Forwarded-For"); + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("Proxy-Client-IP"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("WL-Proxy-Client-IP"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("HTTP_X_FORWARDED_FOR"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("HTTP_X_FORWARDED"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("HTTP_X_CLUSTER_CLIENT_IP"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("HTTP_CLIENT_IP"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("HTTP_FORWARDED_FOR"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("HTTP_FORWARDED"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("HTTP_VIA"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getHeader("REMOTE_ADDR"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = ContextUtil.getHttpRequest().getRemoteAddr(); + } + // 如果 IP 地址包含多个值,则提取第一个值 + if (ipAddress != null && ipAddress.contains(",")) { + ipAddress = ipAddress.split(",")[0].trim(); + } + return ipAddress; + } + /** * 转换为字典格式的数据列表。 * diff --git a/pom.xml b/pom.xml index 79d89b0..5583288 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,31 @@ + + + org.postgresql + postgresql + 42.7.0 + + + com.oracle.database.jdbc + ojdbc8 + 23.3.0.23.09 + + + + + com.h2database + h2 + 2.2.224 + + + + com.microsoft.sqlserver + mssql-jdbc + 12.5.0.jre11-preview + + org.springframework.boot -- Gitee