Golang10⾏代码,搞定对外提供OpenAPI
open api
open api 是什么? Open API即开放API,也称开放接⼝ 所谓的开放API(OpenAPI)是服务型⽹站常见的⼀种应⽤,⽹站的服务商将⾃⼰的⽹站服务封装成⼀系列API(Application Programming Interface,应⽤编程接⼝)开放出去,供第三⽅开发者使⽤,这种⾏为就叫做开放⽹站的API,所开放的API就被称作OpenAPI(开放API)。 (摘⾃百科,主要为了说⼀下概念)
open api 设计常见问题
服务提供
通信⽅式设计
常见的设计是: 必备的有:app key 与 app secret 签名 可选的有: 企业ID, 业务ID。 这些可选的东西⼀般⽤来区分业务领域或者权限⽽存在,确保不同调⽤者领域与权限的隔离。
app key 与app secret 是我见过最常见,也是必然会出现的⼀个设计, 在我对接过的⼗多种API(阿⾥、腾讯、⼩⽶)中, 这这些都是必备的。
app key 的作⽤
主要是为了防⽌请求被篡改以及⽤户识别。⼀般⼀个 app key 是⽤于做⽤户识别的, 服务提供⽅⼀般会存放调⽤⽅的⼀些信息, 并可以通过这个App key 检索到。
app secret 的作⽤
app secret 主要是⽤来签名请求的, 签名过的请求如果被修改之后, 则签名就会发⽣变化,攻击者是不可能知道这个变化的结果的,这样可以有效防⽌攻击者的攻击。
⼀般来讲,会对如下领域进⾏签名:
1. parameter (url 参数)
2. header (请求头)
⼀般的流程如下:
1. ⽤户在请求参数信息中加⼊时间信息
2. ⽤户使⽤⾃⼰的 app secret 对请求进⾏签名
3. ⽤户把签名的结果, app key 与其他参数⼀起放到请求⾥⾯传到服务提供⽅
4. 服务⽅根据 app key到⾃⼰存放的 app secret, 并对请求进⾏签名
5. ⽐对签名信息是否⼀致, 不⼀致,则认认为请求受到了⾮法修改,直接拒绝服务。
6. 校验时间信息, 确保时间信息是在允许的范围内, 否则拒绝提供服务。
如何颁发? ⼀般是由服务提供⽅⽣成这样的⼀个键值对, 并把键值对安全的递给调⽤⽅。 以后 app secret 不会出现在⽹络传输中,只会⽤于双⽅的签名校验。
数据格式与⽂档
1. 服务地址, 需要准确⽆误的告诉服务提供⽅的调⽤地址
2. 环境设置 (⼀般API提供⽅会提供线上与线下两种途径⼀个⽤来线上使⽤,⼀个⽤来调试)
3. 参数规定 (对于⼀个Open API⼀般来讲参数都是固定的, 不能随意变动,否则会引起双⽅不⼩的争执)
4. 结果格式 (结果格式,要写清楚所有出现的结果格式的可能性, 让调⽤者有办法可以提前对任何可
能的结果进⾏处理)
5. ⽰例代码 (⼀段⽰例代码, 对于开发者是很友好的,⼤部分开发者喜欢看到这个)
注意事项
1. 安全防范
2. 限流
秘钥对设计
安全防范
1. 存放攻击
这个是最常见的⼀种, 它不需要知道服务提供⽅与三⽅的秘钥, 只需要知道,双⽅的通信⽅式, 通过分析这种通信⽅式,来达到窃取信息,或者造成攻击的⽬的。 这种⾏为的防范⽅法也是有⼀些的: 在请求⾥⾯加上时间信息,对时间信息进⾏加密签名, 对时间设置可⽤时间段, ⽐如1分钟, 过了⼀分钟,攻击者获取到的信息就不能再⽤了。
2. 秘钥泄露
秘钥泄露对于很多企业来说并不陌⽣, ⼀旦泄露对于双⽅的危害都很⼤, ⽽且如果不能及时发现会带来不⼩的损失, 这种⼈为泄漏, 很多时候并没有太好的办法。 可以做的事情⽐较现实的就是更换秘钥。
3. 穿透攻击
调⽤服务⽅提供的Open API的三⽅合作者, 有可能对服务提供⽅造成压⼒过⼤的攻击, 这种攻击主要来源于两种途径:
1. 三⽅合作者应⽤程序问题导致请求放⼤, 导致的调⽤次数过⾼
2. 三⽅合作者把相关接⼝直接暴露到外部, 这样会带来潜在的问题, 在攻击者识别这样的接⼝之后,疯狂发起攻击, 服务提供⽅的服
务器会承受巨⼤压⼒,甚⾄crash
对于穿透的攻击: 服务提供⽅能做的最常见的⽅案就是限流, 另外就是流量识别, 在流量异常的时候对API接⼝本⾝做⼀些限制
当然安全防范确实还有很多需要考虑的点,⼀般都会结合攻击的特点, 对于攻击类型进⾏定制防范, 对于业界常见的防范措施,⼀般都是要默认加⼊的。
个⼈的开源项⽬
它主要解决的问题是: 简化服务端提供API的流程
1. 封装服务端API签名流程
2. 封装服务端颁发键值对的流程
3. 封装服务端签名校验机制
4. 提供简单的安全防范
这个⼯具是⽤golang设计的, 因此只适⽤于golang的项⽬。 由于这个⼯具是基于 http.Request 进⾏设计的, 因此理论上讲兼容所有的web框架, 如 gin, iris, beego等, 我在readme⾥⾯提供了iris的⽰例代码
如果使⽤mysql来存放app key 和secret信息, 可以建如下的⼀个表
CREATE TABLE `app` (
`app_key` varchar(32) NOT NULL,
`app_secret` varchar(128) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`app_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码
如果已经存在这样⼀个类似的表也可以不⽤建⽴,在代码中指定即可
r, err := openapi.CheckValid(req,
// default implementation is via sql, to fetch the secrect
openapi.SqlSecretKeeper{
Db:        store.GetDb(),  // 可以使⽤的 mysql 连接
TableName: "app",      // 存放app key 和secrets的表名
KeyCol:    "app_key",  // app key 的列名
SecretCol: "app_secret", // app secret的列名
AppKey:    k,          // ⽤户使⽤的 app key
})
复制代码
当然如果您已经封装好了app key 与secret的逻辑, 也可以⾃⼰实现如下接⼝
// the interface to get the secret
type SecretKeeper interface {
GetSecret() (string, error)
}
复制代码
对于使⽤了web 框架的,只需要写⼀个middleware, 并启⽤就⾏了, ⽰例代码如下:
创建middleware
// create a middle ware for iris
func OpenApiHandler(ctx iris.Context) {
//sign header? to prevent header being modified by others
// openapi.SignHeader(true)
req := ctx.Request()
// you can put the key somewhere in the header or url params
k := ctx.URLParam("app_key")
r, err := openapi.CheckValid(req,
// default implementation is via sql, to fetch the secrect
openapi.SqlSecretKeeper{
Db:        store.GetDb(),
TableName: "app",      // the name of table where you store all your app  keys and  secretcs            KeyCol:    "app_key",  // the column name of the app keys
SecretCol: "app_secret", // the column name of the app secrets
AppKey:    k,          // the app key that the client used
})
logError(err)
if r {
// verfy success, continue the request
百度api接口
ctx.Next()
} else {
// verify fail, stop the request and return
ctx.Text(err.Error())
ctx.StopExecution()
return
}
}
复制代码
启⽤middleware
/
/ use the middle ware somewhere
// so all the apis under this group should be
// called with signed result and app key
openApiGroup := app.Party("/open")
openApiGroup.Use(OpenApiHandler)
{
openApiGroup.Get("/app", func(ctx iris.Context) {
ctx.Text("success")
})
}
复制代码
是不是很简单,如果⽂中有误,或者缺失的内容欢迎各种批评教育。如果您能读到这⾥, 我会感觉到⼗分荣幸,谢谢您的关注。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。