蓝鲸 API 网关管理 SDK 是一个用于管理 API 网关的工具,它提供了一套完整的工具和功能,可以帮助您更轻松地管理 API 网关,提高系统的安全性和可靠性。
-
Django Command:SDK 提供了 Django Command,支持网关注册、同步、发布等功能。您可以根据需要编排指令,以满足您的特定需求,并集成到您的项目自动执行 API 网关同步过程,以便更轻松地管理 API 网关。
-
Docker 镜像:对于非 Django 项目,提供了 Docker 基础镜像,封装了 SDK 同步网关的相关功能,以便非 Django 项目轻松管理 API 网关。
-
Django 中间件:SDK 还提供了 Django 中间件,用于解析 API 网关请求后端接口时添加的请求头 X-Bkapi-JWT,以方便后端服务校验请求是否来自 API 网关。这个中间件可以确保只有来自蓝鲸 API 网关的请求才能访问您的后端服务,从而提高系统的安全性。
基础安装:
pip install apigw-manager
如果需要使用 apigw-manager 提供的 Django 中间件解析来自 API 网关的 X-Bkapi-JWT,可以安装:
pip install "apigw-manager[cryptography]"
- 通过预定义的 YAML 文件,您可以轻松地执行网关创建、更新、发布和资源同步操作,从而简化 API 网关管理过程。
- 使用 Django 中间件,您可以解析蓝鲸 API 网关的 X-Bkapi-JWT 请求头,确保只有来自 API 网关的请求才能访问您的后端服务,提升系统安全性。
SDK 同步网关配置到 API 网关,支持多种方案:
- 直接使用 Django Command 同步:此方案适用于 Django 项目;Django 项目,可直接执行 SDK 提供的 Django Command 指令
- 通过镜像方式同步:此方案适用于非 Django 项目;非 Django 项目,无法直接执行 SDK 提供的 Django Command 指令
同步网关配置到 API 网关,需要准备网关配置、资源配置、资源文档、自定义同步脚本等数据,可参考目录:
support-files
├── definition.yaml # 维护网关、环境、资源文档路径、主动授权、发布等配置,但不包含资源配置
├── resources.yaml # 维护资源配置;资源配置可通过 API 网关管理端直接导出,数据量较大,因此单独管理
├── bin
│ └── sync-apigateway.sh # 自定义同步脚本,Django 项目也可以自定义 Django Command
├── bk_apigw_docs_demo.tgz # 资源文档归档文件,可选;可通过 API 网关管理端导出;与资源文档目录 apidocs 二选一
└── apidocs # 资源文档目录,可选;可通过 API 网关管理端导出并解压,或者直接维护 markdown 格式文档文件
├── zh # 中文文档目录
│ └── anything.md
└── en # 英文文档目录
└── anything.md
用于定义网关、环境等配置,为了简化使用,使用以下模型进行处理:
Template(definition.yaml) YAML
+--------------------------+ +----------------------------+
| | | | +--------------------------------------+
| ns1: | | ns1: | | |
| key: {{environ.KEY1}} | | key: value_from_environ |------>| api1({"key": "value_from_environ"}) |
| | Render | | | |
| +------->+ | Load | |
| ns2: | | ns2: | | |
| key: {{settings.KEY2}} | | key: value_from_settings |------>| api2({"key": "value_from_settings"}) |
| | | | | |
| | | | +--------------------------------------+
+--------------------------+ +----------------------------+
definition.yaml 中可以使用 Django 模版语法引用和渲染变量,内置以下变量:
settings
:Django 提供的配置对象,优先适合用于使用 Django Command 同步environ
:环境变量,推荐镜像同步方式使用
推荐在一个文件中统一进行定义,用命名空间区分不同配置间的定义,definition.yaml 样例:
# definition.yaml 配置文件版本号,必填,固定值 1
spec_version: 1
# 定义发布内容,用于命令 `create_version_and_release_apigw`
release:
# 发布版本号;
# 资源配置更新,需更新此版本号才会发布资源版本,此版本号和 sdk 版本号一致,错误设置会影响调用方使用
version: 1.0.0
# 版本标题
title: ""
# 版本描述
comment: ""
# 定义网关基本信息,用于命令 `sync_apigw_config`
apigateway:
description: "描述"
# 网关的英文描述,蓝鲸官方网关需提供英文描述,以支持国际化
description_en: "English description"
# 是否公开;公开,则用户可查看资源文档、申请资源权限;不公开,则网关对用户隐藏
is_public: true
# 标记网关为官方网关,网关名需以 `bk-` 开头,可选;非官方网关,可去除此配置
api_type: 1
# 应用请求网关时,是否允许从请求参数 (querystring, body) 中获取蓝鲸认证信息,默认值为 true;
# 如果为 false,则只能从请求头 X-Bkapi-Authorization 获取蓝鲸认证信息;
# 新接入的网关,可以设置为 false,已接入的网关,待推动所有调用者将认证信息放到请求头后,可设置为 false
allow_auth_from_params: false
# 网关请求后端时,是否删除请求参数 (querystring, body) 中的蓝鲸认证敏感信息,比如 bk_token,为 true 表示允许删除;
# 待请求网关的所有调用者,将认证参数放到请求头 X-Bkapi-Authorization 时,可将此值设置为 false
allow_delete_sensitive_params: false
# 网关维护人员,仅维护人员有管理网关的权限
maintainers:
- "admin"
# 定义环境信息,用于命令 `sync_apigw_stage`
stage:
name: "prod"
description: "描述"
# 环境的英文名,蓝鲸官方网关需提供,以支持国际化
description_en: "English description"
# 环境变量;如未使用,可去除此配置
# vars:
# key: "value"
# 代理配置
proxy_http:
timeout: 60
# 负载均衡类型 + Hosts
upstreams:
loadbalance: "roundrobin"
hosts:
# 网关调用后端服务的默认域名或IP,不包含Path,比如:http://api.example.com
- host: ""
weight: 100
# Header转换;如未使用,可去除此配置
# transform_headers:
# # 设置Headers
# set:
# X-Token: "token"
# 主动授权,网关主动给应用,添加访问网关所有资源或者具体某个资源的权限;
# 用于命令 `grant_apigw_permissions`
grant_permissions:
- bk_app_code: "{{ settings.BK_APP_CODE }}" ## 环境变量方式:"{{ environ.BK_APP_CODE }}"
# 授权维度,可选值:
# gateway: 按网关授权,包括网关下所有资源,以及未来新创建的资源
# resource: 按资源维度授权
grant_dimension: "gateway"
# 如果是按照 resource 维度授权,需要提供如下的具体resource_name
# resource_names:
# - resource_name_1
# - resource_name_2
# 应用申请指定网关所有资源的权限,待网关管理员审批后,应用才可访问网关资源;
# 用于命令 `apply_apigw_permissions`
# apply_permissions:
# - gateway_name: "{{ settings.BK_APIGW_NAME }}" ## 环境变量方式:"{{ environ.BK_APIGW_NAME }}"
# # 权限维度,可选值:gateway,按网关授权,包括网关下所有资源,以及未来新创建的资源
# grant_dimension: "gateway"
# 为网关添加关联应用,关联应用可以通过网关 bk-apigateway 的接口操作网关数据;每个网关最多可有 10 个关联应用;
# 用于命令 `add_related_apps`
related_apps:
- "{{ settings.BK_APP_CODE }}" ## 环境变量方式:"{{ environ.BK_APP_CODE }}"
# 定义资源文档路径,用于命令 `sync_resource_docs_by_archive`;
# 资源文档的目录格式样例如下,en 为英文文档,zh 为中文文档,创建归档文件可使用指令 `tar czvf xxx.tgz en zh`:
# ./
# - en
# - get_user.md
# - zh
# - get_user.md
resource_docs:
# 资源文档的归档文件,可为 tar.gz,zip 格式文件
archivefile: "{{ settings.BK_APIGW_RESOURCE_DOCS_ARCHIVE_FILE }}" ## 环境变量方式:"{{ environ.BK_APIGW_RESOURCE_DOCS_ARCHIVE_FILE }}"
# 资源文档目录,basedir 与 archivefile 二者至少一个有效,若同时存在,则 archivefile 优先
basedir: "{{ settings.BK_APIGW_RESOURCE_DOCS_BASE_DIR }}" ## 环境变量方式:"{{ environ.BK_APIGW_RESOURCE_DOCS_BASE_DIR }}"
注意:
- 同步资源后,需要创建版本并发布才能生效,发布数据定义于 definition.yaml
release
- 资源配置 resources.yaml 变更时,需要更新 definition.yaml
release
中的版本号 version,以便正确创建资源版本及 SDK
用于定义资源配置,建议通过网关管理端导出。为了方便用户直接使用网关导出的资源文件,资源定义默认没有命名空间。
样例可参考:resources.yaml
资源文档,资源文档为 markdown 格式。资源文档的文件名,应为 资源名称
+ .md
格式,假如资源名称为 get_user,则文档文件名应为 get_user.md。
将资源的中文文档放到目录 zh
下,英文文档放到目录 en
下,如果某语言文档不存在,可忽略对应目录。
文档文件目录样例如下:
.
├── en
│ ├── create_user.md
│ └── get_user.md
└── zh
├── create_user.md
└── get_user.md
导入资源文档时,可以直接使用资源文档归档文件,也可以使用资源文档目录。参考上文 definition.yaml 样例, 在项目 definition.yaml 文件中,修改资源文档相关配置 resource_docs:
resource_docs:
# 资源文档的归档文件,可为 tar.gz,zip 格式文件;创建归档文件可使用指令 `tar czvf xxx.tgz en zh`
# archivefile: "{{ settings.BK_APIGW_RESOURCE_DOCS_ARCHIVE_FILE }}"
# 资源文档目录,basedir 与 archivefile 二者至少一个有效,若同时存在,则 archivefile 优先
# basedir: "{{ settings.BK_APIGW_RESOURCE_DOCS_BASE_DIR }}"
basedir: "support-files/apidocs/"
此方案适用于 Django 项目,具体请参考 sync-apigateway-with-django.md
此方案适用于非 Django 项目,具体请参考 sync-apigateway-with-docker.md
后端服务如需解析 API 网关发送的请求头 X-Bkapi-JWT,需要提前获取该网关的公钥。获取网关公钥,有以下方案。
在同步网关数据时,直接添加以下 Command 拉取网关公钥。网关公钥将保存在 model Context 对应的库表 apigw_manager_context 中,SDK 提供的 Django 中间件将从表中读取网关公钥。
# 默认拉取 settings.BK_APIGW_NAME 对应网关的公钥
python manage.py fetch_apigw_public_key
# 拉取指定网关的公钥
python manage.py fetch_apigw_public_key --gateway-name my-gateway
服务仅需接入一些固定的网关部署环境时,可在网关管理端,网关基本信息中查询网关公钥,并配置到项目配置文件。
蓝鲸官方网关,需要自动注册并获取网关公钥,可联系蓝鲸官方运营同学,在服务部署前,由官方提前创建网关,并设置网关公钥、私钥,同时将网关公钥同步给后端服务。 具体可参考 helm-charts 仓库的 README。
API 网关提供了公钥查询接口,后端服务可按需根据接口拉取网关公钥,接口信息如下:
# 将 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"}'
响应样例:
{
"data": {
"public_key": "your public key"
}
}
注意事项:
- 拉取公钥时,不能实时拉取,需要添加缓存(实时拉取会导致整体接口性能下降)
要在后端服务中认证 API 网关传递过来的请求头 X-Bkapi-JWT
,可以通过在 settings 中的 MIDDLEWARE 中添加以下 Django 中间件。这样,在请求处理过程中,会自动解析请求头中的 X-Bkapi-JWT,并将相关信息添加到 request 对象中。
MIDDLEWARE += [
"apigw_manager.apigw.authentication.ApiGatewayJWTGenericMiddleware", # JWT 认证,解析请求头中的 X-Bkapi-JWT,获取 request.jwt 对象
"apigw_manager.apigw.authentication.ApiGatewayJWTAppMiddleware", # 根据 request.jwt,获取 request.app 对象
]
添加以上两个中间件后,request 对象中将会添加 request.jwt
和 request.app
两个对象。这些对象包含了网关名、当前请求的蓝鲸应用 ID 等信息。具体内容可参考下文。
如果需要在 request 对象中获取当前请求用户 request.user
对象,除了上面的中间件外,还需要添加一个中间件以及 AUTHENTICATION_BACKENDS:
# 添加中间件
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 默认从以下两个位置获取网关公钥:
- SDK model Context (库表 apigw_manager_context),需提前执行
python manage.py fetch_apigw_public_key
拉取并保存网关公钥 - settings.APIGW_PUBLIC_KEY,可在网关页面中手动获取公钥,并配置到 settings 中
利用网关公钥,解析请求头中的 X-Bkapi-JWT,在 request
中注入 jwt
对象,有以下属性:
gateway_name
:传入的网关名称;
根据 request.jwt
,在 request
中注入 app
对象,有以下属性:
bk_app_code
:调用接口的应用;verified
:应用是否经过认证;
根据 request.jwt
,在 request
中注入 user
对象:
- 如果用户通过认证:其为一个 Django User Model 对象,用户名为当前请求用户的用户名
- 如果用户未通过认证,其为一个 Django AnonymousUser 对象,用户名为当前请求用户的用户名
如果中间件 ApiGatewayJWTUserMiddleware
中获取用户的逻辑不满足需求,可以继承此中间件并自定义用户获取方法 get_user
,例如::
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
)
注意:在自定义中间件 ApiGatewayJWTUserMiddleware
时,如果继续使用 auth.authenticate
获取用户,请确保正确设置用户认证后端,以遵循 Django AUTHENTICATION_BACKENDS
相关规则。
- 已认证的用户名,根据
UserModel
创建一个用户对象,不存在时返回None
; - 未认证的用户名,返回
AnonymousUser
,可通过继承后修改make_anonymous_user
的实现来定制具体字段;
本地开发测试时,接口可能未接入 API 网关,此时中间件 ApiGatewayJWTGenericMiddleware
无法获取请求头中的 X-Bkapi-JWT。
为方便测试,SDK 提供了一个 Dummy JWT Provider,用于根据环境变量直接构造一个 request.jwt 对象。
在项目中添加本地开发配置文件 local_settings.py
,并将其导入到 settings;然后,在此本地开发配置文件中添加配置:
BK_APIGW_JWT_PROVIDER_CLS = "apigw_manager.apigw.providers.DummyEnvPayloadJWTProvider"
同时提供以下环境变量(非 Django settings)
APIGW_MANAGER_DUMMY_GATEWAY_NAME # JWT 中的网关名
APIGW_MANAGER_DUMMY_PAYLOAD_APP_CODE # JWT payload 中的 app_code
APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username
非 Django 项目,需要项目获取网关公钥,并解析请求头中的 X-Bkapi-JWT;获取网关公钥的方案请参考上文。
解析 X-Bkapi-JWT 时,可根据 jwt header 中的 kid 获取当前网关名,例如:
{
"iat": 1701399603,
"typ": "JWT",
"kid": "my-gateway", # 网关名称
"alg": "RS512" # 加密算法
}
可从 jwt 内容中获取网关认证的应用、用户信息,例如:
{
"user": { # 用户信息
"bk_username": "admin", # 用户名,解析时需同时支持 bk_username、username 两个 key,如 user.get("bk_username") or user.get("username", "")
"verified": true # 用户是否通过认证,true 表示通过认证,false 表示未通过认证
},
"app": { # 蓝鲸应用信息
"bk_app_code": "my-app", # 蓝鲸应用ID,解析时需同时支持 bk_app_code、app_code 两个 key,如 app.get("bk_app_code") or app.get("app_code", "")
"verified": true # 应用是否通过认证,true 表示通过认证,false 表示未通过认证
},
"exp": 1701401103, # 过期时间
"nbf": 1701399303, # Not Before 时间
"iss": "APIGW" # 签发者
}
这种大概率是自定义脚本有问题,参照文档,按照对应目录下的 examples 的同步脚本即可。
2.执行同步命令时报错:Error responded by API Gateway, status_code:_code: 404, request_id:, error_code: 1640401, API not found
这种大概率是网关URL BK_API_URL_TMPL
漏配或者配错了。eg: BK_API_URL_TMPL: http://bkapi.example.com/api/{api_name}"l, 注意 {api_name}是占位符需要保留
这个是因为 definition.yaml
定义的 apigateway.api_type为 1,标记网关为官方网关,网关名需以 bk-
开头,可选;非官方网关,可去除此配置
当设置为 1 时,则 sync-apigateway.sh
里面的 gateway_name
参数需要以 bk- 开头