Kong介绍
Kong是一款基于Nginx_Lua模块写的高可用网关API,通过前置的负载均衡配置把请求均匀地分发到各个Server,来应对大批量的网络请求。基于Nginx 特性,Kong本身也非常容易地扩展到多个服务器上。
Kong主要有三个组件:
Kong Server :基于nginx的服务器,用来接收API请求。
Apache Cassandra/PostgreSQL :用来存储操作数据。
Kong dashboard:官方推荐UI管理工具,也可以使用 restfull 方式 管理admin api。
Kong 工作方式

Kong基本概念:
客户端:指下游客户向Kong的代理端口发出请求。
服务:服务实体,是对自己的每个上游服务的抽象。客户请求被转发到该服务。
路由:路由是进入Kong的入口点,并为要匹配的请求定义规则,并路由到给定的Service。服务和路由之间的关系是一对多的关系。
插件:它是在代理生命周期中运行的业务逻辑。可以通过ADMIN API配置插件 - 全局(所有传入流量)或特定的路由和服务。
用户:是调用API 服务时身份认证的凭据
更多内容参考官网 https://konghq.com/kong/
安装配置
本次基于docker 的方式进行安装。Docker的安装请参照 Docker实践一 Docker安装
1、在Docker中建立虚拟的网络:
1
| docker network create kong-net
|
后续的应用及数据库都使用这个虚拟网络。
2、编写docker-compose.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| version: "3.7" services: kong: # 镜像版本,目前最新 image: kong:1.1.2 environment: # 数据持久化方式,使用postgres数据库 - "KONG_DATABASE=postgres" # 数据库容器名称,Kong连接数据时使用些名称 - "KONG_PG_HOST=kong-database" # 数据库名称 - "KONG_CASSANDRA_CONTACT_POINTS=kong-database" # 日志记录目录 - "KONG_PROXY_ACCESS_LOG=/dev/stdout" - "KONG_ADMIN_ACCESS_LOG=/dev/stdout" - "KONG_PROXY_ERROR_LOG=/dev/stderr" - "KONG_ADMIN_ERROR_LOG=/dev/stderr" # 暴露的端口 - "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" ports: - 8000:8000 - 8443:8443 - 8001:8001 - 8444:8444 # 使用docker网络 networks: - kong-net # 依赖数据库服务 depends_on: - kong-database # kong 管理界面 konga: image: pantsel/konga environment: - "TOKEN_SECRET=51liveup.cn" - "NODE_ENV=production" ports: - 8080:1337 networks: - kong-net depends_on: - kong-database - # 数据库服务 kong-database: image: postgres:9.6 ports: - "5432:5432" environment: # 访问数据库的用户 - POSTGRES_USER=kong - POSTGRES_DB=kong networks: - kong-net volumes: # 同步时间 - /etc/localtime:/etc/localtime:ro # 数据库持久化目录 - /data/data/postgresql:/var/lib/postgresql/data networks: kong-net: external: true
|
使用docker-compose up 命令启动服务。会发现启动时报数据库错误,这是因为kong 使用的postgres 数据还需要进行初始化才能使用。
3、初始化数据库
1 2 3 4 5 6 7
| docker run --rm \ --network=kong-net \ -e "KONG_DATABASE=postgres" \ -e "KONG_PG_HOST=kong-database" \ -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \ kong:latest kong migrations bootstrap
|
一定要在创建数据库容器之后,并且保持数据库的Docker容器在运行状态,再执行初始化数据库,数据库初始化成功后,再次使用docker-compose up -d 启动服务就可以了。
验证安装
在宿主机上执行
1 2
| curl -i http://localhost:8001/
|
返回下面内容:
1 2 3 4 5 6 7 8 9 10
| HTTP/1.1 200 OK Date: Mon, 17 Jun 2019 02:43:33 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Access-Control-Allow-Origin: * Server: kong/1.1.2 Content-Length: 5860 ....
|
表示安装正确。可以正常使用Kong了。
访问 http://localhost:8080 访问Konga 的管理界面,第一次登录使用需要创建管理员帐号和密码。
更多内容参照官网的安装文档。
配置一个实例
配置一个访问 https://www.baidu.com/ 的接口API,实际使用时会对接后端的业务数据接口地址。
1、创建服务
服务是上游服务的抽象,可以是一个应用,或者具体某个接口。
命令行方式创建服务:
1 2 3 4
| curl -i -X POST \ --url http://51liveup.cn:8001/services/ \ --data 'name=baidu-service' \ --data 'url=https://www.baidu.com/'
|
Konga 的管理界面创建和查看服务如下图

