记录下 kong api 网关的搭建过程 (02)-- 管理篇 - (sunznx) 振翅飞翔
05 September 2019

转发

先简单说说 kong 是什么?我用大白话来讲:
有一些接口,我们不能直接给暴露给外部,需要有特定的 key, secret 等认证信息,才能访问。
kong 如何实现这个功能?首先 kong 有个 key, secret 的认证,然后将你的 url 和 kong 的一个 url 进行绑定,例如:

curl -i -X POST http://localhost:8001/apis/ \
     --data "name=baidu" \
     --data "upstream_url=http://www.baidu.com/index" \
     --data "uris=/baidu" \
     --data "methods=GET"

这样,当我们访问 http://localhost:8000/baidu 的时候,kong 就会帮我们把 http://www.baidu.com/index 的响应请求给我们

$ curl http://localhost:8000/baidu
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://www.baidu.com/search/error.html">here</a>.</p>

注意到,我们向 kong 加 url 绑定的时候使用的是 localhost:8001 ,访问的时候用的是 localhost:8000
8001 是 kong 的管理端口,8000 是请求 url 的端口。在正式使用的时候,8001 比较危险,我们并不能直接访问 localhost:8001

jwt 认证

我们访问 http://localhost:8000/baidu 的时候并没有提供认证,也能正常转发。

下面我们来试试 jwt 认证。相关的文档在这里:https://docs.konghq.com/hub/kong-inc/jwt/0.1-x.html

# 开启 jwt 认证
$ curl -X POST http://localhost:8001/apis/baidu/plugins --data "name=jwt"

# 再次访问
$ curl -i http://localhost:8000/baidu
HTTP/1.1 401 Unauthorized
...

{"message":"Unauthorized"}

jwt 认证的 key 和 secret 是和 consumers 关联的

# username 和 custom_id 是和你的业务关联的
$ curl -X POST http://localhost:8001/consumers \
  --data "username=baidu_api" \
  --data "custom_id=514cdadb"
{
    "custom_id": "514cdadb",
    "created_at": 1567677207000,
    "username": "baidu_api",
    "id": "6a242c40-78f8-4cd3-8c05-1fb897efde28"
}

$ curl -X POST http://localhost:8001/consumers/6a242c40-78f8-4cd3-8c05-1fb897efde28/jwt
{
    "created_at": 1567677291000,
    "id": "166421cc-d078-4bd2-a52c-c43decc731a1",
    "algorithm": "HS256",
    "key": "ekVUUCupZ1Z9peG0lLRLbncWw2AhfYAE",
    "secret": "343aXgxn4lzaPKDfXlAoLIxFbtZa4ucV",
    "consumer_id": "6a242c40-78f8-4cd3-8c05-1fb897efde28"
}

拿到 key 和 secret 之后,我们使用 php 生成一个 jwt token

// composer require firebase/php-jwt

require __DIR__ . '/vendor/autoload.php';
$key = 'ekVUUCupZ1Z9peG0lLRLbncWw2AhfYAE';
$secret = '343aXgxn4lzaPKDfXlAoLIxFbtZa4ucV';
$algorithm = 'HS256';

echo Firebase\JWT\JWT::encode([
    'iss' => $key,
    'alg' => $algorithm
], $secret) . PHP_EOL;

// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJla1ZVVUN1cFoxWjlwZUcwbExSTGJuY1d3MkFoZllBRSIsImFsZyI6IkhTMjU2In0.Yj0UUHJto0tVao1HuwlRHzLKUsUMk25Ed0IUWrK1Q4w
$ curl http://localhost:8000/baidu \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJla1ZVVUN1cFoxWjlwZUcwbExSTGJuY1d3MkFoZllBRSIsImFsZyI6IkhTMjU2In0.Yj0UUHJto0tVao1HuwlRHzLKUsUMk25Ed0IUWrK1Q4w'
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://www.baidu.com/search/error.html">here</a>.</p>
</body></html>

安全问题

上面我们提到,kong 的 8001 端口比较危险,那么我们可以屏蔽掉 8001 端口。我所在的项目是这么做的

先让 kong 开启一个 :8000/kong/admin 的转发,之前用 :8001 的 api,都改成通过 :8000/kong/admin 来访问

$ curl -i -X POST http://localhost:8001/apis/ \
     --data "name=kong.admin" \
     --data "upstream_url=http://localhost:8001" \
     --data "uris=/kong/admin" \
     --data "methods=POST,GET,DELETE,UPDATE,PATCH"

我们还需要对 :8000/kong/admin 加认证

在进行认证之前,我们还是需要借助 :8001 帮我们生成一个 admin consumer 用来管理 admin 账号的认证。 kong.admin 这个接口认证简单用 key-auths 来认证,文档在 https://docs.konghq.com/hub/kong-inc/key-auth/0.2-x.html

$ curl -i -X POST http://localhost:8001/consumers \
     --data "username=admin"
{
    "created_at": 1567679146000,
    "username": "admin",
    "id": "928a729b-57d7-49a1-bc13-9ddd553d737a"
}

# admin 使用 key-auths 来认证
$ curl -X POST http://localhost:8001/consumers/admin/key-auth
{
    "id": "a9d43b51-a1e5-485a-8f71-f2b75905fb08",
    "created_at": 1567679202000,
    "key": "1YL5Ongte6F3XLt1K7w2jn5xBpxZEzYg",
    "consumer_id": "928a729b-57d7-49a1-bc13-9ddd553d737a"
}

:8000/kong/admin 开启认证

curl -X POST http://localhost:8001/apis/kong.admin/plugins \
     --data "name=key-auth"

至此,我们可以抛弃 :8001 端口,直接通过 :8000/kong/admin

# 测试不加 key 的情况
$ curl -i -X POST http://localhost:8000/kong/admin/apis/ \
  --data "name=baidu2" \
  --data "upstream_url=http://www.baidu.com/index" \
  --data "uris=/baidu2" \
  --data "methods=GET"

HTTP/1.1 401 Unauthorized
{"message":"No API key found in request"}

上面的请求会出错,原因是我们没有加上 apikey

$ curl -i -X POST http://localhost:8000/kong/admin/apis/ \
  -H 'apikey: 1YL5Ongte6F3XLt1K7w2jn5xBpxZEzYg' \
  --data "name=baidu2" \
  --data "upstream_url=http://www.baidu.com/index" \
  --data "uris=/baidu2" \
  --data "methods=GET"

$ curl http://localhost:8000/baidu2