API接口开放平台
API开放平台
背景:
前端开发需要用到后台接口
使用现成的系统的功能
做一个API接口平台:
- 防止攻击(安全性)
- 不能随便调用(限制、开通)
- 统计调用次数
- 计费
- 流量保护
- API接入
项目介绍
做一个提供API接口调用的平台:
用户可以注册登录,开通接口调用权限。用户可以使用接口,并且每次调用会进行统计。
管理员可以发布接口、下线接口、接入接口,以及可视化接口的调用情况、数据。
业务流程
技术选型
前端
Ant Design Pro
React
Ant Design Procomponents
Umi
Umi Request(Axios的封装)
后端
Spring Boot
Spring Boot Starter(SDK开发)
网关、限流、日志实现 —–> dubbo+nacos
项目开发的阶段划分
1、初始化和展示
项目介绍、设计、技术选型
基础项目的搭建
接口管理
用户查看接口
2、接口调用
- 继续开发接口管理前端页面
- 开发模拟API接口
- 开发调用这个接口的代码
- 保证调用的安全性(API签名认证)
- 客户端SDK的开发
3、接口计量与保护
- 开发接口发布/下线的功能(管理员)
- 前端去浏览接口、查看接口文档、申请签名(注册)、在线调试(用户)
- 统计用户调用接口次数
- 优化系统 - API网关
开发接口发布/下线的功能(管理员)
发布接口(仅管理员可用):
- 检查该接口是否存在
- 判断该接口是否可以调用
- 修改接口数据库中的状态字段为1
下线接口:
- 检查该接口是否存在
- 判断该接口是否可以调用
- 修改接口数据库中的状态字段为0
前端去浏览接口、查看接口文档
–动态路由,用url来传递id,加载不同的接口信息
申请签名(注册) :
用户在注册成功时,自动分配 accessKey、secretKey
扩展:用户可以申请更换签名
在线调用
请求参数的类型(直接用 json 类型)
1 | [ |
先跑通整个流程,再去针对不同的请求或接口类型来设计界面和表单,给用户更好的体验(可以参考postman)
调用流程:
使用走后端方式流程:(后端相当于中转站,防止用户直接拿到接口的地址,从而绕过系统自行调用)

流程:
1. 前端将用户输入的请求参数和要测试的接口发送给平台后端
2. (在调用前可以做一些调用)
3. 平台后端去调用模拟接口
1 | //请求参数要注意大小写! |
TODO
判断该接口是否可以调用时 由固定方法名改为根据测试地址来调用
用户测试接口 由固定方法名->根据测试地址来调用
模拟接口改为从数据库校验 akey
4、管理、统计分析
开发接口调用次数的统计
优化整个系统的架构
a. 网关是什么?
b. 网关的作用?
c. 网关的应用场景及实现
d. 结合业务去应用网关
接口调用次数统计
1、需求:用户每次调用接口成功,次数+1
2、给用户分配或者用户自主申请接口调用次数
业务流程:
1、用户调用接口
2、修改数据库,调用次数+1
5、鉴权
- 实现统一的用户鉴权、统一的接口调用次数统计(把API网关应用到项目中)
- 用到的网关相关特性
- 路由:用户原本直接请求模拟接口,在模拟接口鉴权;现在,我们在网关对用户进行鉴权,网关鉴权通过后再将请求重定向到模拟接口。
- 统一鉴权(accessKey、secretKey)
- 统一业务处理(每次请求接口后,调用次数都要+1)
- 访问控制(黑白名单)
- 流量染色(记录请求是否为网关来的,但是这样的话,在最终接口处还要对请求进行判断,看看有没有被染色)
- 统一日志(记录每次的请求与响应)
- 业务逻辑
- 用户发送请求到API网关
- 请求日志
- (黑白名单)
- 用户鉴权(判断ak、sk是否合法)
- 请求的模拟接口是否存在?
- 请求转发,调用模拟接口
- 响应日志
- 调用成功,接口调用次数+1
- 调用失败,返回一个规范的错误码
- 用到的网关相关特性
6、网关
- 补充完整网关的业务逻辑(怎么去操作数据库、怎么复用之前的方法——RPC)
- 完善系统,开发一个监控统计功能
网关业务逻辑
问题:网关项目比较纯净,没有操作数据库的包、并且还要调用我们之前写过的代码?复制粘贴维护麻烦
理想:直接请求到其他项目的方法
怎么调用其他项目的方法?:
- 复制代码、环境
- HTTP请求(提供一个接口、供其他项目使用)
- server开发一个接口(地址、请求方法、参数、返回值)
- client使用HTTP Client之类的代码包去发送HTTP请求
- RPC
- 作用:像调用本地方法一样调用远程方法
- 对开发者更透明,减少了很多沟通成本
- RPC向远程服务器发送请求时,未必要使用HTTP协议,比如 TCP/IP(性能更高、内部服务更适用)等等等等
- 把公共项目打包成jar包,其他项目去引用(客户端SDK)

具体实现:
- 请求转发
用一个前缀匹配断言:所有路径为 /api/** 的请求转发到http://localhost:8123/api/**
例如:http://localhost:8090/api/name/** ===> http://localhost:8123/api/name/**
模拟接口请求地址:http://localhost:8123/api/name/get?name=123
1 | spring: |
编写业务逻辑
使用了GlobalFilter全局请求拦截处理(类似AOP)
因为网关项目没引入MyBatis等操作数据库的类库,如果该操作较为复杂,可以由backend增删改查项目提供接口,我们直接调用,不用再重复写逻辑。
- HTTP请求 (用HTTPClient、用RestTemplate、Feign)
- RPC (Dubbo)
问题
预期是等模拟接口调用完成,才调用响应日志、统计调用次数
但实际上 chain.filter 方法立即返回了,直到filter过滤器 return 后才调用了模拟接口
原因:chain.filter 是一个异步操作,理解为前端的 promise
解决方案:利用response装饰者,增强原有的response的处理能力
Dubbo框架(RPC实现)
两种使用方式:
Spring Boot 代码(注解 + 编程式):写java接口,服务提供者和消费者都去引用这个接口
IDL (接口调用语言):创建一个公共的接口定义文件,服务提供者和消费者读取这个文件,优点是跨语言、所有的框架都认识
底层是Triple协议
整合运用:
- backend项目作为服务提供者,提供三个方法
- 去数据库中查是否已分配给用户
- 查询数据库,模拟接口是否存在,以及请求方法是否匹配
- 调用成功后 调用次数+1 invokeCount
- Gataway项目作为服务调用者,调用这三个方法
7、完善网关、接口调用信息可视化、展望……
- 完成网关业务逻辑
- 实际情况应该是去数据库查看是否已分配给用户(ak、sk是否合法)
- 先根据accessKey判断用户是否存在,查到secretKey
- 对比secretKey和用户传的加密后的secretKey是否一致
- 从数据库查询模拟接口是否存在,以及请求方式是否匹配(还可以校验请求参数)
- 调用成功后,调用次数+1 (invoke count
- 实际情况应该是去数据库查看是否已分配给用户(ak、sk是否合法)
- 开发管理员分析的功能
- 上线
如何获取接口转发服务器的地址
网关启动时, 获取所有的接口信息,维护到内存的hashmap中;有请求时,根据请求的url路径或者其他参数[比如host请求头]来判断应该转发到哪台服务器,以及用于校验接口是否存在
公共服务:
目的是让方法、实体类在多个项目间复用,重复编写
1. 数据库中查询是否已分配给用户密钥(accessKey,返回用户信息,为空表示不存在)
1. 从数据库查询模拟接口是否存在(请求路径、请求方法、请求参数、返回接口信息,为空表示不存在)
1. 调用次数+1 (accessKey(标识用户)、接口路径)
步骤:
- 新建干净的maven项目,只保留必要的公共依赖
- 抽取service和实体类
- install本地 maven包
- 让服务提供方引入common包,测试是否正常运行
- 让服务消费方引入common包
扩展
怎么让其他用户也上传接口?
需要提供一个机制(界面),让用户输入自己的接口host(服务器地址)、接口信息,将接口信息写入数据库。
将接口信息写入数据库之前,要对接口进行校验,比如测试调用,保证接口正常。并遵循我们项目的要求(并且使用我们的sdk)
在interfaceInfo表中加入host字段,区分服务器地址,让接口提供者更灵活地接入系统。
网关判断是否还有调用次数
网关限流、提高性能等等
开发统计分析:
提供可视化平台,用图表的方式展示所有的调用情况,便于调整业务
需求:
各接口总调用次数占比(饼图)前3的接口,从而分析出哪些接口没人用(降低资源或者下线)以及高频接口(增加资源、提高收费)
实现:
前端:
展示饼图——推荐使用线程的库
ECharts、AntV
- 看官网
- 进入实例页面
- 找到想要的图
- 在线调试
- 复制代码
- 改为真实数据
后端:
1. SQL查询调用数据
select interfaceInfoId, sum(totalNum) as totalNum from interface_info group by interfaceInfoId order by totalNum desc limit 3;
2. 业务层去关联查询接口信息
上线:
前端:参考用户中心的上线方式
后端:
- backend项目:web项目,部署springboot的jar包(对外的)
- gateway项目:web项目,部署springboot的jar包(对外的)
- interface 模拟接口项目:web项目,部署springboot的jar包(不建议对外暴露的)
如果自己学习用:单个服务器部署这三个项目足够
如果想搞大事:多个服务器建议在同一内网,内网交互会更快,且更安全
设计库表:
哪个用户?哪个接口?
用户 <==多对多==> 接口
因此需要再创建一个表:
用户调用接口关系表
1 | -- 用户调用接口关系表 |
步骤:
1. 开发基本增删改查(给管理员用)
1. 开发用户调用接口次数+1的功能(service)
问题:
——如果每个接口的方法都写调用次数+1,是不是比较麻烦?
——致命问题:接口开发者需要自己去添加统计代码
使用AOP切面的优点:独立于接口,在每个接口调用后统计次数+1
AOP切面的缺点:存在于单个项目中,如果每个团队都要开发自己的接口,那么都需要写AOP切面—还是没有解决 问题
网关相关知识:
网关的优点:统一进行一些操作,处理一些问题
路由
起到转发的作用,比如有接口 A 和接口 B,网关会记录这些信息,根据用户访问的地址和参数,转发请求到对应的接口(服务器/集群)
/a=>接口A
/b=>接口B
统一鉴权
- 判断用户是否有权限进行操作,无论访问什么接口,都统一去判断,不用重复写
统一跨域处理
- 网关统一处理跨域
缓存
流量染色
- 给请求(流量)添加一些标识,一般是设置在请求头中,添加新的请求头
访问控制
- 黑白名单,比如限制DDOS IP
统一业务处理
- 把每个项目中都要做的通用逻辑放到上层(网关),统一处理,比如本项目的接口调用次数统计
发布控制
- 灰度发布,比如上线新接口,先给新接口分配20%流量,慢慢增加,最终替代老接口
负载均衡
- 在路由的基础上
/c=>服务 A / 集群A(随机转发到其中的某一个机器)
- 统一接口保护
- 限制请求
- 信息脱敏
- 降级(熔断)
- 限流
- 超时时间
- 统一日志
- 统一的请求、响应信息记录
- 统一文档
- 将下游项目的文档进行聚合,在一个页面统一查看
网关的分类
- 全局网关(接入层网关):作用是负载均衡、请求日志等,不和业务逻辑绑定
- 业务网关(微服务网关):会有一些业务逻辑,作用是将请求转发到不同的业务/项目/接口/服务
实现:
Nginx( 全局网关 )、Kong网关( API网关 Kong: https://github.com/Kong/kong ),编程成本高
Spring Cloud Gateway (取代了 Zuul) 性能高,可以用Java代码来写逻辑,易于学习
参考文章:https://blog.csdn.net/qq_21040559/article/details/122961395
网关技术选型:https://zhuanlan.zhihu.com/p/500587132
Spring Cloud Gateway用法
官网:https://spring.io/projects/spring-cloud-gateway
核心概念
路由:根据什么条件,转发请求到哪里
断言:一组规则、条件,用来确定如何转发路由
过滤器:对请求进行一系列处理,比如添加请求头、添加请求参数
请求流程:
- 客户端发起请求
- Handler Mapping:根据断言,去将请求转发到对应的路由
- Web Handler:处理请求(一层层经过过滤器)
- 实际调用服务
两种配置方式:
- 配置式(application.yaml中配置) 方便、规范
- 编程式 相对麻烦、灵活
断言:(一些条件)
- after:在什么时间之后
- before……
……等等多种因素都能放进来考虑
建议开启日志——方便发现问题:
1 | logging: |
过滤器:
基本(对请求头、响应头进行增删改查)
在某设定路由下,做自定义的事(如添加请求头、添加请求体参数等等等等)
或者 访问某路由如果失败,会转而访问另一个设定的路由(降级)
需求分析
- 管理员可以对接口信息进行增删改查
- 用户可以访问前台,查看接口信息
数据库表设计
接口信息表
id
name 接口名称
description 描述
url 接口地址
type 请求类型
requestHeader 请求头
responseHeader 响应头
status 接口状态 0-关闭 1-开启
isDelete
createTime
updateTime
1 | use tjyapi; |
前端项目脚手架:
1 | 1. 在终端运行 |
1 | 版本信息: |
运行一下空项目:
前端:ant design pro 脚手架
后端:直接使用模板 springboot-init
基础功能
增删改查、登录(复制粘贴)
前端接口调用:openapi插件自动生成
openapi规范
模拟接口项目 tjyapi-interface
提供三个模拟接口
- GET接口
- POST接口(url传参)
- POST接口(Restful)
调用接口
几种 HTTP 调用方式:
HttpClient
RestTemplate
第三方库(OKHTTP、Hutool)
Hutool:https://www.hutool.cn
1 | <dependency> |
https://doc.hutool.cn/pages/HttpUtil/#概述
API签名认证
本质:
1. 签发签名
1. 使用签名(校验签名)
为什么需要?
- 保证安全性,不能随便来一个人都能调用
怎么实现?
通过http request header头 传递参数
参数1: accessKey:调用的标识 userA、userB (复杂、无序、无规律)
参数2: secretKey: 密钥——–该参数不传递到请求头中,通过私下约定进行同步
可以暂时理解为用户名和密码,区别为:ak、sk是无状态的;你每次访问都需携带正确的ak、sk才能访问
自行编写代码给每个用户生成ak、sk
千万不能把密钥直接在服务器之间传递,因为有可能被拦截
(所以要对密码进行进一步处理)
参数3: 用户请求参数
参数4: sign 参数
加密方式:对称加密、非对称加密、md5签名
用户参数:abc + 密钥 => 签名生成算法 => 不可解密的值
abc + miyao => dnasufbnusfuisdafiuadbfouiadbfuiasdbofibdaiksfbefewdf
服务端用一样的方式生成签名,只要结果和用户传来的一致,就表示一致
怎么防重放?
参数5: 每次请求时都 + nonce随机数,只能用一次
服务端要保存用过的随机数
参数6: 加timestamp时间戳,校验时间戳是否过期
API签名认证是一个很灵活的设计,具体有哪些参数,参数名如何 一定要根据具体场景来,(比如userId、appId、version、固定值等等)
思考:开发者每次调用接口都要自己写签名算法?
——开发一个简单易用的SDK
理想情况:开发者只需要关心调用哪些接口、传递哪些参数,就跟调用自己写的代码一样简单。
——开发一个starter的好处:
开发者引入后,可以直接在application.yaml中写配置,自动创建客户端
spring-boot-configuration-processor的作用是自动生成配置的代码提示