2、创建路由
在刚才创建的baidu-service的服务上创建路由
1 2 3 4
| curl -i -X POST \ --url http://51liveup.cn:8001/services/baidu-service/routes \ --data 'hosts[]=baidu.com' \ --data 'paths[]=/api/baidu'
|


3、通过Postman 访问数据

注意,如果Api暴露的地址与前面Host定义的地址(baidu.com)不一致,就需要在请求的Headers里面加入参数Host=baidu.com
JWT 插件的使用
上面的配置,只要知道Router的地址,就可以访问获取数据,我们要把API加入身份认证。如果API面对不是具体用户,而是其他的系统,可以使用JWT来进行系统间身份认证,使用Kong JWT插件就可能完成这功能。JWT 插件要在对应的Router上进行启用。
1 2
| curl -X POST http://51liveup.cn:8001/routes/fee36521-e549-410f-8986-9fbba02219c1/plugins \ --data "name=jwt"
|
fee36521-e549-410f-8986-9fbba02219c1 是创建的router的ID。

这时再通过Postman 访问上面的接口就会提示:
1 2 3
| { "message": "Unauthorized" }
|
客户端要访问需要提供JWT的认证信息才可以。
创建用户
1 2 3
| curl -i -X POST \ --url http://51liveup.cn:8001/consumers/ \ --data "username=baiduuser"
|
用户生成JWT凭证
1 2 3
| curl -i -X POST \ --url http://51liveup.cn:8001/consumers/baiduuser/jwt \ --header "Content-Type: application/x-www-form-urlencoded"
|
返回凭证信息,也可以通过 get 方法查询凭证信息
1 2 3 4 5 6 7 8 9 10 11
| { "rsa_public_key": null, "created_at": 1560723665, "consumer": { "id": "8bb94f49-22a6-4d77-9a64-21f13adc0342" }, "id": "a110d234-6dc1-4443-9da2-21acddc66e09", "algorithm": "HS256", "secret": "lCe8Lbb7F0KtLccaBcBnOvYg76V7wmQx", "key": "7yQoUdF0aFUC9N593uLQLbqL7RSPj2qM" }
|
使用key和secret在 https://jwt.io/ 可以生成jwt 凭证信息.

再通过postman 访问,就可以看到数据了。
下面是通过Java代码访问数据的例子,不适用上面配置的baidu的接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import okhttp3.*; import java.io.IOException; import java.nio.charset.Charset; import java.util.Base64; public class Example { public static void main(String[] args) { Example example = new Example(); example.get("http://51liveup.cn:8000/api/xxxx/1"); } public void get(String url) { OkHttpClient mOkHttpClient = new OkHttpClient(); Request request = createBuilder().url(url).post(FormBody.create(MediaType.parse("application/json; charset=utf-8"), "{}")).build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { System.out.println("failure : \r\n" + e); } @Override public void onResponse(Call call, Response response) throws IOException { System.out.println("response:"); System.out.println(response.body().string()); } }); } private Request.Builder createBuilder() { Request.Builder builder = new Request.Builder(); builder.addHeader("host", "51liveup.cn"); builder.addHeader("Authorization", "Bearer " + generateJwt()); return builder; } private String generateJwt() { String jwt = Jwts.builder() .setHeaderParam("typ", "JWT") .setHeaderParam("alg", "HS256") .setIssuer("7yQoUdF0aFUC9N593uLQLbqL7RSPj2qM") // key .signWith(SignatureAlgorithm.HS256, Base64.getEncoder().encodeToString("lCe8Lbb7F0KtLccaBcBnOvYg76V7wmQx".getBytes(Charset.forName("utf-8")))) .compact(); System.out.println("jwt:" + jwt); return jwt; } }
|
ACL 插件的使用
JWT插件可以保护API能够被受信用户访问,但不能区别哪个用户能够访问哪个API,即接口权限问题,我们使用ACL 插件解决这个问题.
在上面定义好的路由上启用acl 插件,指定白名单,
1 2 3 4
| curl -i -X POST \ --url http://51liveup.cn:8001/routes/afb8bfbd-977e-464f-8c94-05d6c5c98429/plugins \ --data "name=acl" \ --data "config.whitelist=baiduGroup"
|
此时再访问api,会提示不能访问这个服务。
1 2 3
| { "message": "You cannot consume this service" }
|
只需将baiduuser这个用户关联到白名单内的baiduGroup组里即可。
1 2 3
| curl -i -X POST \ --url http://localhost:8001/consumers/tianqiuser/acls \ --data "group=tianqi"
|
再次访问接口,能正常返回数据。
现在就可以对网关暴露的接口进行身份认证和权限控制了。