Skip to content

Commit

Permalink
更新 readme (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-smile authored Dec 18, 2023
1 parent 4c73fe6 commit c5355c5
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 131 deletions.
107 changes: 55 additions & 52 deletions sdks/apigw-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ grant_permissions:

# 应用申请指定网关所有资源的权限,待网关管理员审批后,应用才可访问网关资源;
# 用于命令 `apply_apigw_permissions`
apply_permissions:
- gateway_name: "{{ settings.BK_APIGW_NAME }}"
# 权限维度,可选值:gateway,按网关授权,包括网关下所有资源,以及未来新创建的资源
grant_dimension: "gateway"
# apply_permissions:
# - gateway_name: "{{ settings.BK_APIGW_NAME }}"
# # 权限维度,可选值:gateway,按网关授权,包括网关下所有资源,以及未来新创建的资源
# grant_dimension: "gateway"

# 为网关添加关联应用,关联应用可以通过网关 bk-apigateway 的接口操作网关数据;每个网关最多可有 10 个关联应用;
# 用于命令 `add_related_apps`
Expand Down Expand Up @@ -174,6 +174,8 @@ resource_docs:

用于定义资源配置,建议通过网关管理端导出。为了方便用户直接使用网关导出的资源文件,资源定义默认没有命名空间。

样例可参考:[resources.yaml](examples/django/use-custom-script/support-files/resources.yaml)

#### 3. apidocs(可选)

资源文档,资源文档为 markdown 格式。资源文档的文件名,应为 `资源名称` + `.md` 格式,假如资源名称为 get_user,则文档文件名应为 get_user.md。
Expand Down Expand Up @@ -209,6 +211,54 @@ resource_docs:

此方案适用于非 Django 项目,具体请参考 [sync-apigateway-with-docker.md](docs/sync-apigateway-with-docker.md)


## 如何获取网关公钥

后端服务如需解析 API 网关发送的请求头 X-Bkapi-JWT,需要提前获取该网关的公钥。获取网关公钥,有以下方案。

### 1. 根据 SDK 提供的 Django Command 拉取

在同步网关数据时,直接添加以下 Command 拉取网关公钥。网关公钥将保存在 model Context 对应的库表 apigw_manager_context 中,SDK 提供的 Django 中间件将从表中读取网关公钥。

```bash
# 默认拉取 settings.BK_APIGW_NAME 对应网关的公钥
python manage.py fetch_apigw_public_key

# 拉取指定网关的公钥
python manage.py fetch_apigw_public_key --gateway-name my-gateway
```

### 2. 直接获取网关公钥,配置到项目配置文件

服务仅需接入一些固定的网关部署环境时,可在网关管理端,网关基本信息中查询网关公钥,并配置到项目配置文件。

蓝鲸官方网关,需要自动注册并获取网关公钥,可联系蓝鲸官方运营同学,在服务部署前,由官方提前创建网关,并设置网关公钥、私钥,同时将网关公钥同步给后端服务。
具体可参考 helm-charts 仓库的 README。

### 3. 通过网关公开接口,拉取网关公钥

API 网关提供了公钥查询接口,后端服务可按需根据接口拉取网关公钥,接口信息如下:
```bash
# 将 bkapi.example.com 替换为网关 API 地址,
# 将 gateway_name 替换为待查询公钥的网关名,
# 提供正确的蓝鲸应用账号
curl -X GET 'https://bkapi.example.com/api/bk-apigateway/prod/api/v1/apis/{gateway_name}/public_key/' \
-H 'X-Bkapi-Authorization: {"bk_app_code": "my-app", "bk_app_secret": "secret"}'
```

响应样例:

```json
{
"data": {
"public_key": "your public key"
}
}
```

注意事项:
- 拉取公钥时,不能实时拉取,需要添加缓存(实时拉取会导致整体接口性能下降)

## 校验请求来自 API 网关

### 场景一:Django 项目
Expand Down Expand Up @@ -296,7 +346,7 @@ APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username

### 场景二:非 Django 项目

非 Django 项目,需要项目获取网关公钥,并解析请求头中的 X-Bkapi-JWT;获取网关公钥的方案请参考下文
非 Django 项目,需要项目获取网关公钥,并解析请求头中的 X-Bkapi-JWT;获取网关公钥的方案请参考上文

解析 X-Bkapi-JWT 时,可根据 jwt header 中的 kid 获取当前网关名,例如:
```
Expand Down Expand Up @@ -324,50 +374,3 @@ APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username
"iss": "APIGW" # 签发者
}
```

### 如何获取网关公钥

后端服务如需解析 API 网关发送的请求头 X-Bkapi-JWT,需要提前获取该网关的公钥。获取网关公钥,有以下方案。

#### 1. 根据 SDK 提供的 Django Command 拉取

在同步网关数据时,直接添加以下 Command 拉取网关公钥。网关公钥将保存在 model Context 对应的库表 apigw_manager_context 中,SDK 提供的 Django 中间件将从表中读取网关公钥。

```bash
# 默认拉取 settings.BK_APIGW_NAME 对应网关的公钥
python manage.py fetch_apigw_public_key

# 拉取指定网关的公钥
python manage.py fetch_apigw_public_key --gateway-name my-gateway
```

#### 2. 直接获取网关公钥,配置到项目配置文件

服务仅需接入一些固定的网关部署环境时,可在网关管理端,网关基本信息中查询网关公钥,并配置到项目配置文件。

蓝鲸官方网关,需要自动注册并获取网关公钥,可联系蓝鲸官方运营同学,在服务部署前,由官方提前创建网关,并设置网关公钥、私钥,同时将网关公钥同步给后端服务。
具体可参考 helm-charts 仓库的 README。

#### 3. 通过网关公开接口,拉取网关公钥

API 网关提供了公钥查询接口,后端服务可按需根据接口拉取网关公钥,接口信息如下:
```bash
# 将 bkapi.example.com 替换为网关 API 地址,
# 将 gateway_name 替换为待查询公钥的网关名,
# 提供正确的蓝鲸应用账号
curl -X GET 'https://bkapi.example.com/api/bk-apigateway/prod/api/v1/apis/{gateway_name}/public_key/' \
-H 'X-Bkapi-Authorization: {"bk_app_code": "my-app", "bk_app_secret": "secret"}'
```

响应样例:

```json
{
"data": {
"public_key": "your public key"
}
}
```

注意事项:
- 拉取公钥时,不能实时拉取,需要添加缓存(实时拉取会导致整体接口性能下降)
162 changes: 88 additions & 74 deletions sdks/apigw-manager/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ definition.yaml 中可以使用 Django 模版语法引用和渲染变量,内
# 应用申请指定网关所有资源的权限,待网关管理员审批后,应用才可访问网关资源;
# 用于命令 `apply_apigw_permissions`
apply_permissions:
- gateway_name: "{{ settings.BK_APIGW_NAME }}"
# 权限维度,可选值:gateway,按网关授权,包括网关下所有资源,以及未来新创建的资源
grant_dimension: "gateway"
# apply_permissions:
# - gateway_name: "{{ settings.BK_APIGW_NAME }}"
# # 权限维度,可选值:gateway,按网关授权,包括网关下所有资源,以及未来新创建的资源
# grant_dimension: "gateway"
# 为网关添加关联应用,关联应用可以通过网关 bk-apigateway 的接口操作网关数据;每个网关最多可有 10 个关联应用;
# 用于命令 `add_related_apps`
Expand Down Expand Up @@ -195,6 +195,8 @@ definition.yaml 中可以使用 Django 模版语法引用和渲染变量,内

用于定义资源配置,建议通过网关管理端导出。为了方便用户直接使用网关导出的资源文件,资源定义默认没有命名空间。

样例可参考:\ `resources.yaml <examples/django/use-custom-script/support-files/resources.yaml>`_

3. apidocs(可选)
~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -235,31 +237,92 @@ definition.yaml 中可以使用 Django 模版语法引用和渲染变量,内

此方案适用于非 Django 项目,具体请参考 `sync-apigateway-with-docker.md <docs/sync-apigateway-with-docker.md>`_

如何获取网关公钥
----------------

后端服务如需解析 API 网关发送的请求头 X-Bkapi-JWT,需要提前获取该网关的公钥。获取网关公钥,有以下方案。

1. 根据 SDK 提供的 Django Command 拉取
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

在同步网关数据时,直接添加以下 Command 拉取网关公钥。网关公钥将保存在 model Context 对应的库表 apigw_manager_context 中,SDK 提供的 Django 中间件将从表中读取网关公钥。

.. code-block:: bash
# 默认拉取 settings.BK_APIGW_NAME 对应网关的公钥
python manage.py fetch_apigw_public_key
# 拉取指定网关的公钥
python manage.py fetch_apigw_public_key --gateway-name my-gateway
2. 直接获取网关公钥,配置到项目配置文件
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

服务仅需接入一些固定的网关部署环境时,可在网关管理端,网关基本信息中查询网关公钥,并配置到项目配置文件。

蓝鲸官方网关,需要自动注册并获取网关公钥,可联系蓝鲸官方运营同学,在服务部署前,由官方提前创建网关,并设置网关公钥、私钥,同时将网关公钥同步给后端服务。
具体可参考 helm-charts 仓库的 README。

3. 通过网关公开接口,拉取网关公钥
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

API 网关提供了公钥查询接口,后端服务可按需根据接口拉取网关公钥,接口信息如下:

.. code-block:: bash
# 将 bkapi.example.com 替换为网关 API 地址,
# 将 gateway_name 替换为待查询公钥的网关名,
# 提供正确的蓝鲸应用账号
curl -X GET 'https://bkapi.example.com/api/bk-apigateway/prod/api/v1/apis/{gateway_name}/public_key/' \
-H 'X-Bkapi-Authorization: {"bk_app_code": "my-app", "bk_app_secret": "secret"}'
响应样例:

.. code-block:: json
{
"data": {
"public_key": "your public key"
}
}
注意事项:


* 拉取公钥时,不能实时拉取,需要添加缓存(实时拉取会导致整体接口性能下降)

校验请求来自 API 网关
---------------------

场景一:Django 项目
^^^^^^^^^^^^^^^^^^^

如果后端服务需要认证 API 网关传递过来的请求头 X-Bkapi-JWT,可在 settings MIDDLEWARE 中加入以下 Django 中间件
要在后端服务中认证 API 网关传递过来的请求头 ``X-Bkapi-JWT``\ ,可以通过在 settings 中的 MIDDLEWARE 中添加以下 Django 中间件。这样,在请求处理过程中,会自动解析请求头中的 X-Bkapi-JWT,并将相关信息添加到 request 对象中。

.. code-block:: python
MIDDLEWARE += [
"apigw_manager.apigw.authentication.ApiGatewayJWTGenericMiddleware", # JWT 认证,解析请求头中的 X-Bkapi-JWT,获取 request.jwt 对象
"apigw_manager.apigw.authentication.ApiGatewayJWTAppMiddleware", # 根据 request.jwt,获取 request.app 对象
"apigw_manager.apigw.authentication.ApiGatewayJWTUserMiddleware", # 根据 request.jwt,获取 request.user 对象
]
如需获取 request.user 对象,还需要在 settings AUTHENTICATION_BACKENDS 中加入:
添加以上两个中间件后,request 对象中将会添加 ``request.jwt`` 和 ``request.app`` 两个对象。这些对象包含了网关名、当前请求的蓝鲸应用 ID 等信息。具体内容可参考下文。

如果需要在 request 对象中获取当前请求用户 ``request.user`` 对象,除了上面的中间件外,还需要添加一个中间件以及 AUTHENTICATION_BACKENDS:

.. code-block:: python
# 添加中间件
MIDDLEWARE += [
"apigw_manager.apigw.authentication.ApiGatewayJWTUserMiddleware", # 根据 request.jwt,获取 request.user 对象
]
# 添加 AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDS += [
"apigw_manager.apigw.authentication.UserModelBackend",
]
注意,Django 中间件 ApiGatewayJWTGenericMiddleware 解析 X-Bkapi-JWT 时,需要获取网关公钥,SDK 默认从以下两个位置获取网关公钥:
注意,Django 中间件 ApiGatewayJWTGenericMiddleware 解析 ``X-Bkapi-JWT`` 时,需要获取网关公钥,SDK 默认从以下两个位置获取网关公钥:


* SDK model Context (库表 apigw_manager_context),需提前执行 ``python manage.py fetch_apigw_public_key`` 拉取并保存网关公钥
Expand All @@ -279,7 +342,7 @@ ApiGatewayJWTGenericMiddleware
ApiGatewayJWTAppMiddleware
""""""""""""""""""""""""""

根据 request.jwt,在 ``request`` 中注入 ``app`` 对象,有以下属性:
根据 ``request.jwt``\ ,在 ``request`` 中注入 ``app`` 对象,有以下属性:


* ``bk_app_code``\ :调用接口的应用;
Expand All @@ -288,17 +351,24 @@ ApiGatewayJWTAppMiddleware
ApiGatewayJWTUserMiddleware
"""""""""""""""""""""""""""

根据 request.jwt,在 ``request`` 中注入 ``user`` 对象,该对象通过以下调用获取:
根据 ``request.jwt``\ ,在 ``request`` 中注入 ``user`` 对象:


* 如果用户通过认证:其为一个 Django User Model 对象,用户名为当前请求用户的用户名
* 如果用户未通过认证,其为一个 Django AnonymousUser 对象,用户名为当前请求用户的用户名

如果中间件 ``ApiGatewayJWTUserMiddleware`` 中获取用户的逻辑不满足需求,可以继承此中间件并自定义用户获取方法 ``get_user``\ ,例如::

.. code-block:: python
def get_user(self, request, gateway_name=None, bk_username=None, verified=False, **credentials):
return auth.authenticate(
request, gateway_name=gateway_name, bk_username=bk_username, verified=verified, **credentials
)
class MyJWTUserMiddleware(ApiGatewayJWTUserMiddleware):
def get_user(self, request, gateway_name=None, bk_username=None, verified=False, **credentials):
...
return auth.authenticate(
request, gateway_name=gateway_name, bk_username=bk_username, verified=verified, **credentials
)
因使用 auth.authenticate 获取用户,所以需要在 settings AUTHENTICATION_BACKENDS 中添加对应的用户认证后端,才能获取到 request.user 对象。
如果该中间件认证逻辑不满足需求,可继承此中间件,重载 ``get_user`` 方法。
注意:在自定义中间件 ``ApiGatewayJWTUserMiddleware`` 时,如果继续使用 ``auth.authenticate`` 获取用户,请确保正确设置用户认证后端,以遵循 Django ``AUTHENTICATION_BACKENDS`` 相关规则。

用户认证后端
~~~~~~~~~~~~
Expand All @@ -322,7 +392,7 @@ UserModelBackend
BK_APIGW_JWT_PROVIDER_CLS = "apigw_manager.apigw.providers.DummyEnvPayloadJWTProvider"
同时提供以下环境变量(非 settings)
同时提供以下环境变量(非 Django settings)

.. code-block::
Expand All @@ -333,7 +403,7 @@ UserModelBackend
场景二:非 Django 项目
^^^^^^^^^^^^^^^^^^^^^^

非 Django 项目,需要项目获取网关公钥,并解析请求头中的 X-Bkapi-JWT;获取网关公钥的方案请参考下文
非 Django 项目,需要项目获取网关公钥,并解析请求头中的 X-Bkapi-JWT;获取网关公钥的方案请参考上文

解析 X-Bkapi-JWT 时,可根据 jwt header 中的 kid 获取当前网关名,例如:

Expand Down Expand Up @@ -363,59 +433,3 @@ UserModelBackend
"nbf": 1701399303, # Not Before 时间
"iss": "APIGW" # 签发者
}
如何获取网关公钥
^^^^^^^^^^^^^^^^

后端服务如需解析 API 网关发送的请求头 X-Bkapi-JWT,需要提前获取该网关的公钥。获取网关公钥,有以下方案。

1. 根据 SDK 提供的 Django Command 拉取
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在同步网关数据时,直接添加以下 Command 拉取网关公钥。网关公钥将保存在 model Context 对应的库表 apigw_manager_context 中,SDK 提供的 Django 中间件将从表中读取网关公钥。

.. code-block:: bash
# 默认拉取 settings.BK_APIGW_NAME 对应网关的公钥
python manage.py fetch_apigw_public_key
# 拉取指定网关的公钥
python manage.py fetch_apigw_public_key --gateway-name my-gateway
2. 直接获取网关公钥,配置到项目配置文件
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

服务仅需接入一些固定的网关部署环境时,可在网关管理端,网关基本信息中查询网关公钥,并配置到项目配置文件。

蓝鲸官方网关,需要自动注册并获取网关公钥,可联系蓝鲸官方运营同学,在服务部署前,由官方提前创建网关,并设置网关公钥、私钥,同时将网关公钥同步给后端服务。
具体可参考 helm-charts 仓库的 README。

3. 通过网关公开接口,拉取网关公钥
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

API 网关提供了公钥查询接口,后端服务可按需根据接口拉取网关公钥,接口信息如下:

.. code-block:: bash
# 将 bkapi.example.com 替换为网关 API 地址,
# 将 gateway_name 替换为待查询公钥的网关名,
# 提供正确的蓝鲸应用账号
curl -X GET 'https://bkapi.example.com/api/bk-apigateway/prod/api/v1/apis/{gateway_name}/public_key/' \
-H 'X-Bkapi-Authorization: {"bk_app_code": "my-app", "bk_app_secret": "secret"}'
响应样例:

.. code-block:: json
{
"data": {
"issuer": "",
"public_key": "your public key"
}
}
注意事项:


* **issuer**\ :一般情况下,签发者为空字符串;如果一个后端需要对接到多个 API 网关部署环境,可让不同部署环境的网关配置不同的 issuer(在部署网关时,可通过环境变量 BK_APIGW_JWT_ISSUER 指定),后端服务可根据 ``网关名`` + ``issuer`` 区分不同部署环境的网关公钥。
* 拉取公钥时,需要对数据进行缓存,不能高并发拉取
4 changes: 3 additions & 1 deletion sdks/apigw-manager/docs/sync-apigateway-with-django.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
# 如果任何命令返回一个非零退出状态(错误),脚本将会立即终止执行
set -e

# 待同步网关名,需修改为实际网关名;直接指定网关名,则不需要配置 Django settings BK_APIGW_NAME
# 待同步网关名,需修改为实际网关名;
# - 如在下面指令的参数中,指定了参数 --gateway-name=${gateway_name},则使用该参数指定的网关名
# - 如在下面指令的参数中,未指定参数 --gateway-name,则使用 Django settings BK_APIGW_NAME
gateway_name="bk-demo"

# 待同步网关、资源定义文件,需调整为实际的配置文件地址
Expand Down
Loading

0 comments on commit c5355c5

Please sign in to comment.