由于最近项目中需要搭建一个网关,在大佬的建议和自己调研下,最终使用了Kong(其实主要因为我只是个菜鸡前端,用这个比较简单,基本只要调API就完事了 = ̄ω ̄= )。Kong是一款可扩展性和灵活性都很高的开源API网关,可以通过添加Kong节点水平扩展服务,可以通过Lua编写的插件扩展已有功能(官方已经提供了很多实用的插件),本文介绍的就是通过添加OAuth2和Rate Limiting插件实现API网关鉴权与调用频率限制,更多丰富的功能参考官方文档。
写这篇文章的原因是由于在搭建网关过程中遇到了些问题,解决问题时发现Kong的旧版本与新版本差异很大,而国内外网上的例子大多是基于旧版本的或者内容比较简单,gitHub的Issue中也有很多基于旧版本的回答无法用来参考,并且官方文档中有些地方对于新手比较容易产生误解,所以想写篇简单清晰的例子提供下参考,有什么不对的地方也请指正!
注意事项
- Kong:本文是基于1.4版本的,Kong新旧版本差异有点大,所以过一阵可能也不适用了......
- OAuth2:新版本插件要求必须使用HTTPS请求获取token,所以必须要有域名与SSL证书(个人是使用Let’s Encrypt申请的免费证书,不过好像也有基于IP的SSL证书,没实践过,这里不做更多介绍),旧版本没有硬性要求所以网上有些例子用的HTTP请求也可以获取到token。
Kong部署
本文是介绍部署在docker中的例子,可以在DockerHub中查看更详细的介绍,更多部署方式参考官方文档。
1.创建一个Docker网络,以使容器能够发现彼此并进行通信
docker network create kong-net
2.创建Postgres数据库(也可以使用Cassandra,有个坑是用Postgres12会报错,11.5则不会)
docker run -d --name kong-database \
-p 5432:5432 \
-e "POSTGRES_USER=kong" \
-e "POSTGRES_DB=kong" \
postgres:9.6
3.准备数据库
docker run --rm \
--link kong-database:kong-database \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
kong kong migrations bootstrap
4.运行一个Kong容器
- 8000:监听来自客户端的HTTP请求,匹配路由后转发请求到具体的service上
- 8443:监听来自客户端的HTTPS请求,匹配路由后转发请求到具体的service上
- 8001:Kong的Admin API的HTTP接口,用于管理Kong平台,例如服务于路由的注册、插件的添加与配置
- 8444:Kong的Admin API的HTTPS接口,用于管理Kong平台,例如服务于路由的注册、插件的添加与配置
docker run -d --name kong \
--link kong-database:kong-database \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
-p 8000:8000 \
-p 8443:8443 \
-p 8001:8001 \
-p 8444:8444 \
kong
到这我们就搭好了个基本的单节点Kong网关,Kong只有企业版才提供GUI来管理API,gitHub上有开源的第三方仓库的GUI工具,不过只提供了些基本功能,生产环境也用不上,毕竟管理API的端口不应该暴露出来,只该在内部调用,有兴趣可以看看konga和kong-dashboard(有个坑是使用konga时数据库主机地址要填公网IP)。
插件使用
搭好网关后我们就可以开始通过插件来扩展些我们需要的功能了,Kong的插件可以用于全局、服务、路由和消费者,同时在不同地方应用相同插件时是有优先级的,详情参考官方文档的Admin API下的Precedence,以下分别以全局插件(Rate Limiting例子)和服务插件(OAuth2例子)做介绍,OAuth2插件文档中还是有些地方比较容易让我这样的新手误解的......
OAuth2(Client Credentials模式)
官方插件文档、官方接口文档
个人喜欢使用postman调用Admin API,域名已做处理,官方文档和网上不少例子是用curl命令调用的,看个人习惯吧,自行转换,反正最后都是通过代码调,以下是示例中用到的几个地址,自行替换为自己的地址
- 测试用接口:http://xxx.53.189.48:3001/login/local
- Kong的Admin API的HTTP接口地址:http://xxxxx.org:8001
- 网关的HTTP接口地址:https://xxxxx.org:8000
- 网关的HTTPS接口地址:https://xxxxx.org:8443
1.需要准备个测试用接口,当作一个service,在这我用项目的单点登录接口做演示,以下是正常调用返回结果,表示接口可用
2.通过接口上传所需的SSL证书,以便我们可以调用网关的HTTPS接口地址
3.创建一个service(通常是一个路由对应一个微服务,所以service只能指定一个path,如果一个微服务对应多个路由的话,则在定义route时需要设置strip_path为false,使匹配到的路由添加到上游服务的path中),当这个service被调用时,会将请求转发到http://xxx.53.189.48:3001/login/local
4.通过 http://xxxxx.org:8001/services/{上一步创建service时指定的name或返回的service id}/routes 创建一个route,创建完route后,我们就可以通过 http://xxxxx.org:8000/sso/login/local 调用service,默认配置下,当请求转发到上游服务时,会将这个route的path去掉,如例子中请求http://xxxxx.org:8000/sso/login/local转发到service后,会将/sso/login/local去掉,然后使用service的path调用具体服务,即http://xxx.53.189.48:3001/login/local;route的methods、paths和hosts都可以指定多个,从而可以通过多个url调用此服务。
5.为service添加一个OAuth2插件,再次调用http://xxxxx.org:8000/sso/login/local后就会返回错误,提示缺少token
6.当我们添加插件后,插件将会监听/oauth2/authorize,/oauth2/token,/oauth2_tokens,/oauth2_tokens/:token_id等路由,我们需要自己创建route映射到这些路由,这是容易误解的地方之一,之前一直直接调用https://xxxxx.org:8443/oauth2/token期望返回token(这里要注意使用的是网关的HTTPS接口地址,而不再是Kong的Admin API的HTTP接口地址),但是一直提示"Not found"、"no Route matched with those values"等错误
7.创建一个consumer,consumer作为消费者通常与用户相关联,我们该为每个用户创建一个consumer
8.通过 http://xxxxx.org:8001/consumers/{上一步创建consumer后返回的id}/oauth2 为这个consumer创建一个应用,应用会返回client_secret与client_id用于申请token
9.调用第5步中创建route时指定的path,即https://xxxxx.org:8443/sso/oauth2/token,与上一步生成的client_secret与client_id申请token
10.然后在请求头中加入Authorization: Bearer {token},发现调用成功
Rate Limiting
官方插件文档、官方接口文档
1.在全局添加插件Rate Limiting,指定每分钟限制调用一次
2.然后在一分钟内两次调用测试接口,返回错误"API rate limit exceeded",即表示限制成功
3.我们希望指定用户可以多次调用时,可以通过在consumer上添加相同插件,由于consumer上的插件优先级高于全局插件,所以指定的用户可以越过全局插件限制多次调用
4.再次调用测试接口发现一分钟内调用10次才返回错误
待更新补充~