«

»

十二 20 2016

基于 Kong 建立支持 OAuth2.0 的 API 网关

有些时候你会为一个产品制作很多功能,而这些功能往往需要相同的用户认证系统,相同的接口统计系统。为每个功能都复制模块代码实在不是什么好主意。

这个时候,你需要一个 API 网关,统一管理各个接口的各种功能,比如 SSL 证书,OAuth2 验证等。

本文编写时 Kong 版本为 0.9.5,本文适用于 0.9.5-0.9.9 之间的版本。目前 Kong 已升级到 0.10,API 变化很大,本文的部分内容将不再适用,请读者注意!

概念

  • Kong

Kong 是一个开源的 API 网关,你也可以认为他是一个 API 中间件。对于一个 API 中间件,Kong 将此思维发挥到了极致,所有对 Kong 配置的增加和修改,也都是依靠 RESTful API 来操作。

Kong 非常适合于管理后端基于 Docker 等以微服务形式实现的 API 接口。官网:https://getkong.org/

本文将基于 Kong 0.9.5 版本进行实现。

  • OAuth2

OAuth2 我就不讲是什么了

实现 Kong 完成 API 转发

需求和现状

我们将要为以下服务实现 API 请求转发:

  • Web 服务 X,地址为 10.0.0.100:8081
  • Web 服务 Y,地址为 10.0.0.100:8082
  • Web 服务 Z,地址为 10.0.0.100:8083,此服务器提供用户信息服务,可用于保存或验证用户的账号密码。

Kong 服务器地址:10.0.1.0,域名:www.your-kong-domain.com

安装和启动

本文的安装全部使用 Docker 进行安装。对于 Docker 的使用方式,请参见我之前的文章

  • 安装并启动 cassandra 数据库

注意这个服务启动的速度非常慢,我在阿里云上启动这个服务,9042 端口生效需要 18 秒(cassandra 官网给出的 wait-for-it.sh 只等了 15 秒就受不了了),启动真的很慢,太着急而启动 Kong 服务 的话会出现数据库连接失败而推出的问题。

  • 安装并启动 Kong

这个服务启动也很慢,急着进行下一步启动 kong-dashboard 连接端口也会报错,不能着急。kong 的 docker 版本做的还不够好,日志并没有连到标准输入输出上,需要的话可以进入容器内查看。日志地址:/usr/local/kong/logs/

  • 安装并启动 kong-dashboard

由于 Kong 对 RESTful API 的极致使用简直到了强迫症的程度,所以对于某些操作,有一个高效的 UI 界面要比在纯命令行模式下更容易观察和操作。

kong-dashboard 是一个开源的 Kong UI 管理工具。其 UI 使用的框架是我非常喜欢的 Materialize。项目地址:https://github.com/PGBI/kong-dashboard

打开 Kong 服务器的 8080 端口,就可以进入 kong-dashboard 的界面了。

填写我们的 Kong 管理地址和端口 8001,就可以进入管理页面了。

策略模式

官方文档:https://getkong.org/docs/0.5.x/admin-api/

为了便于理解,将 Kong 目前能实现模式简单分为两种,以便于理解:Request host 和 Request path。

Request host:

要在 Kong 服务器上绑定多个域名。(提供 api 服务的服务器可以没有域名绑定,只要有 IP 即可)

Request path

Kong 服务器上只需要绑定一个域名,不同的 API 集合依靠不同的请求路径实现。(同上,提供 api 服务的服务器可以没有域名绑定,只要有 IP 即可)

Request host 和 Request path 可组合使用,但比较复杂,不建议使用。

新建 API 策略

我们新建一条名为 service1 的策略,将请求转发到 http://110.0.0.100:8081

两个参数:

  • strip_request_path:在使用 Request path 模式会用到的参数。

如果开启时效果是这样的:

不开启时则是:

根据你后端进行设计和配置。

我们这次使用的是 Request host,用不到这个参数。(默认不开启)

  • preserve_host:不开启此项的话,API 服务器收到的请求中,HOST 参数和 SERVER_NAME 为 API 服务器。开启则为 Kong 服务器绑定的域名。(默认不开启)

点击保存后,访问 kong.yourname.com,就能打开 http://110.0.0.100:8081 了。

开启 HTTPS

使用 OAuth2 的前提便是你的接口是 HTTPS 的,所以我们先开启 HTTPS。

貌似 kong-dashboard 的 ssl 插件也可以实现,不过为了理解下 Kong 的 RESTFUL API,所以添加 SSL 证书的步骤,我们全部都采用使用命令行添加的方式。

Let’s encrypt 人工申请证书

(如果暂时没有域名,或者不打算麻烦 Let’s encrypt 或其他认证机构,也可以跳过,kong 的 https 端口上默认绑着 localhost 的自签证书用于开发环境和测试。可以跳过此段)

虽然 Let’s encrypt 提供了非常多的方法让大家申请可信任的 ssl 证书,然而我还是更喜欢人工申请证书文件,手动导入到服务器。这么做比较有真实感,另外因为服务器总要换来换去,总麻烦人家 Let’s encrypt 也不好,还是证书文件最实在。

这里就不解释申请的方法了。

 

最终你会得到四个文件:

证书文件、链文件、全链文件、私钥文件。证书和私钥就不解释了,全链文件就是证书和私钥加一起,适用于比较新的 http 服务器。链文件是啥至今没搞懂,也不知道在哪用。

使用 SSL 插件导入证书

注意:SSL 插件只适用于 request_host 模式!!!

