适配eureka

pull/1/head
tanghc 5 years ago
parent 51ca5ba319
commit be0adc0a8e
  1. 2
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/IPBlacklistApi.java
  2. 6
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitNewApi.java
  3. 105
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/RouteApi.java
  4. 10
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java
  5. 47
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/RouteVO.java
  6. 22
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/GatewayPushDTO.java
  7. 69
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigServiceRoute.java
  8. 18
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/RouteRoleDTO.java
  9. 11
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/ConfigServiceRouteMapper.java
  10. 5
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/PermRolePermissionMapper.java
  11. 58
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java
  12. 52
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/RouteService.java
  13. 5
      sop-admin/sop-admin-server/src/main/resources/application-dev.properties
  14. 19
      sop-admin/sop-admin-server/src/main/resources/mybatis/mapper/PermRolePermissionMapper.xml
  15. 6
      sop-common/sop-gateway-common/pom.xml
  16. 9
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ChannelMsg.java
  17. 22
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/GatewayPushDTO.java
  18. 25
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/AbstractConfiguration.java
  19. 5
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ServiceRoutesLoader.java
  20. 154
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/BaseRoutesListener.java
  21. 34
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/EurekaRoutesListener.java
  22. 80
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosRoutesListener.java
  23. 26
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RegistryListener.java
  24. 24
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/RoutesProcessor.java
  25. 43
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/support/EurekaListenerConfiguration.java
  26. 12
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java
  27. 68
      sop-gateway/src/main/java/com/gitee/sop/gateway/controller/ConfigChannelController.java
  28. 71
      sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigServiceRoute.java
  29. 10
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/ChannelMsgProcessor.java
  30. 39
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbEnvGrayManager.java
  31. 38
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbIPBlacklistManager.java
  32. 37
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbIsvManager.java
  33. 61
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbIsvRoutePermissionManager.java
  34. 33
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbLimitConfigManager.java
  35. 32
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java
  36. 68
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java
  37. 11
      sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/ConfigServiceRouteMapper.java

@ -86,7 +86,7 @@ public class IPBlacklistApi {
} }
} }
public void sendIpBlacklistMsg(ConfigIpBlacklist configIpBlacklist, ChannelOperation channelOperation) throws Exception { public void sendIpBlacklistMsg(ConfigIpBlacklist configIpBlacklist, ChannelOperation channelOperation) {
ChannelMsg channelMsg = new ChannelMsg(channelOperation, configIpBlacklist); ChannelMsg channelMsg = new ChannelMsg(channelOperation, configIpBlacklist);
configPushService.publishConfig(NacosConfigs.DATA_ID_IP_BLACKLIST, NacosConfigs.GROUP_CHANNEL, channelMsg); configPushService.publishConfig(NacosConfigs.DATA_ID_IP_BLACKLIST, NacosConfigs.GROUP_CHANNEL, channelMsg);
} }

