应具备哪些能力
- 应用授权
- 签名验证
- 密钥管理
- 接口权限分配
- 动态路由
- 接口限流
- ip黑名单/白名单
- 监控日志
- 文档整合
- 基础sdk(java、php等)
- 用户token 认证
架构图
表设计
1、应用信息表
CREATE TABLE `app_info` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`app_key` varchar(100) NOT NULL COMMENT 'appKey',
`status` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '1启用,2禁用',
`sign_type` tinyint NOT NULL DEFAULT '1' COMMENT '1:MD5,2:RSA2',
`remark` varchar(128) DEFAULT NULL COMMENT '备注',
`ip_list` varchar(128) DEFAULT NULL COMMENT 'ip 白名单, 空-不校验',
`third_id` int DEFAULT '0' COMMENT 'ice_third_info.id',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_app_key` (`app_key`),
KEY `idx_userid` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='应用信息表';
2、 应用密钥
CREATE TABLE `app_keys` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`app_key` varchar(128) NOT NULL DEFAULT '',
`sign_type` tinyint NOT NULL DEFAULT '1' COMMENT '1:MD5,2:RSA2',
`secret` varchar(200) NOT NULL DEFAULT '' COMMENT 'sign_type=1时使用',
`key_format` tinyint NOT NULL DEFAULT '1' COMMENT '秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用)',
`public_key_isv` text NOT NULL COMMENT '开发者生成的公钥',
`private_key_isv` text NOT NULL COMMENT '开发者生成的私钥(交给开发者)',
`public_key_platform` text NOT NULL COMMENT '平台生成的公钥(交给开发者)',
`private_key_platform` text NOT NULL COMMENT '平台生成的私钥',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_appkey` (`app_key`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='应用秘钥';
3、路由配置
CREATE TABLE `route` (
`id` varchar(128) NOT NULL DEFAULT '' COMMENT '路由id',
`service_id` varchar(128) NOT NULL DEFAULT '服务id',
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名',
`version` varchar(64) NOT NULL DEFAULT '' COMMENT '版本号',
`predicates` varchar(256) DEFAULT NULL COMMENT '路由断言(SpringCloudGateway专用)',
`filters` varchar(256) DEFAULT NULL COMMENT '路由过滤器(SpringCloudGateway专用)',
`uri` varchar(128) NOT NULL DEFAULT '' COMMENT '路由规则转发的目标uri',
`path` varchar(128) NOT NULL DEFAULT '' COMMENT 'uri后面跟的path',
`order_index` int NOT NULL DEFAULT '0' COMMENT '路由执行的顺序',
`ignore_validate` tinyint NOT NULL DEFAULT '0' COMMENT '是否忽略验证,业务参数验证除外',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态,0:待审核,1:启用,2:禁用',
`merge_result` tinyint NOT NULL DEFAULT '0' COMMENT '是否合并结果',
`permission` tinyint NOT NULL DEFAULT '0' COMMENT '是否需要授权才能访问',
`need_token` tinyint NOT NULL DEFAULT '0' COMMENT '是否需要token',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_serviceid` (`service_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='路由配置';
4、应用权限
CREATE TABLE `app_permission` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`app_key` varchar(128) NOT NULL COMMENT '应用 appKey',
`route_id` varchar(64) NOT NULL COMMENT 'api_id',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_app_perm` (`app_key`,`route_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='应用url 权限';
5、限流配置
CREATE TABLE `route_limit` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`route_id` varchar(128) DEFAULT NULL COMMENT '路由id',
`app_key` varchar(128) DEFAULT NULL COMMENT '应用 appKey',
`limit_ip` varchar(300) DEFAULT NULL COMMENT '限流ip,多个用英文逗号隔开',
`service_id` varchar(64) NOT NULL DEFAULT '' COMMENT '服务id',
`limit_type` tinyint NOT NULL DEFAULT '1' COMMENT '限流策略,1:窗口策略,2:令牌桶策略',
`exec_count_per_second` int DEFAULT NULL COMMENT '每秒可处理请求数',
`duration_seconds` int NOT NULL DEFAULT '1' COMMENT '限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效)',
`limit_code` varchar(64) DEFAULT NULL COMMENT '返回的错误码',
`limit_msg` varchar(100) DEFAULT NULL COMMENT '返回的错误信息',
`token_bucket_count` int DEFAULT NULL COMMENT '令牌桶容量',
`limit_status` tinyint NOT NULL DEFAULT '0' COMMENT '限流开启状态,1:开启,0关闭',
`order_index` int NOT NULL DEFAULT '0' COMMENT '顺序,值小的优先执行',
`remark` varchar(128) DEFAULT NULL COMMENT '备注',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='限流配置'
6、IP 黑名单
CREATE TABLE `ip_black` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`ip` varchar(64) NOT NULL DEFAULT '' COMMENT 'ip',
`remark` varchar(128) DEFAULT NULL COMMENT '备注',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_ip` (`ip`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='IP黑名单';
7、接口监控信息
CREATE TABLE `monitor_info` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`route_id` varchar(128) NOT NULL DEFAULT '' COMMENT '路由id',
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名',
`version` varchar(64) NOT NULL DEFAULT '' COMMENT '版本号',
`service_id` varchar(64) NOT NULL DEFAULT '',
`instance_id` varchar(128) NOT NULL DEFAULT '',
`max_time` int NOT NULL DEFAULT '0' COMMENT '请求耗时最长时间',
`min_time` int NOT NULL DEFAULT '0' COMMENT '请求耗时最小时间',
`total_time` bigint NOT NULL DEFAULT '0' COMMENT '总时长,毫秒',
`total_request_count` bigint NOT NULL DEFAULT '0' COMMENT '总调用次数',
`success_count` bigint NOT NULL DEFAULT '0' COMMENT '成功次数',
`error_count` bigint NOT NULL DEFAULT '0' COMMENT '失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_routeid` (`route_id`,`instance_id`) USING BTREE,
KEY `idex_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='接口监控信息';
8、监控错误日志
CREATE TABLE `monitor_info_error` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`error_id` varchar(64) NOT NULL DEFAULT '' COMMENT '错误id,md5Hex(instanceId + routeId + errorMsg)',
`instance_id` varchar(128) NOT NULL DEFAULT '' COMMENT '实例id',
`route_id` varchar(128) NOT NULL DEFAULT '',
`error_msg` text NOT NULL,
`error_status` int NOT NULL DEFAULT '0' COMMENT 'http status,非200错误',
`count` int NOT NULL DEFAULT '0' COMMENT '错误次数',
`is_deleted` tinyint NOT NULL DEFAULT '0',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_errorid` (`error_id`) USING BTREE,
KEY `idx_routeid` (`route_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
9、资源表
CREATE TABLE `resource` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL DEFAULT '' COMMENT '资源名称',
`content` varchar(128) NOT NULL DEFAULT '' COMMENT '资源内容(URL)',
`ext_content` text,
`version` varchar(32) NOT NULL DEFAULT '' COMMENT '版本',
`type` tinyint NOT NULL DEFAULT '0' COMMENT '资源类型:0:SDK链接',
`is_deleted` tinyint NOT NULL DEFAULT '0',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='资源表';
方法名
1、 增加 @interface Open.class
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Open {
/**
* 接口名,如:fee.query
*/
String value();
/**
* 版本号,默认版本号是""
*/
String version() default "";
/**
* 忽略验证,业务参数除外
*/
boolean ignoreValidate() default false;
/**
* 指定接口是否需要授权才能访问,可在admin中进行修改
*/
boolean permission() default false;
/**
* 是否需要appAuthToken,设置为true,网关端会校验token是否存在
*/
boolean needToken() default false;
/**
* 定义业务错误码,用于文档显示
*/
BizCode[] bizCode() default {};
}
2、 增加@interface BizCode
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BizCode {
/**
* 错误码
*
* @return
*/
String code();
/**
* 错误描述
* @return
*/
String msg();
/**
* 解决方案
* @return
*/
String solution() default "";
}
- 实现方式, 在restfull 接口上增加 @Open("") 注解
@Open("fee.query")
@RequestMapping("/fee/query")
public ObjectResponse<QueryFeeResponse> request(ApiBaseRequest apiBaseRequest) {
return null;
}
- serviceName 名称规范
接口命名没有做强制要求,但我们还是推荐按照下面的方式进行命名:
接口名的命名规则为:服务模块.业务模块.功能模块.行为,如:
- fee.user.monthcard.query 计费服务.用户模块.月卡.查寻
如果觉得命名规则有点长可以精简为:服务模块.功能模块.行为,如fee.monthcard.query,前提是确保前缀要有所区分,不和其它服务冲突。
路由自动加载
- 应用服务监听 ApplicationListener 的ContextRefreshedEvent 事件
- 获取所有RequestMappingHandlerMapping 接口映射
- 组装ice_route 路由对象
- 将组装的路由对象列表 异步发送 给 base 项目
- base 项目把 路由信息存储到db,并使用消息总线 刷新gateway route信息
伪代码 :
@Slf4j
public class RequestMappingScan implements ApplicationListener<ContextRefreshedEvent> {
private RabbitSender rabbitSender;
private static final AntPathMatcher pathMatch = new AntPathMatcher();
private CenterScanProperties scanProperties;
public RequestMappingScan(RabbitSender rabbitSender, CenterScanProperties centerScanProperties) {
this.rabbitSender = rabbitSender;
this.scanProperties = centerScanProperties;
}
/**
* 初始化方法
*
* @param event
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
if (rabbitSender == null || scanProperties == null || !scanProperties.isRegisterRequestMapping()) {
return;
}
Environment env = applicationContext.getEnvironment();
// 服务名称
String serviceId = env.getProperty("spring.application.name", "application");
// 所有接口映射
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
// 获取url与类和方法的对应信息
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
List<RequestMatcher> permitAll = Lists.newArrayList();
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> m : map.entrySet()) {
RequestMappingInfo info = m.getKey();
HandlerMethod method = m.getValue();
if (method.getMethodAnnotation(Open.class) == null) {
// 忽略非open 接口
continue;
}
Set<MediaType> mediaTypeSet = info.getProducesCondition().getProducibleMediaTypes();
for (MethodParameter params : method.getMethodParameters()) {
if (params.hasParameterAnnotation(RequestBody.class)) {
mediaTypeSet.add(MediaType.APPLICATION_JSON_UTF8);
break;
}
}
list.add(convertApi(serviceId, mediaTypeSet, info, method, permitAll));
}
JSONObject resource = new JSONObject();
resource.put("application", serviceId);
resource.put("mapping", list);
log.info("ApplicationReadyEvent:[{}]", serviceId);
rabbitSender.sendMessage("", QueueConstants.QUEUE_SCAN_API_RESOURCE, resource);
}
}
gateway 处理
1、获取请求参数,验签
2、修改请求参数,路由转发。参考自:blog.csdn.net/fuck487/art…
3、接口监控处理
4、gateway 监听消息总线事件 自动刷新路由或者配置信息(路由信息、应用权限、ip黑名单)
门户网站
1、 申请应用信息
2、上传 应用公钥或者服务密钥
3、开发文档
- 请求地址
- 公共参数(pid、serviceName、 sign、 timestamp、signType )
- 请求业务参数说明
- 返回参数说明
- 请求示例
- 响应示例
- 异常示例
- 返回错误码说明
- API 调试工具
4、门户服务监听nacos 事件。业务启动时,门户服务获取 api-doc 文档,执行 http://{ip}:{port}/v2/api-docs ,讲swagge json 放入本地内存中。 当业务服务关闭时,删除内存中的 swagge json。
5、h5 重写swagge ui