访问你 API 的 HTTPS 地址:https://api.your-kong-domain.com/node1/,检查证书绑定情况。

对于 request_path 模式,不需要使用插件,只需要修改 Kong 的配置文件即可。这个部分没有提供 API,需要手动修改 kong.conf。相关文档:https://getkong.org/docs/0.9.x/configuration/#ssl_cert_path

实现 OAuth2 验证

我们来实现一个 WebApp 常用的 密码模式(Resource Owner Password Credentials Grant)OAuth2 接口验证。

这个实例与官方实例略有不同。官方的用户认证服务器在 Kong 的外侧,用户直接访问。而我们的例子中,用户认证服务器在 Kong 的内侧,我们需要通过 Kong 进行访问。其他条件没有区别,整体核心思路是相同的。

整体流程图

解释:

  1. 用户发送账号密码到 Kong 服务器的用户验证 API;
  2. Kong 服务器将请求 1 转发给用户验证服务器;
  3. 用户认证服务器验证用户账号密码是否正确,如果不正确直接返回错误结果并完成用户认证;如果正确则将:用户账号密码client_idclient_secretscopeprovision_key用户 ID 发送给 Kong 的 OAuth2 接口
  4. Kong 生成 access_token 并返回给用户认证服务器,并缓存与过期时间相同的 scope用户 ID
  5. 认证服务器将 4 中收到的 access_token 返回请求 2 给 Kong,Kong 再返回请求 1 给用户。
  6. 用户得到 access_token 之后,就可以访问有 OAuth2 验证的接口了。access_token 中已隐含了用户 ID 和 scope。

实现

增加 consumer

添加一个 consumer,设置 Username:oauth_test,Custom id:100。点击保存。

回到 comsumers 列表,点击编辑刚才添加的 oauth_test。在最下方点击 OAUTH2,新建 oauth2 credentials。

  • Username:oauth_test_credential
  • Client_id:留空,自动生成
  • Client_secret:留空,自动生成
  • Redirect URI:密码模式在这里用不到,但是此字段不能为空,于是你随便填一个吧

点击创建。Kong 会自动生成 Client ID 和 Client Secret。这两个信息将用于我们的 WebApp。

为接口增加 OAuth2 验证

我们为 策略 node1:https://www.your-kong-domain.com/node1 → http://10.0.0.100:8081/node1 添加 OAuth2 验证。

  • mandatory_scope: 强制需要 scope,至少需要一个 scope 来验证用户权限
  • implicit_grant: 简化模式
  • hide_credentials: 隐藏凭据,开启后,后端 API 就看不到 access_token
  • password_grant: 密码模式
  • accept_http_if_already_terminated: 接受 HTTP 请求
  • client_credentials: 客户端模式
  • authorization_code: 授权码模式

我们需要勾选 hide_credentials 和 password_grant

准备用户认证服务器

新建一个 API 策略 auth:https://www.your-kong-domain.com/auth/ → http://10.0.0.100:8083/auth/

并且在认证服务器上新建脚本 test.php:

当用户名为 abc 且密码为 123 的时候就算账号密码正确,否则认证失败。

注意 $url 那里已经写死了 node1,对于实际使用时,应该在用户请求中将对应的 api 当做参数与用户名和密码同时传进来。

测试请求

最终会从 msg 中得到:

现在向需要 OAuth2 认证的接口写一个新的 php 文件:

发起 OAuth2 请求:

响应:

可以在 Header 中看到,scope 和 userid 已经传过来了。

整个基于 Kong 的 OAuth2 认证就完成了。

总结

目前 Kong 的版本已经升级到了 0.9.6,但仍有很多功能缺失。Kong 是基于 NGINX 开发实现的,然而很多 NGINX 已经实现的功能,比如 Websocket 转发,仍没有完全支持。下一个版本或许会开始支持 Websocket。OAuth2 这里也有很多问题,在这里就不一一描述了。坑都不是很大,多数情况下都可以满足需求。

14 comments

Skip to comment form

  1. 慕若曦

    圣诞节快乐

    1. 石樱灯笼

      域名留错了,pw 怎么变 com 了

      1. 慕若曦

        被浏览器的填充给坑了……

        1. 石樱灯笼

          都被坑过

  2. 小彦

    开懂一点点,大概是一个 Auth 服务相关的?
    还有子域名路由和 alias 功能?

  3. 夏天烤洋芋

    不知道这是干嘛的!

    1. 石樱灯笼

      微服务架构的网站会用的东西

  4. xiaopanp

    有用过 Kong+consul?

    1. 石樱灯笼

      没有

  5. viable

    您好
    请问 我 ip:8080 填写 IP:8001 就出现 错误 什么什么原因? 楼主能帮忙解答嘛?

  6. SparkleBO

    Scope 是什么意思啊

    1. 石樱灯笼

      token 的权限范围

  7. xiaoxiao

    后端有多个实例提供服务应该如何配置

    1. 石樱灯笼

      当时研究 Kong 的时候,公司买的负载均衡,且实现非常不优雅,没有一丝参考价值,所以最终并没有写进文内。
      至于 Kong 自己的负载均衡以及 后端 API 的负载均衡,当时没有时间去学习。而由于公司项目组也没有活到项目开发完成,所以也没有再深入研究。
      Kong v0.10 之后的版本与 v0.9 版本差异也很大,现在版本已经到 0.11.2 了,而且还分了社区版和企业版。想要使用的话建议重新学习新版本。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据