@ -18,7 +18,6 @@ import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.entity.ConfigLimit; import com.gitee.sop.adminserver.entity.ConfigLimit;
import com.gitee.sop.adminserver.mapper.ConfigLimitMapper; import com.gitee.sop.adminserver.mapper.ConfigLimitMapper;
import com.gitee.sop.adminserver.service.RouteConfigService; import com.gitee.sop.adminserver.service.RouteConfigService;
import com.gitee.sop.adminserver.service.RouteService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -33,9 +32,6 @@ import org.springframework.transaction.annotation.Transactional;
@Slf4j @Slf4j
public class LimitNewApi { public class LimitNewApi {
@Autowired
RouteService routeService;
@Autowired @Autowired
RouteConfigService routeConfigService; RouteConfigService routeConfigService;
@ -44,7 +40,7 @@ public class LimitNewApi {
@Api(name = "config.limit.list") @Api(name = "config.limit.list")
@ApiDocMethod(description = "限流列表(新)", elementClass = LimitNewVO.class) @ApiDocMethod(description = "限流列表(新)", elementClass = LimitNewVO.class)
PageInfo<ConfigLimit> listLimit(LimitNewParam param) throws Exception { PageInfo<ConfigLimit> listLimit(LimitNewParam param) {
Query query = Query.build(param); Query query = Query.build(param);
query.orderby("route_id", Sort.ASC) query.orderby("route_id", Sort.ASC)
.orderby("app_key", Sort.ASC) .orderby("app_key", Sort.ASC)

@ -4,6 +4,10 @@ import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService; import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc; import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod; import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.support.PageEasyui;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.isv.result.RoleVO; import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.api.service.param.RouteAddParam; import com.gitee.sop.adminserver.api.service.param.RouteAddParam;
import com.gitee.sop.adminserver.api.service.param.RouteDeleteParam; import com.gitee.sop.adminserver.api.service.param.RouteDeleteParam;
@ -14,19 +18,24 @@ import com.gitee.sop.adminserver.api.service.result.RouteVO;
import com.gitee.sop.adminserver.bean.RouteConfigDto; import com.gitee.sop.adminserver.bean.RouteConfigDto;
import com.gitee.sop.adminserver.common.BizException; import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.entity.ConfigRouteBase; import com.gitee.sop.adminserver.entity.ConfigRouteBase;
import com.gitee.sop.adminserver.entity.ConfigServiceRoute;
import com.gitee.sop.adminserver.entity.PermRole; import com.gitee.sop.adminserver.entity.PermRole;
import com.gitee.sop.adminserver.entity.PermRolePermission;
import com.gitee.sop.adminserver.entity.RouteRoleDTO;
import com.gitee.sop.adminserver.mapper.ConfigRouteBaseMapper; import com.gitee.sop.adminserver.mapper.ConfigRouteBaseMapper;
import com.gitee.sop.adminserver.mapper.ConfigServiceRouteMapper;
import com.gitee.sop.adminserver.mapper.PermRoleMapper; import com.gitee.sop.adminserver.mapper.PermRoleMapper;
import com.gitee.sop.adminserver.mapper.PermRolePermissionMapper; import com.gitee.sop.adminserver.mapper.PermRolePermissionMapper;
import com.gitee.sop.adminserver.service.RouteConfigService; import com.gitee.sop.adminserver.service.RouteConfigService;
import com.gitee.sop.adminserver.service.RoutePermissionService; import com.gitee.sop.adminserver.service.RoutePermissionService;
import com.gitee.sop.adminserver.service.RouteService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -46,48 +55,56 @@ public class RouteApi {
@Autowired @Autowired
ConfigRouteBaseMapper configRouteBaseMapper; ConfigRouteBaseMapper configRouteBaseMapper;
@Autowired
private ConfigServiceRouteMapper configServiceRouteMapper;
@Autowired @Autowired
RoutePermissionService routePermissionService; RoutePermissionService routePermissionService;
@Autowired @Autowired
RouteConfigService routeConfigService; RouteConfigService routeConfigService;
@Autowired @ApiDocMethod(description = "路由列表,分页")
RouteService routeService; @Api(name = "route.page")
PageEasyui<RouteVO> page(RouteSearchParam form) {
Query query = Query.build(form);
query.orderby("id", Sort.ASC);
PageEasyui<RouteVO> datagrid = MapperUtil.queryForEasyuiDatagrid(configServiceRouteMapper, query, RouteVO.class);
List<String> routeIdList = datagrid.getRows()
.parallelStream()
.map(RouteVO::getId)
.collect(Collectors.toList());
@Api(name = "route.list") Map<String, Byte> routeIdStatusMap = configRouteBaseMapper
@ApiDocMethod(description = "路由列表") .list(new Query().in("route_id", routeIdList))
List<RouteVO> listRoute(RouteSearchParam param) throws Exception {
List<RouteVO> routeDefinitionList = routeService.getRouteDefinitionList(param)
.stream() .stream()
.map(gatewayRouteDefinition -> { .collect(Collectors.toMap(ConfigRouteBase::getRouteId, ConfigRouteBase::getStatus));
RouteVO vo = new RouteVO();
BeanUtils.copyProperties(gatewayRouteDefinition, vo); Map<String, List<RouteRoleDTO>> routeIdRoleMap = permRolePermissionMapper.listRouteRole(routeIdList)
vo.setRoles(this.getRouteRole(gatewayRouteDefinition.getId())); .parallelStream()
ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", gatewayRouteDefinition.getId()); .collect(Collectors.groupingBy(RouteRoleDTO::getRouteId));
if (configRouteBase != null) {
vo.setStatus(configRouteBase.getStatus());
}
return vo;
})
.collect(Collectors.toList());
return routeDefinitionList; datagrid.getRows().forEach(vo -> {
String routeId = vo.getId();
List<RouteRoleDTO> routeRoleDTOS = routeIdRoleMap.getOrDefault(routeId, Collections.emptyList());
vo.setRoles(routeRoleDTOS);
Byte status = routeIdStatusMap.getOrDefault(routeId, vo.getStatus());
vo.setStatus(status);
});
return datagrid;
} }
@Api(name = "route.list", version = "1.2") @Api(name = "route.list", version = "1.2")
@ApiDocMethod(description = "路由列表1.2") @ApiDocMethod(description = "路由列表1.2")
List<RouteVO> listRoute2(RouteSearchParam param) throws Exception { List<ConfigServiceRoute> listRoute2(RouteSearchParam param) {
List<RouteVO> routeDefinitionList = routeService.getRouteDefinitionList(param) String serviceId = param.getServiceId();
.stream() if (StringUtils.isBlank(serviceId)) {
.map(gatewayRouteDefinition -> { return Collections.emptyList();
RouteVO vo = new RouteVO(); }
BeanUtils.copyProperties(gatewayRouteDefinition, vo); Query query = Query.build(param);
return vo; return configServiceRouteMapper.list(query);
})
.collect(Collectors.toList());
return routeDefinitionList;
} }
@Api(name = "route.add") @Api(name = "route.add")
@ -149,6 +166,34 @@ public class RouteApi {
/** /**
* 获取路由对应的角色 * 获取路由对应的角色
*
* @param routeIdList routeIdList
* @return
*/
private List<RoleVO> getRouteRole(List<String> routeIdList) {
// key:routeId, value: roleCode
Map<String, List<String>> routeIdRoleCodeMap = permRolePermissionMapper.list(new Query().in("route_id", routeIdList))
.stream()
.collect(Collectors.groupingBy(PermRolePermission::getRouteId,
Collectors.mapping(PermRolePermission::getRoleCode, Collectors.toList())));
return permRolePermissionMapper.list(new Query().in("route_id", routeIdList))
.stream()
.map(permRolePermission -> {
RoleVO vo = new RoleVO();
String roleCode = permRolePermission.getRoleCode();
PermRole permRole = permRoleMapper.getByColumn("role_code", roleCode);
BeanUtils.copyProperties(permRole, vo);
return vo;
})
.collect(Collectors.toList());
}
/**
* 获取路由对应的角色
*
* @param id routeId * @param id routeId
* @return * @return
*/ */

@ -1,6 +1,9 @@
package com.gitee.sop.adminserver.api.service.param; package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField; import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -11,11 +14,16 @@ import javax.validation.constraints.NotBlank;
*/ */
@Getter @Getter
@Setter @Setter
public class RouteSearchParam { public class RouteSearchParam extends PageParam {
@ApiDocField(description = "服务名serviceId") @ApiDocField(description = "服务名serviceId")
@NotBlank(message = "serviceId不能为空") @NotBlank(message = "serviceId不能为空")
private String serviceId; private String serviceId;
@ApiDocField(description = "路由id") @ApiDocField(description = "路由id")
@Condition(column = "id", operator = Operator.like)
private String id; private String id;
@ApiDocField(description = "是否授权接口,1:是")
@Condition(ignoreValue = "0")
private Integer permission;
} }

@ -1,7 +1,6 @@
package com.gitee.sop.adminserver.api.service.result; package com.gitee.sop.adminserver.api.service.result;
import com.gitee.sop.adminserver.api.isv.result.RoleVO; import com.gitee.sop.adminserver.entity.RouteRoleDTO;
import com.gitee.sop.adminserver.bean.RouteDefinition;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -12,6 +11,46 @@ import java.util.List;
*/ */
@Getter @Getter
@Setter @Setter
public class RouteVO extends RouteDefinition { public class RouteVO {
private List<RoleVO> roles;
/** 数据库字段:id */
private String id;
/** 数据库字段:service_id */
private String serviceId;
/** 接口名, 数据库字段:name */
private String name;
/** 版本号, 数据库字段:version */
private String version;
/** 路由断言(SpringCloudGateway专用), 数据库字段:predicates */
private String predicates;
/** 路由过滤器(SpringCloudGateway专用), 数据库字段:filters */
private String filters;
/** 路由规则转发的目标uri, 数据库字段:uri */
private String uri;
/** uri后面跟的path, 数据库字段:path */
private String path;
/** 路由执行的顺序, 数据库字段:order */
private Integer order;
/** 是否忽略验证,业务参数验证除外, 数据库字段:ignore_validate */
private Byte ignoreValidate;
/** 状态,0:待审核,1:启用,2:禁用, 数据库字段:status */
private Byte status;
/** 是否合并结果, 数据库字段:merge_result */
private Byte mergeResult;
/** 是否需要授权才能访问, 数据库字段:permission */
private Byte permission;
private List<RouteRoleDTO> roles;
} }

@ -0,0 +1,22 @@
package com.gitee.sop.adminserver.bean;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class GatewayPushDTO {
private String dataId;
private String groupId;
private ChannelMsg channelMsg;
public GatewayPushDTO() {
}
public GatewayPushDTO(String dataId, String groupId, ChannelMsg channelMsg) {
this.dataId = dataId;
this.groupId = groupId;
this.channelMsg = channelMsg;
}
}

@ -0,0 +1,69 @@
package com.gitee.sop.adminserver.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* 表名config_service_route
* 备注路由配置
*
* @author tanghc
*/
@Table(name = "config_service_route")
@Data
public class ConfigServiceRoute {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
/** 数据库字段:id */
private String id;
/** 数据库字段:service_id */
private String serviceId;
/** 接口名, 数据库字段:name */
private String name;
/** 版本号, 数据库字段:version */
private String version;
/** 路由断言(SpringCloudGateway专用), 数据库字段:predicates */
private String predicates;
/** 路由过滤器(SpringCloudGateway专用), 数据库字段:filters */
private String filters;
/** 路由规则转发的目标uri, 数据库字段:uri */
private String uri;
/** uri后面跟的path, 数据库字段:path */
private String path;
/** 路由执行的顺序, 数据库字段:order */
private Integer order;
/** 是否忽略验证,业务参数验证除外, 数据库字段:ignore_validate */
private Byte ignoreValidate;
/** 状态,0:待审核,1:启用,2:禁用, 数据库字段:status */
private Byte status;
/** 是否合并结果, 数据库字段:merge_result */
private Byte mergeResult;
/** 是否需要授权才能访问, 数据库字段:permission */
private Byte permission;
/** 数据库字段:gmt_create */
private Date gmtCreate;
/** 数据库字段:gmt_modified */
private Date gmtModified;
}

@ -0,0 +1,18 @@
package com.gitee.sop.adminserver.entity;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class RouteRoleDTO {
/** routeId, 数据库字段:route_id */
private String routeId;
/** 角色代码, 数据库字段:role_code */
private String roleCode;
/** 角色描述, 数据库字段:description */
private String description;
}

@ -0,0 +1,11 @@
package com.gitee.sop.adminserver.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.adminserver.entity.ConfigServiceRoute;
/**
* @author tanghc
*/
public interface ConfigServiceRouteMapper extends CrudMapper<ConfigServiceRoute, Long> {
}

@ -3,10 +3,15 @@ package com.gitee.sop.adminserver.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper; import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.adminserver.entity.PermRolePermission; import com.gitee.sop.adminserver.entity.PermRolePermission;
import com.gitee.sop.adminserver.entity.RouteRoleDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* @author tanghc * @author tanghc
*/ */
public interface PermRolePermissionMapper extends CrudMapper<PermRolePermission, Long> { public interface PermRolePermissionMapper extends CrudMapper<PermRolePermission, Long> {
List<RouteRoleDTO> listRouteRole(@Param("routeIdList") List<String> routeIdList);
} }

@ -5,10 +5,20 @@ import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.NacosException;
import com.gitee.sop.adminserver.bean.ChannelMsg; import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.GatewayPushDTO;
import com.gitee.sop.adminserver.bean.HttpTool;
import com.gitee.sop.adminserver.common.BizException; import com.gitee.sop.adminserver.common.BizException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/** /**
* @author tanghc * @author tanghc
*/ */
@ -16,17 +26,53 @@ import org.springframework.stereotype.Service;
@Service @Service
public class ConfigPushService { public class ConfigPushService {
private static final String GATEWAY_PUSH_URL = "http://%s/configChannelMsg";
private static HttpTool httpTool = new HttpTool();
@NacosInjected @NacosInjected
private ConfigService configService; private ConfigService configService;
@Value("${gateway.host:}")
private String gatewayHost;
@Value("${zuul.secret}")
private String secret;
public void publishConfig(String dataId, String groupId, ChannelMsg channelMsg) { public void publishConfig(String dataId, String groupId, ChannelMsg channelMsg) {
try { if (StringUtils.isNotBlank(gatewayHost)) {
log.info("nacos配置, dataId={}, groupId={}, operation={}", dataId, groupId, channelMsg.getOperation()); String[] hosts = gatewayHost.split(",");
configService.publishConfig(dataId, groupId, JSON.toJSONString(channelMsg)); for (String host : hosts) {
} catch (NacosException e) { GatewayPushDTO gatewayPushDTO = new GatewayPushDTO(dataId, groupId, channelMsg);
log.error("nacos配置失败, dataId={}, groupId={}, operation={}", dataId, groupId, channelMsg.getOperation(), e); String url = String.format(GATEWAY_PUSH_URL, host);
throw new BizException("nacos配置失败"); try {
String requestBody = JSON.toJSONString(gatewayPushDTO);
Map<String, String> header = new HashMap<>(8);
header.put("sign", buildRequestBodySign(requestBody, secret));
String resp = httpTool.requestJson(url, requestBody, header);
if (!"ok".equals(resp)) {
throw new IOException(resp);
}
} catch (IOException e) {
log.error("nacos配置失败, dataId={}, groupId={}, operation={}, url={}", dataId, groupId, channelMsg.getOperation(), url, e);
throw new BizException("推送配置失败");
}
}
} else {
try {
log.info("nacos配置, dataId={}, groupId={}, operation={}", dataId, groupId, channelMsg.getOperation());
configService.publishConfig(dataId, groupId, JSON.toJSONString(channelMsg));
} catch (NacosException e) {
log.error("nacos配置失败, dataId={}, groupId={}, operation={}", dataId, groupId, channelMsg.getOperation(), e);
throw new BizException("nacos配置失败");
}
} }
}
public static String buildRequestBodySign(String requestBody, String secret) {
String signContent = secret + requestBody + secret;
return DigestUtils.md5Hex(signContent);
} }
} }

@ -1,52 +0,0 @@
package com.gitee.sop.adminserver.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
import com.gitee.sop.adminserver.bean.RouteDefinition;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.bean.ServiceRouteInfo;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import static java.util.stream.Collectors.toList;
/**
* @author tanghc
*/
@Service
public class RouteService {
@NacosInjected
private ConfigService configService;
public List<RouteDefinition> getRouteDefinitionList(RouteSearchParam param) throws Exception {
String serviceId = param.getServiceId();
if (StringUtils.isBlank(serviceId)) {
return Collections.emptyList();
}
String configData = configService.getConfig(NacosConfigs.getRouteDataId(serviceId), NacosConfigs.GROUP_ROUTE, 3000);
if (StringUtils.isBlank(configData)) {
return Collections.emptyList();
}
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(configData, ServiceRouteInfo.class);
return serviceRouteInfo.getRouteDefinitionList()
.stream()
.filter(gatewayRouteDefinition -> {
boolean isRoute = gatewayRouteDefinition.getOrder() != Integer.MIN_VALUE;
String id = param.getId();
if (StringUtils.isBlank(id)) {
return isRoute;
} else {
return isRoute && gatewayRouteDefinition.getId().contains(id);
}
})
.collect(toList());
}
}

@ -9,6 +9,10 @@ mysql.password=root
# nacos注册中心地址 # nacos注册中心地址
nacos.url=127.0.0.1:8848 nacos.url=127.0.0.1:8848
# 网关地址,多个用逗号隔开
# 此配置仅在使用eureka时有用,不用的话注释掉
gateway.host=127.0.0.1:8081
# ------- 需要改的配置end ------- # ------- 需要改的配置end -------
# token过期时间,分钟 # token过期时间,分钟
@ -27,6 +31,7 @@ spring.datasource.username=${mysql.username}
spring.datasource.password=${mysql.password} spring.datasource.password=${mysql.password}
# 固定不用改 # 固定不用改
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
easyopen.show-doc=false easyopen.show-doc=false
easyopen.ignore-validate=true easyopen.ignore-validate=true

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 注意:文件名必须跟Dao类名字一致,因为是根据文件名做关联。 -->
<mapper namespace="com.gitee.sop.adminserver.mapper.PermRolePermissionMapper">
<select id="listRouteRole" resultType="com.gitee.sop.adminserver.entity.RouteRoleDTO">
SELECT
t.route_id as routeId,
t.role_code as roleCode,
t2.description
FROM perm_role_permission t
INNER JOIN perm_role t2 ON t.role_code=t2.role_code
WHERE t.route_id in
<foreach collection="routeIdList" item="routeId" open="(" separator="," close=")">
#{routeId}
</foreach>
</select>
</mapper>

@ -71,6 +71,12 @@
<artifactId>spring-boot-starter-webflux</artifactId> <artifactId>spring-boot-starter-webflux</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
<version>2.1.0.RELEASE</version>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>

@ -1,5 +1,6 @@
package com.gitee.sop.gatewaycommon.bean; package com.gitee.sop.gatewaycommon.bean;
import com.alibaba.fastjson.JSONObject;
import lombok.Data; import lombok.Data;
/** /**
@ -7,6 +8,10 @@ import lombok.Data;
*/ */
@Data @Data
public class ChannelMsg { public class ChannelMsg {
private String operation = "_unknown_"; private String operation;
private String data = "{}"; private JSONObject data;
public <T> T toObject(Class<T> clazz) {
return data.toJavaObject(clazz);
}
} }

@ -0,0 +1,22 @@
package com.gitee.sop.gatewaycommon.bean;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class GatewayPushDTO {
private String dataId;
private String groupId;
private ChannelMsg channelMsg;
public GatewayPushDTO() {
}
public GatewayPushDTO(String dataId, String groupId, ChannelMsg channelMsg) {
this.dataId = dataId;
this.groupId = groupId;
this.channelMsg = channelMsg;
}
}

@ -7,6 +7,8 @@ import com.gitee.sop.gatewaycommon.bean.SpringContext;
import com.gitee.sop.gatewaycommon.limit.LimitManager; import com.gitee.sop.gatewaycommon.limit.LimitManager;
import com.gitee.sop.gatewaycommon.message.ErrorFactory; import com.gitee.sop.gatewaycommon.message.ErrorFactory;
import com.gitee.sop.gatewaycommon.param.ParameterFormatter; import com.gitee.sop.gatewaycommon.param.ParameterFormatter;
import com.gitee.sop.gatewaycommon.route.NacosRoutesListener;
import com.gitee.sop.gatewaycommon.route.RegistryListener;
import com.gitee.sop.gatewaycommon.secret.IsvManager; import com.gitee.sop.gatewaycommon.secret.IsvManager;
import com.gitee.sop.gatewaycommon.session.SessionManager; import com.gitee.sop.gatewaycommon.session.SessionManager;
import com.gitee.sop.gatewaycommon.validate.Validator; import com.gitee.sop.gatewaycommon.validate.Validator;
@ -16,8 +18,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.cloud.client.discovery.event.HeartbeatEvent; import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@ -28,12 +31,13 @@ import javax.annotation.PostConstruct;
/** /**
* @author tanghc * @author tanghc
*/ */
public class AbstractConfiguration implements ApplicationContextAware, ApplicationListener<HeartbeatEvent> { public class AbstractConfiguration implements ApplicationContextAware {
@Autowired @Autowired
protected Environment environment; protected Environment environment;
@Autowired @Autowired
private ServiceRoutesLoader serviceRoutesLoader; private RegistryListener registryListener;
protected ApplicationContext applicationContext; protected ApplicationContext applicationContext;
@ -44,12 +48,15 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
/** /**
* nacos事件监听 * nacos事件监听
* @see org.springframework.cloud.alibaba.nacos.discovery.NacosWatch NacosWatch *
* @param heartbeatEvent * @param heartbeatEvent
*/ */
@Override @EventListener(classes = HeartbeatEvent.class)
public void onApplicationEvent(HeartbeatEvent heartbeatEvent) { public void listenNacosEvent(ApplicationEvent heartbeatEvent) {
serviceRoutesLoader.load(heartbeatEvent); Object source = heartbeatEvent.getSource();
if (source != null && source.getClass().getName().contains("NacosWatch")) {
registryListener.onRegister(heartbeatEvent);
}
} }
/** /**
@ -57,8 +64,8 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
ServiceRoutesLoader serviceRoutesLoader() { RegistryListener registryListener() {
return new ServiceRoutesLoader(); return new NacosRoutesListener();
} }
@Bean @Bean

@ -10,6 +10,7 @@ import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.gitee.sop.gatewaycommon.bean.NacosConfigs; import com.gitee.sop.gatewaycommon.bean.NacosConfigs;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
import com.gitee.sop.gatewaycommon.bean.TargetRoute; import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import com.gitee.sop.gatewaycommon.route.RegistryListener;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -31,7 +32,11 @@ import java.util.Objects;
* 发现新服务更新路由信息 * 发现新服务更新路由信息
* *
* @author tanghc * @author tanghc
*
* @deprecated
* @see RegistryListener
*/ */
@Deprecated
@Slf4j @Slf4j
public class ServiceRoutesLoader<T extends TargetRoute> { public class ServiceRoutesLoader<T extends TargetRoute> {

@ -0,0 +1,154 @@
package com.gitee.sop.gatewaycommon.route;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
import com.gitee.sop.gatewaycommon.manager.BaseRouteCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.DigestUtils;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author tanghc
*/
@Slf4j
public abstract class BaseRoutesListener implements RegistryListener {
private static final String SOP_ROUTES_PATH = "/sop/routes";
private static final String SECRET = "a3d9sf!1@odl90zd>fkASwq";
private static final int FIVE_SECONDS = 1000 * 5;
private static final String METADATA_SERVER_CONTEXT_PATH = "server.servlet.context-path";
private static final String METADATA_SOP_ROUTES_PATH = "sop.routes.path";
private static RestTemplate restTemplate = new RestTemplate();
static {
// 解决statusCode不等于200,就抛异常问题
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
protected boolean hasError(HttpStatus statusCode) {
return statusCode == null;
}
});
}
private Map<String, Long> updateTimeMap = new ConcurrentHashMap<>(16);
@Autowired
private BaseRouteCache<?> baseRouteCache;
@Autowired
private RoutesProcessor routesProcessor;
/**
* 移除路由信息
*
* @param serviceId serviceId
*/
public void removeRoutes(String serviceId) {
doOperator(serviceId, () -> {
log.info("服务下线,删除路由配置,serviceId: {}", serviceId);
baseRouteCache.remove(serviceId);
routesProcessor.removeAllRoutes(serviceId);
});
}
/**
* 拉取路由信息
*
* @param instance 服务实例
*/
public void pullRoutes(InstanceDefinition instance) {
String serviceName = instance.getServiceId();
doOperator(serviceName, () -> {
String url = getRouteRequestUrl(instance);
log.info("拉取路由配置,serviceId: {}, url: {}", serviceName, url);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
String body = responseEntity.getBody();
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(body, ServiceRouteInfo.class);
baseRouteCache.load(serviceRouteInfo, callback -> routesProcessor.saveRoutes(serviceRouteInfo, instance));
} else {
log.error("拉取路由配置异常,url: {}, status: {}, body: {}", url, responseEntity.getStatusCodeValue(), responseEntity.getBody());
}
});
}
private void doOperator(String serviceId, Runnable runnable) {
if (canOperator(serviceId)) {
runnable.run();
}
}
private boolean canOperator(String serviceId) {
// nacos会不停的触发事件,这里做了一层拦截
// 同一个serviceId5秒内允许访问一次
Long lastUpdateTime = updateTimeMap.getOrDefault(serviceId, 0L);
long now = System.currentTimeMillis();
boolean can = now - lastUpdateTime > FIVE_SECONDS;
if (can) {
updateTimeMap.put(serviceId, now);
}
return can;
}
/**
* 拉取路由请求url
*
* @param instance 服务实例
* @return 返回最终url
*/
private static String getRouteRequestUrl(InstanceDefinition instance) {
Map<String, String> metadata = instance.getMetadata();
String customPath = metadata.get(METADATA_SOP_ROUTES_PATH);
String homeUrl;
String servletPath;
// 如果metadata中指定了获取路由的url
if (StringUtils.isNotBlank(customPath)) {
// 自定义完整的url
if (customPath.startsWith("http")) {
homeUrl = customPath;
servletPath = "";
} else {
homeUrl = getHomeUrl(instance);
servletPath = customPath;
}
} else {
// 默认处理
homeUrl = getHomeUrl(instance);
String contextPath = metadata.getOrDefault(METADATA_SERVER_CONTEXT_PATH, "");
servletPath = contextPath + SOP_ROUTES_PATH;
}
if (StringUtils.isNotBlank(servletPath) && !servletPath.startsWith("/")) {
servletPath = '/' + servletPath;
}
String query = buildQuery(SECRET);
return homeUrl + servletPath + query;
}
private static String getHomeUrl(InstanceDefinition instance) {
return "http://" + instance.getIp() + ":" + instance.getPort();
}
private static String buildQuery(String secret) {
String time = String.valueOf(System.currentTimeMillis());
String source = secret + time + secret;
String sign = DigestUtils.md5DigestAsHex(source.getBytes());
return "?time=" + time + "&sign=" + sign;
}
}

@ -0,0 +1,34 @@
package com.gitee.sop.gatewaycommon.route;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import com.netflix.appinfo.InstanceInfo;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent;
import org.springframework.context.ApplicationEvent;
/**
* 加载服务路由eureka实现
*
* @author tanghc
*/
public class EurekaRoutesListener extends BaseRoutesListener {
@Override
public void onRegister(ApplicationEvent applicationEvent) {
EurekaInstanceRegisteredEvent event = (EurekaInstanceRegisteredEvent)applicationEvent;
InstanceInfo instanceInfo = event.getInstanceInfo();
InstanceDefinition instanceDefinition = new InstanceDefinition();
instanceDefinition.setInstanceId(instanceInfo.getInstanceId());
instanceDefinition.setServiceId(instanceInfo.getAppName());
instanceDefinition.setIp(instanceInfo.getIPAddr());
instanceDefinition.setPort(instanceInfo.getPort());
instanceDefinition.setMetadata(instanceInfo.getMetadata());
pullRoutes(instanceDefinition);
}
@Override
public void onDeregister(ApplicationEvent applicationEvent) {
EurekaInstanceCanceledEvent event = (EurekaInstanceCanceledEvent)applicationEvent;
removeRoutes(event.getServerId());
}
}

@ -0,0 +1,80 @@
package com.gitee.sop.gatewaycommon.route;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.context.ApplicationEvent;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Objects;
/**
* 加载服务路由nacos实现
*
* @author tanghc
*/
@Slf4j
public class NacosRoutesListener extends BaseRoutesListener {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@NacosInjected
private ConfigService configService;
@Override
public void onRegister(ApplicationEvent applicationEvent) {
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
List<ServiceInfo> subscribes = null;
try {
subscribes = namingService.getSubscribeServices();
} catch (NacosException e) {
log.error("namingService.getSubscribeServices()错误", e);
}
if (CollectionUtils.isEmpty(subscribes)) {
return;
}
// subscribe
String thisServiceId = nacosDiscoveryProperties.getService();
for (ServiceInfo serviceInfo : subscribes) {
String serviceName = serviceInfo.getName();
// 如果是本机服务,跳过
if (Objects.equals(thisServiceId, serviceName)) {
continue;
}
try {
List<Instance> allInstances = namingService.getAllInstances(serviceName);
if (CollectionUtils.isEmpty(allInstances)) {
// 如果没有服务列表,则删除所有路由信息
removeRoutes(serviceName);
} else {
for (Instance instance : allInstances) {
InstanceDefinition instanceDefinition = new InstanceDefinition();
instanceDefinition.setInstanceId(instance.getInstanceId());
instanceDefinition.setServiceId(serviceName);
instanceDefinition.setIp(instance.getIp());
instanceDefinition.setPort(instance.getPort());
instanceDefinition.setMetadata(instance.getMetadata());
pullRoutes(instanceDefinition);
}
}
} catch (Exception e) {
log.error("选择服务实例失败,serviceName: {}", serviceName, e);
}
}
}
@Override
public void onDeregister(ApplicationEvent applicationEvent) {
}
}

@ -0,0 +1,26 @@
package com.gitee.sop.gatewaycommon.route;
import org.springframework.context.ApplicationEvent;
/**
* 发现新服务更新路由信息
*
* @author tanghc
*/
public interface RegistryListener {
/**
* 触发新服务注册
*
* @param applicationEvent 事件可能是nacos事件也可能是eureka事件
*/
void onRegister(ApplicationEvent applicationEvent);
/**
* 注销服务
*
* @param applicationEvent
*/
void onDeregister(ApplicationEvent applicationEvent);
}

@ -0,0 +1,24 @@
package com.gitee.sop.gatewaycommon.route;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
/**
* @author tanghc
*/
public interface RoutesProcessor {
/**
* 删除serviceId下所有路由
*
* @param serviceId serviceId
*/
void removeAllRoutes(String serviceId);
/**
* 保存路由
*
* @param serviceRouteInfo 路由信息
* @param instance 服务实例
*/
void saveRoutes(ServiceRouteInfo serviceRouteInfo, InstanceDefinition instance);
}

@ -0,0 +1,43 @@
package com.gitee.sop.gatewaycommon.support;
import com.gitee.sop.gatewaycommon.route.EurekaRoutesListener;
import com.gitee.sop.gatewaycommon.route.RegistryListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
/**
* eureka注册中心事件监听
* @author tanghc
*/
public class EurekaListenerConfiguration {
@Autowired
private RegistryListener registryListener;
/**
* 客户端注册触发
* @param event
*/
@EventListener
public void onEurekaInstanceRegisteredEvent(EurekaInstanceRegisteredEvent event) {
registryListener.onRegister(event);
}
/**
* 客户端下线触发
* @param event
*/
@EventListener
public void onEurekaInstanceCanceledEvent(EurekaInstanceCanceledEvent event) {
registryListener.onDeregister(event);
}
@Bean
RegistryListener registryListener() {
return new EurekaRoutesListener();
}
}

@ -6,6 +6,7 @@ import com.gitee.sop.gatewaycommon.param.ApiUploadContext;
import com.gitee.sop.gatewaycommon.param.UploadContext; import com.gitee.sop.gatewaycommon.param.UploadContext;
import com.netflix.zuul.http.HttpServletRequestWrapper; import com.netflix.zuul.http.HttpServletRequestWrapper;
import lombok.Data; import lombok.Data;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.fileupload.servlet.ServletFileUpload;
@ -268,6 +269,17 @@ public class RequestUtil {
return uploadInfo; return uploadInfo;
} }
public static void checkResponseBody(String responseBody, String sign, String secret) throws Exception {
if (sign == null) {
throw new Exception("签名不存在");
}
String signContent = secret + responseBody + secret;
String clientSign = DigestUtils.md5Hex(signContent);
if (!sign.equals(clientSign)) {
throw new Exception("签名错误");
}
}
@Data @Data
public static class UploadInfo { public static class UploadInfo {
private Map<String, String> uploadParams; private Map<String, String> uploadParams;

@ -0,0 +1,68 @@
package com.gitee.sop.gateway.controller;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gateway.manager.ChannelMsgProcessor;
import com.gitee.sop.gateway.manager.DbEnvGrayManager;
import com.gitee.sop.gateway.manager.DbIPBlacklistManager;
import com.gitee.sop.gateway.manager.DbIsvManager;
import com.gitee.sop.gateway.manager.DbIsvRoutePermissionManager;
import com.gitee.sop.gateway.manager.DbLimitConfigManager;
import com.gitee.sop.gateway.manager.DbRouteConfigManager;
import com.gitee.sop.gatewaycommon.bean.GatewayPushDTO;
import com.gitee.sop.gatewaycommon.bean.NacosConfigs;
import com.gitee.sop.gatewaycommon.bean.SpringContext;
import com.gitee.sop.gatewaycommon.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author tanghc
*/
@Slf4j
@RestController
public class ConfigChannelController {
private static Map<String, Class<? extends ChannelMsgProcessor>> processorMap = new HashMap<>(16);
static {
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_GRAY, DbEnvGrayManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_IP_BLACKLIST, DbIPBlacklistManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ISV, DbIsvManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_PERMISSION, DbIsvRoutePermissionManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_LIMIT_CONFIG, DbLimitConfigManager.class);
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_CONFIG, DbRouteConfigManager.class);
}
@Value("${zuul.secret}")
private String secret;
@PostMapping("/configChannelMsg")
public String configChannel(HttpServletRequest request) throws IOException {
String requestJson = RequestUtil.getText(request);
String sign = request.getHeader("sign");
try {
RequestUtil.checkResponseBody(requestJson, sign, secret);
} catch (Exception e) {
log.error("configChannelMsg错误", e);
return e.getMessage();
}
GatewayPushDTO gatewayPushDTO = JSON.parseObject(requestJson, GatewayPushDTO.class);
ChannelMsgProcessor channelMsgProcessor = getChannelMsgProcessor(gatewayPushDTO);
channelMsgProcessor.process(gatewayPushDTO.getChannelMsg());
return "ok";
}
private ChannelMsgProcessor getChannelMsgProcessor(GatewayPushDTO gatewayPushDTO) {
String key = gatewayPushDTO.getGroupId() + gatewayPushDTO.getDataId();
Class<? extends ChannelMsgProcessor> aClass = processorMap.get(key);
return SpringContext.getBean(aClass);
}
}

@ -0,0 +1,71 @@
package com.gitee.sop.gateway.entity;
import com.gitee.fastmybatis.core.annotation.LogicDelete;
import lombok.Data;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* 表名config_service_route
* 备注路由配置
*
* @author tanghc
*/
@Table(name = "config_service_route")
@Data
public class ConfigServiceRoute {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
/** 数据库字段:id */
private String id;
/** 数据库字段:service_id */
private String serviceId;
/** 接口名, 数据库字段:name */
private String name;
/** 版本号, 数据库字段:version */
private String version;
/** 路由断言(SpringCloudGateway专用), 数据库字段:predicates */
private String predicates;
/** 路由过滤器(SpringCloudGateway专用), 数据库字段:filters */
private String filters;
/** 路由规则转发的目标uri, 数据库字段:uri */
private String uri;
/** uri后面跟的path, 数据库字段:path */
private String path;
/** 路由执行的顺序, 数据库字段:order */
private Integer order;
/** 是否忽略验证,业务参数验证除外, 数据库字段:ignore_validate */
private Byte ignoreValidate;
/** 状态,0:待审核,1:启用,2:禁用, 数据库字段:status */
private Byte status;
/** 是否合并结果, 数据库字段:merge_result */
private Byte mergeResult;
/** 是否需要授权才能访问, 数据库字段:permission */
private Byte permission;
/** 数据库字段:gmt_create */
private Date gmtCreate;
/** 数据库字段:gmt_modified */
private Date gmtModified;
}

@ -0,0 +1,10 @@
package com.gitee.sop.gateway.manager;
import com.gitee.sop.gatewaycommon.bean.ChannelMsg;
/**
* @author tanghc
*/
public interface ChannelMsgProcessor {
void process(ChannelMsg channelMsg);
}

@ -36,7 +36,7 @@ import java.util.stream.Stream;
*/ */
@Slf4j @Slf4j
@Service @Service
public class DbEnvGrayManager extends DefaultEnvGrayManager { public class DbEnvGrayManager extends DefaultEnvGrayManager implements ChannelMsgProcessor {
private static final int STATUS_ENABLE = 1; private static final int STATUS_ENABLE = 1;
@ -97,29 +97,32 @@ public class DbEnvGrayManager extends DefaultEnvGrayManager {
this.saveServiceGrayConfig(serviceGrayConfig); this.saveServiceGrayConfig(serviceGrayConfig);
} }
@Override
public void process(ChannelMsg channelMsg) {
ServiceGrayDefinition userKeyDefinition = channelMsg.toObject(ServiceGrayDefinition.class);
String serviceId = userKeyDefinition.getServiceId();
switch (channelMsg.getOperation()) {
case "set":
ConfigGray configGray = configGrayMapper.getByColumn("service_id", serviceId);
setServiceGrayConfig(configGray);
break;
case "open":
openGray(userKeyDefinition.getInstanceId(), serviceId);
break;
case "close":
closeGray(userKeyDefinition.getInstanceId());
break;
default:
}
}
@PostConstruct @PostConstruct
protected void after() throws Exception { protected void after() throws Exception {
configService.addListener(NacosConfigs.DATA_ID_GRAY, NacosConfigs.GROUP_CHANNEL, new AbstractListener() { configService.addListener(NacosConfigs.DATA_ID_GRAY, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override @Override
public void receiveConfigInfo(String configInfo) { public void receiveConfigInfo(String configInfo) {
ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class); ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class);
String data = channelMsg.getData(); process(channelMsg);
ServiceGrayDefinition userKeyDefinition = JSON.parseObject(data, ServiceGrayDefinition.class);
String serviceId = userKeyDefinition.getServiceId();
switch (channelMsg.getOperation()) {
case "set":
ConfigGray configGray = configGrayMapper.getByColumn("service_id", serviceId);
setServiceGrayConfig(configGray);
break;
case "open":
openGray(userKeyDefinition.getInstanceId(), serviceId);
break;
case "close":
closeGray(userKeyDefinition.getInstanceId());
break;
default:
}
} }
}); });
} }

@ -23,7 +23,7 @@ import java.util.List;
*/ */
@Slf4j @Slf4j
@Service @Service
public class DbIPBlacklistManager extends DefaultIPBlacklistManager { public class DbIPBlacklistManager extends DefaultIPBlacklistManager implements ChannelMsgProcessor {
@Autowired @Autowired
private IPBlacklistMapper ipBlacklistMapper; private IPBlacklistMapper ipBlacklistMapper;
@ -35,29 +35,34 @@ public class DbIPBlacklistManager extends DefaultIPBlacklistManager {
public void load() { public void load() {
List<String> ipList = ipBlacklistMapper.listAllIP(); List<String> ipList = ipBlacklistMapper.listAllIP();
log.info("加载IP黑名单, size:{}", ipList.size()); log.info("加载IP黑名单, size:{}", ipList.size());
ipList.stream().forEach(this::add); ipList.forEach(this::add);
} }
@Override
public void process(ChannelMsg channelMsg) {
final IPDto ipDto = channelMsg.toObject(IPDto.class);
String ip = ipDto.getIp();
switch (channelMsg.getOperation()) {
case "add":
log.info("添加IP黑名单,ip:{}", ip);
add(ip);
break;
case "delete":
log.info("移除IP黑名单,ip:{}", ip);
remove(ip);
break;
default:
}
}
@PostConstruct @PostConstruct
protected void after() throws Exception { protected void after() throws Exception {
configService.addListener(NacosConfigs.DATA_ID_IP_BLACKLIST, NacosConfigs.GROUP_CHANNEL, new AbstractListener() { configService.addListener(NacosConfigs.DATA_ID_IP_BLACKLIST, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override @Override
public void receiveConfigInfo(String configInfo) { public void receiveConfigInfo(String configInfo) {
ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class); ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class);
final IPDto ipDto = JSON.parseObject(channelMsg.getData(), IPDto.class); process(channelMsg);
String ip = ipDto.getIp();
switch (channelMsg.getOperation()) {
case "add":
log.info("添加IP黑名单,ip:{}", ip);
add(ip);
break;
case "delete":
log.info("移除IP黑名单,ip:{}", ip);
remove(ip);
break;
default:
}
} }
}); });
} }
@ -66,5 +71,4 @@ public class DbIPBlacklistManager extends DefaultIPBlacklistManager {
private static class IPDto { private static class IPDto {
private String ip; private String ip;
} }
} }

@ -24,7 +24,7 @@ import java.util.List;
*/ */
@Slf4j @Slf4j
@Service @Service
public class DbIsvManager extends CacheIsvManager { public class DbIsvManager extends CacheIsvManager implements ChannelMsgProcessor{
@Autowired @Autowired
private IsvInfoMapper isvInfoMapper; private IsvInfoMapper isvInfoMapper;
@ -35,7 +35,7 @@ public class DbIsvManager extends CacheIsvManager {
@Override @Override
public void load() { public void load() {
List<IsvDetailDTO> isvInfoList = isvInfoMapper.listIsvDetail(); List<IsvDetailDTO> isvInfoList = isvInfoMapper.listIsvDetail();
isvInfoList.stream() isvInfoList
.forEach(isvInfo -> { .forEach(isvInfo -> {
IsvDefinition isvDefinition = new IsvDefinition(); IsvDefinition isvDefinition = new IsvDefinition();
BeanUtils.copyProperties(isvInfo, isvDefinition); BeanUtils.copyProperties(isvInfo, isvDefinition);
@ -43,6 +43,23 @@ public class DbIsvManager extends CacheIsvManager {
}); });
} }
@Override
public void process(ChannelMsg channelMsg) {
final IsvDefinition isvDefinition = channelMsg.toObject(IsvDefinition.class);
switch (channelMsg.getOperation()) {
case "update":
log.info("更新ISV信息,isvDefinition:{}", isvDefinition);
update(isvDefinition);
break;
case "remove":
log.info("删除ISV,isvDefinition:{}", isvDefinition);
remove(isvDefinition.getAppKey());
break;
default:
}
}
@PostConstruct @PostConstruct
protected void after() throws Exception { protected void after() throws Exception {
ApiConfig.getInstance().setIsvManager(this); ApiConfig.getInstance().setIsvManager(this);
@ -50,20 +67,8 @@ public class DbIsvManager extends CacheIsvManager {
configService.addListener(NacosConfigs.DATA_ID_ISV, NacosConfigs.GROUP_CHANNEL, new AbstractListener() { configService.addListener(NacosConfigs.DATA_ID_ISV, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override @Override
public void receiveConfigInfo(String configInfo) { public void receiveConfigInfo(String configInfo) {
ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class); ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class);
final IsvDefinition isvDefinition = JSON.parseObject(channelMsg.getData(), IsvDefinition.class); process(channelMsg);
switch (channelMsg.getOperation()) {
case "update":
log.info("更新ISV信息,isvDefinition:{}", isvDefinition);
update(isvDefinition);
break;
case "remove":
log.info("删除ISV,isvDefinition:{}", isvDefinition);
remove(isvDefinition.getAppKey());
break;
default:
}
} }
}); });
} }

@ -40,7 +40,7 @@ import static java.util.stream.Collectors.toList;
*/ */
@Slf4j @Slf4j
@Service @Service
public class DbIsvRoutePermissionManager extends DefaultIsvRoutePermissionManager { public class DbIsvRoutePermissionManager extends DefaultIsvRoutePermissionManager implements ChannelMsgProcessor {
@Autowired @Autowired
Environment environment; Environment environment;
@ -122,41 +122,46 @@ public class DbIsvRoutePermissionManager extends DefaultIsvRoutePermissionManage
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Data @Override
static class IsvRole { public void process(ChannelMsg channelMsg) {
private String appKey; final IsvRoutePermission isvRoutePermission = channelMsg.toObject(IsvRoutePermission.class);
private String roleCode; switch (channelMsg.getOperation()) {
} case "reload":
log.info("重新加载路由权限信息,isvRoutePermission:{}", isvRoutePermission);
try {
load();
} catch (Exception e) {
log.error("重新加载路由权限失败, channelMsg:{}", channelMsg, e);
}
break;
case "update":
log.info("更新ISV路由权限信息,isvRoutePermission:{}", isvRoutePermission);
update(isvRoutePermission);
break;
case "remove":
log.info("删除ISV路由权限信息,isvRoutePermission:{}", isvRoutePermission);
remove(isvRoutePermission.getAppKey());
break;
default:
}
}
@PostConstruct @PostConstruct
protected void after() throws Exception { protected void after() throws Exception {
configService.addListener(NacosConfigs.DATA_ID_ROUTE_PERMISSION, NacosConfigs.GROUP_CHANNEL, new AbstractListener() { configService.addListener(NacosConfigs.DATA_ID_ROUTE_PERMISSION, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override @Override
public void receiveConfigInfo(String configInfo) { public void receiveConfigInfo(String configInfo) {
ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class); ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class);
final IsvRoutePermission isvRoutePermission = JSON.parseObject(channelMsg.getData(), IsvRoutePermission.class); process(channelMsg);
switch (channelMsg.getOperation()) {
case "reload":
log.info("重新加载路由权限信息,isvRoutePermission:{}", isvRoutePermission);
try {
load();
} catch (Exception e) {
log.error("重新加载路由权限失败, channelMsg:{}", channelMsg, e);
}
break;
case "update":
log.info("更新ISV路由权限信息,isvRoutePermission:{}", isvRoutePermission);
update(isvRoutePermission);
break;
case "remove":
log.info("删除ISV路由权限信息,isvRoutePermission:{}", isvRoutePermission);
remove(isvRoutePermission.getAppKey());
break;
default:
}
} }
}); });
} }
@Data
static class IsvRole {
private String appKey;
private String roleCode;
}
} }

@ -24,7 +24,7 @@ import javax.annotation.PostConstruct;
*/ */
@Slf4j @Slf4j
@Service @Service
public class DbLimitConfigManager extends DefaultLimitConfigManager { public class DbLimitConfigManager extends DefaultLimitConfigManager implements ChannelMsgProcessor {
@Autowired @Autowired
ConfigLimitMapper configLimitMapper; ConfigLimitMapper configLimitMapper;
@ -39,7 +39,6 @@ public class DbLimitConfigManager extends DefaultLimitConfigManager {
public void load() { public void load() {
Query query = new Query(); Query query = new Query();
configLimitMapper.list(query) configLimitMapper.list(query)
.stream()
.forEach(configLimit -> putVal(configLimit)); .forEach(configLimit -> putVal(configLimit));
} }
@ -50,25 +49,29 @@ public class DbLimitConfigManager extends DefaultLimitConfigManager {
this.update(configLimitDto); this.update(configLimitDto);
} }
@Override
public void process(ChannelMsg channelMsg) {
final ConfigLimitDto configLimitDto = channelMsg.toObject(ConfigLimitDto.class);
switch (channelMsg.getOperation()) {
case "reload":
log.info("重新加载限流配置信息,configLimitDto:{}", configLimitDto);
load();
break;
case "update":
log.info("更新限流配置信息,configLimitDto:{}", configLimitDto);
update(configLimitDto);
break;
default:
}
}
@PostConstruct @PostConstruct
protected void after() throws Exception { protected void after() throws Exception {
configService.addListener(NacosConfigs.DATA_ID_LIMIT_CONFIG, NacosConfigs.GROUP_CHANNEL, new AbstractListener() { configService.addListener(NacosConfigs.DATA_ID_LIMIT_CONFIG, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override @Override
public void receiveConfigInfo(String configInfo) { public void receiveConfigInfo(String configInfo) {
ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class); ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class);
final ConfigLimitDto configLimitDto = JSON.parseObject(channelMsg.getData(), ConfigLimitDto.class); process(channelMsg);
switch (channelMsg.getOperation()) {
case "reload":
log.info("重新加载限流配置信息,configLimitDto:{}", configLimitDto);
load();
break;
case "update":
log.info("更新限流配置信息,configLimitDto:{}", configLimitDto);
update(configLimitDto);
break;
default:
}
} }
}); });
} }

@ -27,7 +27,7 @@ import java.util.Collection;
*/ */
@Slf4j @Slf4j
@Service @Service
public class DbRouteConfigManager extends DefaultRouteConfigManager { public class DbRouteConfigManager extends DefaultRouteConfigManager implements ChannelMsgProcessor {
@Autowired @Autowired
ConfigRouteBaseMapper configRouteBaseMapper; ConfigRouteBaseMapper configRouteBaseMapper;
@ -72,25 +72,29 @@ public class DbRouteConfigManager extends DefaultRouteConfigManager {
this.doUpdate(routeId, object); this.doUpdate(routeId, object);
} }
@Override
public void process(ChannelMsg channelMsg) {
final RouteConfig routeConfig = channelMsg.toObject( RouteConfig.class);
switch (channelMsg.getOperation()) {
case "reload":
log.info("重新加载路由配置信息,routeConfigDto:{}", routeConfig);
load();
break;
case "update":
log.info("更新路由配置信息,routeConfigDto:{}", routeConfig);
update(routeConfig);
break;
default:
}
}
@PostConstruct @PostConstruct
protected void after() throws Exception { protected void after() throws Exception {
configService.addListener(NacosConfigs.DATA_ID_ROUTE_CONFIG, NacosConfigs.GROUP_CHANNEL, new AbstractListener() { configService.addListener(NacosConfigs.DATA_ID_ROUTE_CONFIG, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override @Override
public void receiveConfigInfo(String configInfo) { public void receiveConfigInfo(String configInfo) {
ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class); ChannelMsg channelMsg = JSON.parseObject(configInfo, ChannelMsg.class);
final RouteConfig routeConfig = JSON.parseObject(channelMsg.getData(), RouteConfig.class); process(channelMsg);
switch (channelMsg.getOperation()) {
case "reload":
log.info("重新加载路由配置信息,routeConfigDto:{}", routeConfig);
load();
break;
case "update":
log.info("更新路由配置信息,routeConfigDto:{}", routeConfig);
update(routeConfig);
break;
default:
}
} }
}); });
} }

@ -0,0 +1,68 @@
package com.gitee.sop.gateway.manager;
import com.alibaba.fastjson.JSON;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.gateway.entity.ConfigServiceRoute;
import com.gitee.sop.gateway.mapper.ConfigServiceRouteMapper;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
import com.gitee.sop.gatewaycommon.route.RoutesProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author tanghc
*/
@Slf4j
@Component
public class DbRoutesProcessor implements RoutesProcessor {
@Autowired
private ConfigServiceRouteMapper configServiceRouteMapper;
@Override
public void removeAllRoutes(String serviceId) {
// 删除serviceId下所有的路由
Query delServiceQuery = new Query().eq("service_id", serviceId);
configServiceRouteMapper.deleteByQuery(delServiceQuery);
}
@Override
@Transactional
public void saveRoutes(ServiceRouteInfo serviceRouteInfo, InstanceDefinition instance) {
log.info("保存路由信息到数据库,instance: {}", instance);
String serviceId = serviceRouteInfo.getServiceId();
List<ConfigServiceRoute> configServiceRoutes = serviceRouteInfo
.getRouteDefinitionList()
.parallelStream()
.map(routeDefinition -> {
ConfigServiceRoute configServiceRoute = new ConfigServiceRoute();
configServiceRoute.setId(routeDefinition.getId());
configServiceRoute.setName(routeDefinition.getName());
configServiceRoute.setVersion(routeDefinition.getVersion());
configServiceRoute.setUri(routeDefinition.getUri());
configServiceRoute.setPath(routeDefinition.getPath());
configServiceRoute.setFilters(JSON.toJSONString(routeDefinition.getFilters()));
configServiceRoute.setPredicates(JSON.toJSONString(routeDefinition.getPredicates()));
configServiceRoute.setIgnoreValidate((byte) routeDefinition.getIgnoreValidate());
configServiceRoute.setMergeResult((byte) routeDefinition.getMergeResult());
configServiceRoute.setStatus((byte) routeDefinition.getStatus());
configServiceRoute.setPermission((byte) routeDefinition.getPermission());
configServiceRoute.setOrder(routeDefinition.getOrder());
configServiceRoute.setServiceId(serviceId);
return configServiceRoute;
})
.collect(Collectors.toList());
// 删除serviceId下所有的路由
this.removeAllRoutes(serviceId);
// 批量保存
configServiceRouteMapper.saveBatch(configServiceRoutes);
}
}

@ -0,0 +1,11 @@
package com.gitee.sop.gateway.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.gateway.entity.ConfigServiceRoute;
/**
* @author tanghc
*/
public interface ConfigServiceRouteMapper extends CrudMapper<ConfigServiceRoute, Long> {
}
Loading…
Cancel
Save