Golang 10行代码,搞定对外提供 OpenAPI

open api

open api 是什么? Open API即开放API,也称开放接口 所谓的开放API(OpenAPI)是服务型网站常见的一种应用,网站的服务商将本身的网站服务封装成一系列API(Application Programming Interface,应用编程接口)开放出去,供第三方开发者使用,这种行为就叫作开放网站的API,所开放的API就被称做OpenAPI(开放API)。 (摘自百科,主要为了说一下概念)mysql

open api 设计常见问题

服务提供

通讯方式设计

常见的设计是: 必备的有:app key 与 app secret 签名 可选的有: 企业ID, 业务ID。 这些可选的东西通常用来区分业务领域或者权限而存在,确保不一样调用者领域与权限的隔离。git

app key 与app secret 是我见过最多见,也是必然会出现的一个设计, 在我对接过的十多种API(阿里、腾讯、小米)中, 这这些都是必备的。github

app key 的做用

主要是为了防止请求被篡改以及用户识别。通常一个 app key 是用于作用户识别的, 服务提供方通常会存放调用方的一些信息, 并能够经过这个App key 检索到。golang

app secret 的做用

app secret 主要是用来签名请求的, 签名过的请求若是被修改以后, 则签名就会发生变化,攻击者是不可能知道这个变化的结果的,这样能够有效防止攻击者的攻击。web

通常来说,会对以下领域进行签名:sql

  1. parameter (url 参数)
  2. header (请求头)

通常的流程以下:编程

  1. 用户在请求参数信息中加入时间信息
  2. 用户使用本身的 app secret 对请求进行签名
  3. 用户把签名的结果, app key 与其余参数一块儿放到请求里面传到服务提供方
  4. 服务方根据 app key找到本身存放的 app secret, 并对请求进行签名
  5. 比对签名信息是否一致, 不一致,则认认为请求受到了非法修改,直接拒绝服务。
  6. 校验时间信息, 确保时间信息是在容许的范围内, 不然拒绝提供服务。

如何颁发? 通常是由服务提供方生成这样的一个键值对, 并把键值对安全的递给调用方。 之后 app secret 不会出如今网络传输中,只会用于双方的签名校验。api

数据格式与文档

  1. 服务地址, 须要准确无误的告诉服务提供方的调用地址
  2. 环境设置 (通常API提供方会提供线上与线下两种途径一个用来线上使用,一个用来调试)
  3. 参数规定 (对于一个Open API通常来说参数都是固定的, 不能随意变更,不然会引发双方不小的争执)
  4. 结果格式 (结果格式,要写清楚全部出现的结果格式的可能性, 让调用者有办法能够提早对任何可能的结果进行处理)
  5. 示例代码 (一段示例代码, 对于开发者是很友好的,大部分开发者喜欢看到这个)

注意事项

  1. 安全防范
  2. 限流

秘钥对设计

安全防范

1. 存放攻击

这个是最多见的一种, 它不须要知道服务提供方与三方的秘钥, 只须要知道,双方的通讯方式, 经过分析这种通讯方式,来达到窃取信息,或者形成攻击的目的。 这种行为的防范方法也是有一些的: 在请求里面加上时间信息,对时间信息进行加密签名, 对时间设置可用时间段, 好比1分钟, 过了一分钟,攻击者获取到的信息就不能再用了。安全

2. 秘钥泄露

秘钥泄露对于不少企业来讲并不陌生, 一旦泄露对于双方的危害都很大, 并且若是不能及时发现会带来不小的损失, 这种人为泄漏, 不少时候并无太好的办法。 能够作的事情比较现实的就是更换秘钥。服务器

3. 穿透攻击

调用服务方提供的Open API的三方合做者, 有可能对服务提供方形成压力过大的攻击, 这种攻击主要来源于两种途径:

  1. 三方合做者应用程序问题致使请求放大, 致使的调用次数太高
  2. 三方合做者把相关接口直接暴露到外部, 这样会带来潜在的问题, 在攻击者识别这样的接口以后,疯狂发起攻击, 服务提供方的服务器会承受巨大压力,甚至crash

对于穿透的攻击: 服务提供方能作的最多见的方案就是限流, 另外就是流量识别, 在流量异常的时候对API接口自己作一些限制

固然安全防范确实还有不少须要考虑的点,通常都会结合攻击的特色, 对于攻击类型进行定制防范, 对于业界常见的防范措施,通常都是要默认加入的。

我的的开源项目

openapi 它主要解决的问题是: 简化服务端提供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
		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")
		})
	}

复制代码

是否是很简单,若是文中有误,或者缺失的内容欢迎各类批评教育。

若是您能读到这里, 我会感受到十分荣幸,谢谢您的关注。

谢谢支持

相关文章
相关标签/搜索