API Console
test
Sign in with BES

接入旧产品

做完后,不改旧产品(product)的代码,用户用 BES 账号登录:代理用登录客户端(client)登录,网关带服务账号(service account)的证书在每个请求上问 Control,旧产品只读三个请求头。

环境登录地址带证书调用的接口地址
localhttps://id.local.globalbes.sghttps://id.local.globalbes.sg:19443
testhttps://id.test.globalbes.sghttps://id.test.globalbes.sg:9443
staging待定待定
prod待定待定

Sign in to send requests to see which of these steps are done. Sign in

1. 登记,拿到凭据
1.1

创建产品

在哪里做:测试台

创建旧产品,得到 product_id。后面登记客户端、服务账号、功能和角色都要填它。

POSTProduct Create

在测试台点本步的 Product Create 标签,按下面的值填表(id 用前面步骤返回的),点 Send request。

填表的值:

{
  "request_ref": "create-cost",
  "code": "cost",
  "scope_level": "tenant",
  "display_name": "Cost Control"
}

返回:

{
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "code": "cost",
  "scope_level": "tenant",
  "sellable": true,
  "display_name": "Cost Control",
  "status": "active",
  "version": 0
}
  • code:小写字母开头,只用小写字母、数字和 _.-,登记后不再改。

错误:

HTTP 错误码 处理
409 PRODUCT_CODE_CONFLICT 这个 code 已被占用,换一个。
1.2

登记代理的登录客户端

在哪里做:测试台

登记代理用的登录客户端 cost-proxy,得到 client_secret。存成代理读取的文件,代理换令牌时用。

POSTClient Register

在测试台点本步的 Client Register 标签,按下面的值填表(id 用前面步骤返回的),点 Send request。

填表的值:

{
  "request_ref": "register-cost-proxy",
  "client_id": "cost-proxy",
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "deployment_version": "legacy-v1",
  "display_name": "Cost Control",
  "kind": "confidential_web",
  "auth_method": "client_secret_basic",
  "redirect_uris": [
    "https://cost.example.com/oauth2/callback",
    "https://cost.example.com/bes/tenant-chosen"
  ],
  "scopes": [
    "openid",
    "profile"
  ],
  "token_profile": "web",
  "dpop_required": false
}

返回:

{
  "client_id": "cost-proxy",
  "display_name": "Cost Control",
  "kind": "confidential_web",
  "auth_method": "client_secret_basic",
  "redirect_uris": [
    "https://cost.example.com/oauth2/callback",
    "https://cost.example.com/bes/tenant-chosen"
  ],
  "post_logout_redirect_uris": [],
  "backchannel_logout_uri": null,
  "scopes": [
    "openid",
    "profile"
  ],
  "dpop_required": false,
  "token_profile": "web",
  "service_account_id": null,
  "status": "pending",
  "version": 0,
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "public_key_jwk": null,
  "deployment_version": "legacy-v1",
  "client_secret": "<client_secret>"
}
  • 每个旧产品一个登录客户端。
  • redirect_uris:代理的回调 https://<旧产品主机>/oauth2/callback,逐字符一致;选租户(tenant)的回调也要登记在这里。
  • client_secret 只在这次返回里出现,存成代理读取的文件 /run/secrets/oidc-client。

错误:

HTTP 错误码 处理
409 CLIENT_ID_CONFLICT 这个 client_id 已被占用,换一个。
1.3

启用登录客户端

在哪里做:测试台

把 cost-proxy 改成 active,得到可用的登录客户端。用户从此能通过代理登录。

POSTClient Set Status

在测试台点本步的 Client Set Status 标签,按下面的值填表(id 用前面步骤返回的),点 Send request。

填表的值:

{
  "client_id": "cost-proxy",
  "request_ref": "activate-cost-proxy",
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "status": "active"
}

返回:

{
  "client_id": "cost-proxy",
  "display_name": "Cost Control",
  "kind": "confidential_web",
  "auth_method": "client_secret_basic",
  "redirect_uris": [
    "https://cost.example.com/oauth2/callback",
    "https://cost.example.com/bes/tenant-chosen"
  ],
  "post_logout_redirect_uris": [],
  "backchannel_logout_uri": null,
  "scopes": [
    "openid",
    "profile"
  ],
  "dpop_required": false,
  "token_profile": "web",
  "service_account_id": null,
  "status": "active",
  "version": 1,
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "public_key_jwk": null,
  "deployment_version": "legacy-v1"
}
1.4

创建网关的服务账号

在哪里做:测试台

创建网关的服务账号,得到 service_account_id。生成证书请求时写进证书。

POSTService Account Create

在测试台点本步的 Service Account Create 标签,按下面的值填表(id 用前面步骤返回的),点 Send request。

填表的值:

{
  "request_ref": "create-cost-gateway-account",
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "display_name": "Cost gateway"
}

返回:

{
  "service_account_id": "7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364",
  "registered_client_id": "svc-7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364",
  "display_name": "Cost gateway",
  "certificate_fingerprint": null,
  "not_before": null,
  "not_after": null,
  "previous_fingerprint": null,
  "previous_fingerprint_valid_until": null,
  "status": "pending",
  "version": 0,
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "control_role_id": "2e7f9a04-5c1b-4d83-a6f2-0b9c8d7e1a35",
  "role_id": "8b4d2f6a-3c71-4e95-a0d8-f6b2c4e8a137"
}
  • 账号给代理后面的私有网关用,带 Control 的 product-runtime 角色(role)。
  • 返回的 status 是 pending,登记证书后再启用。
1.5

生成私钥和证书请求

在哪里做:代理和网关的服务器

跑一条 openssl 命令,得到私钥 gateway-key.pem 和证书请求 gateway.csr。证书请求拿去签发;私钥留在网关所在的服务器上,不发给任何人。

CERT_DIR="$HOME/bes-certs/cost-gateway"
mkdir -p "$CERT_DIR"
SERVICE_ACCOUNT_ID=7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364   # 换成上一步返回的 service_account_id
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
  -subj "/CN=Cost gateway" \
  -addext "subjectAltName=URI:urn:bes:service:$SERVICE_ACCOUNT_ID" \
  -addext "basicConstraints=critical,CA:FALSE" \
  -addext "keyUsage=critical,digitalSignature" \
  -addext "extendedKeyUsage=clientAuth" \
  -keyout "$CERT_DIR/gateway-key.pem" -out "$CERT_DIR/gateway.csr"
  • 证书唯一的 URI SAN 是 urn:bes:service:<service_account_id>。
  • 私钥 gateway-key.pem 放进部署的密钥存储。
1.6

签发证书

在哪里做:客户端 CA 所在的机器

用客户端 CA 给证书请求签名,得到证书 gateway-cert.pem。证书拷回网关所在的服务器,和私钥一起用来调 Control。

前提:OpenSSL 3.0 及以上,openssl version 显示 OpenSSL 3.x。Mac 自带的 /usr/bin/openssl 是 LibreSSL,不认 -copy_extensions:先执行 brew install openssl@3,再执行 export PATH="$(brew --prefix openssl@3)/bin:$PATH"。

本环境(test)

  • CA_DIR:待定,test 环境的客户端 CA 位置未定。
  • 签发前把 gateway.csr 拷到客户端 CA 所在机器的 ~/bes-certs/cost-gateway/;签发后把 gateway-cert.pem 拷回网关所在的服务器的同一目录,和私钥放在一起。
CERT_DIR="$HOME/bes-certs/cost-gateway"
mkdir -p "$CERT_DIR"
CA_DIR="待定"
openssl x509 -req -days 825 -sha256 -in "$CERT_DIR/gateway.csr" \
  -CA "$CA_DIR/client-ca.pem" -CAkey "$CA_DIR/client-ca-key.pem" \
  -CAserial "$CERT_DIR/gateway.srl" -CAcreateserial -copy_extensions copy \
  -out "$CERT_DIR/gateway-cert.pem"
其他环境

local

本地的客户端 CA 在本机,证书请求不用拷,在本机执行:

CERT_DIR="$HOME/bes-certs/cost-gateway"
mkdir -p "$CERT_DIR"
CA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/bes/local/stack/secrets/control"
openssl x509 -req -days 825 -sha256 -in "$CERT_DIR/gateway.csr" \
  -CA "$CA_DIR/client-ca.pem" -CAkey "$CA_DIR/client-ca-key.pem" \
  -CAserial "$CERT_DIR/gateway.srl" -CAcreateserial -copy_extensions copy \
  -out "$CERT_DIR/gateway-cert.pem"

staging

  • CA_DIR:待定,staging 环境的客户端 CA 位置未定。
  • 签发前把 gateway.csr 拷到客户端 CA 所在机器的 ~/bes-certs/cost-gateway/;签发后把 gateway-cert.pem 拷回网关所在的服务器的同一目录,和私钥放在一起。
CERT_DIR="$HOME/bes-certs/cost-gateway"
mkdir -p "$CERT_DIR"
CA_DIR="待定"
openssl x509 -req -days 825 -sha256 -in "$CERT_DIR/gateway.csr" \
  -CA "$CA_DIR/client-ca.pem" -CAkey "$CA_DIR/client-ca-key.pem" \
  -CAserial "$CERT_DIR/gateway.srl" -CAcreateserial -copy_extensions copy \
  -out "$CERT_DIR/gateway-cert.pem"

prod

  • CA_DIR:待定,prod 环境的客户端 CA 位置未定。
  • 签发前把 gateway.csr 拷到客户端 CA 所在机器的 ~/bes-certs/cost-gateway/;签发后把 gateway-cert.pem 拷回网关所在的服务器的同一目录,和私钥放在一起。
CERT_DIR="$HOME/bes-certs/cost-gateway"
mkdir -p "$CERT_DIR"
CA_DIR="待定"
openssl x509 -req -days 825 -sha256 -in "$CERT_DIR/gateway.csr" \
  -CA "$CA_DIR/client-ca.pem" -CAkey "$CA_DIR/client-ca-key.pem" \
  -CAserial "$CERT_DIR/gateway.srl" -CAcreateserial -copy_extensions copy \
  -out "$CERT_DIR/gateway-cert.pem"
1.7

算指纹和有效期

在哪里做:代理和网关的服务器

从证书算出指纹和起止时间,得到三个值。下一步登记证书时填进 certificate_fingerprint、certificate_not_before、certificate_expires_at。

CERT_DIR="$HOME/bes-certs/cost-gateway"
openssl x509 -in "$CERT_DIR/gateway-cert.pem" -outform DER | openssl dgst -sha256 | awk '{print $NF}'
openssl x509 -in "$CERT_DIR/gateway-cert.pem" -noout -startdate -enddate
  • 第一行输出是 certificate_fingerprint:证书 DER 编码的 SHA-256,64 位十六进制。
  • notBefore= 后的时间是 certificate_not_before,notAfter= 后的是 certificate_expires_at,改写成 RFC 3339 UTC,例如 Jan 1 08:00:00 2026 GMT 写成 2026-01-01T08:00:00Z。
  • 证书和私钥拷成网关读取的 /run/secrets/gateway-cert、/run/secrets/gateway-key。
1.8

登记网关的证书

在哪里做:测试台

把指纹和有效期登记到网关的服务账号上,得到登记了证书的账号。之后 Control 认得这张证书。

POSTService Account Register

在测试台点本步的 Service Account Register 标签,按下面的值填表(id 用前面步骤返回的),点 Send request。

填表的值:

{
  "service_account_id": "7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364",
  "request_ref": "register-cost-gateway-certificate",
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "display_name": "Cost gateway",
  "certificate_fingerprint": "<certificate_fingerprint>",
  "certificate_not_before": "2026-01-01T08:00:00Z",
  "certificate_expires_at": "2028-04-05T08:00:00Z"
}

返回:

{
  "service_account_id": "7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364",
  "registered_client_id": "svc-7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364",
  "display_name": "Cost gateway",
  "certificate_fingerprint": "<certificate_fingerprint>",
  "not_before": "2026-01-01T08:00:00+00:00",
  "not_after": "2028-04-05T08:00:00+00:00",
  "previous_fingerprint": null,
  "previous_fingerprint_valid_until": null,
  "status": "pending",
  "version": 1,
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "control_role_id": "2e7f9a04-5c1b-4d83-a6f2-0b9c8d7e1a35",
  "tenant_id": null,
  "project_id": null,
  "role_id": "8b4d2f6a-3c71-4e95-a0d8-f6b2c4e8a137"
}
  • product_id、display_name 与创建服务账号时相同;指纹和有效期取上一步算出的值。

错误:

HTTP 错误码 处理
400 CERTIFICATE_FINGERPRINT_INVALID 指纹要 64 位十六进制,重新计算。
409 CERTIFICATE_FINGERPRINT_CONFLICT 这张证书已登记给别的账号,重新生成证书。
1.9

启用网关的服务账号

在哪里做:测试台

把网关的服务账号改成 active,得到可用的服务账号。网关从此能带证书调 Control。

POSTService Account Set Status

在测试台点本步的 Service Account Set Status 标签,按下面的值填表(id 用前面步骤返回的),点 Send request。

填表的值:

{
  "service_account_id": "7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364",
  "request_ref": "activate-cost-gateway-account",
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "status": "active"
}

返回:

{
  "service_account_id": "7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364",
  "registered_client_id": "svc-7f3c1a9e-4b62-4d85-9e17-c0a8d5b2f364",
  "display_name": "Cost gateway",
  "certificate_fingerprint": "<certificate_fingerprint>",
  "not_before": "2026-01-01T08:00:00+00:00",
  "not_after": "2028-04-05T08:00:00+00:00",
  "previous_fingerprint": null,
  "previous_fingerprint_valid_until": null,
  "status": "active",
  "version": 2,
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "control_role_id": "2e7f9a04-5c1b-4d83-a6f2-0b9c8d7e1a35",
  "tenant_id": null,
  "project_id": null,
  "role_id": "8b4d2f6a-3c71-4e95-a0d8-f6b2c4e8a137"
}
  • 没登记证书或证书已过期时不能启用。

做完这一部分,手里有:product_id、代理的 client_secret、网关的证书和私钥。client_secret 给代理登录,证书和私钥给网关用。

2. 登记功能(feature)和角色
2.1

登记进入功能

在哪里做:测试台

登记一个进入功能 legacy.entry,得到只有这一项的功能清单。网关按它检查每个请求。

PUTFeature Register

在测试台点本步的 Feature Register 标签,按下面的值填表(id 用前面步骤返回的),点 Send request。

填表的值:

{
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "deployment_version": "legacy-v1",
  "request_ref": "register-cost-entry",
  "features": [
    {
      "code": "legacy.entry",
      "route_template": "/legacy-entry",
      "method": "GET",
      "risk": "low",
      "display_name": "Enter Cost Control"
    }
  ]
}

返回:

[
  {
    "feature_id": "9c5e3a7b-4d82-4fa6-b1e9-07c3d5f9b248",
    "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
    "code": "legacy.entry",
    "route_template": "/legacy-entry",
    "method": "GET",
    "risk": "low",
    "display_name": "Enter Cost Control",
    "deployment_version": "legacy-v1",
    "status": "active"
  }
]
  • 只登记这一个功能:code legacy.entry、method GET、route_template /legacy-entry、deployment_version legacy-v1、risk low。
  • 进入后能做什么,仍由旧产品自己决定。
2.2

定义角色

在哪里做:测试台

定义含 legacy.entry 的角色,得到 role_id。给人分配进入旧产品的权限时填它。

PUTRole Define

在测试台点本步的 Role Define 标签,按下面的值填表(id 用前面步骤返回的),点 Send request。

填表的值:

{
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "code": "cost-user",
  "request_ref": "define-cost-user",
  "display_name": "Cost Control user",
  "feature_codes": [
    "legacy.entry"
  ]
}

返回:

{
  "role_id": "0e6f4b8c-5a93-40b7-82fa-18d4e6a0c359",
  "product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
  "code": "cost-user",
  "display_name": "Cost Control user",
  "scope_level": "tenant",
  "feature_codes": [
    "legacy.entry"
  ],
  "status": "active",
  "tenant_id": null,
  "project_id": null,
  "version": 0
}
  • 能进入旧产品的每个角色的 feature_codes 都要有 legacy.entry。

做完这一部分,手里有:功能 legacy.entry 和角色的 role_id。给人分配进入权限时填 role_id。

3. 在旧产品前面放代理
只有代理对外可达,旧产品只接受私有网关的连接;否则任何人都能自己发身份请求头。代理的请求日志和认证日志保持关闭,这些日志含令牌。
3.1

部署 oauth2-proxy

在哪里做:代理和网关的服务器

在旧产品前面部署 oauth2-proxy,得到对外的登录入口。用户经它登录,令牌留在代理的服务端会话里。

GETAuthorize (sign-in page)POSTToken
  • 模板在 bes-infra 的 components/oauth2-proxy/:oauth2-proxy.cfg、nginx.conf.template、compose.example.yaml;镜像 quay.io/oauth2-proxy/oauth2-proxy:v7.15.4。
  • 环境变量:OAUTH2_PROXY_OIDC_ISSUER_URL 写 https://id.test.globalbes.sg,OAUTH2_PROXY_CLIENT_ID 写 cost-proxy,OAUTH2_PROXY_REDIRECT_URL 写 https://cost.example.com/oauth2/callback,OAUTH2_PROXY_UPSTREAMS 写下一步的私有网关,OAUTH2_PROXY_COOKIE_NAME 写 __Host-cost-rp(不设 cookie 域),再加 Redis 连接。
  • 每个产品一个登录客户端、一个 cookie 名、一个 Redis 命名空间。
oauth2-proxy(oauth2-proxy.cfg)
provider = "oidc"
scope = "openid profile"
http_address = "0.0.0.0:4180"
code_challenge_method = "S256"
insecure_oidc_skip_nonce = false
insecure_oidc_skip_issuer_verification = false
skip_claims_from_profile_url = true
# The stable subject fills the proxy's mandatory Email slot; it is not an email address.
oidc_email_claim = "sub"
email_domains = ["*"]
client_secret_file = "/run/secrets/oidc-client"
cookie_secret_file = "/run/secrets/rp-cookie"
cookie_secure = true
cookie_httponly = true
cookie_samesite = "lax"
cookie_expire = "8h"
cookie_refresh = "1m"
pass_access_token = true
pass_authorization_header = false
set_authorization_header = false
reverse_proxy = true
session_store_type = "redis"
request_logging = false
auth_logging = false

做完这一部分,手里有:对外的 oauth2-proxy,用户经它登录,令牌留在代理的会话里。

4. 验证登录状态
4.1

让用户选租户

在哪里做:代理和网关的配置

代理带用户的 access_token 发起选租户,用户选好后得到 context_id。写进 cookie __Host-BESLegacyContext,网关每次带上。

POSTSession Tenant Selection Start

程序里发这个请求(id 用前面步骤返回的):

带:证书 + 用户的 access_token

请求:

POST https://id.test.globalbes.sg:9443/api/sessions/tenant-selection
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "continue_url": "https://cost.example.com/bes/tenant-chosen"
}

返回:

HTTP/1.1 201 Created

{
  "selection_id": "9c4e1b7a-2d58-4f3c-b6a0-e81d5f2c7a94",
  "selection_url": "/tenants?selection=9c4e1b7a-2d58-4f3c-b6a0-e81d5f2c7a94",
  "expires_at": "2026-01-01T08:05:00+00:00"
}
  • 模板只读这个 cookie,不发起选择;这一步加在代理一侧。
  • 用户的 access_token 取 oauth2-proxy 传来的 X-Forwarded-Access-Token(pass_access_token = true),带网关证书调用。
  • continue_url 必须与 cost-proxy 登记的 redirect_uris 之一完全一致。
  • 把浏览器跳到 https://id.test.globalbes.sg + selection_url;用户选好租户后回到 https://cost.example.com/bes/tenant-chosen?context_id=0f6b8d2e-4a17-4c93-9e5b-a1d27c4f3b68。
  • 把 context_id 写进 cookie __Host-BESLegacyContext(Secure、Path=/、不设 Domain)。
  • 选择 5 分钟内有效。

错误:

HTTP 错误码 处理
401 LOGIN_REQUIRED BES 登录已结束,重新登录后再发起。
4.2

每个请求问 Control

在哪里做:代理和网关的配置

网关在每个请求上带证书、access_token、context_id 问 Control,通过就得到这个人的身份头。网关把它们传给旧产品。

GETGateway Validate

程序里发这个请求(id 用前面步骤返回的):

带:证书 + 用户的 access_token

请求:

GET https://id.test.globalbes.sg:9443/internal/auth/validate
Authorization: Bearer <access_token>
X-BES-Product: 8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246
X-BES-Legacy-Identity: true
X-BES-Context-Id: 0f6b8d2e-4a17-4c93-9e5b-a1d27c4f3b68
X-Original-Method: GET
X-Original-URI: /legacy-entry
X-Original-Host: cost.example.com
X-BES-Deployment-Version: legacy-v1

返回:

HTTP/1.1 200 OK
  • 网关是代理和旧产品之间的私有 nginx,带网关证书调 https://id.test.globalbes.sg:9443;这个接口只返回状态码和响应头,没有响应体。
  • 200:放行,并把返回头 X-BES-Person-Id、X-BES-External-Id、X-BES-Tenant-Id 传给旧产品。
  • 401:登录已结束或令牌无效,让用户重新登录。
  • 403:拒绝,原因在返回头 X-BES-Auth-Reason:access_denied 没有进入权限,entitlement_missing 租户没有有效的开通记录(entitlement),target_unknown 先登记 legacy.entry 功能。
  • 其他状态和超时一律拒绝。
  • cookie __Host-BESLegacyContext 为空(还没选租户)时,网关直接返回 403。
  • 能自己发 HTTP 请求的产品可以改用 session.validate,见「接入新产品」的「验证每个请求」。
nginx(nginx.conf.template)
location = /_bes_validate {
    internal;
    proxy_pass ${CONTROL_INTERNAL_ORIGIN}/internal/auth/validate;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header Cookie "";
    proxy_set_header Authorization "Bearer $http_x_forwarded_access_token";
    proxy_set_header X-BES-TLS-Verified "";
    proxy_set_header X-BES-Client-Cert "";
    proxy_set_header X-BES-Operator "";
    proxy_set_header X-BES-Product "${LEGACY_PRODUCT_ID}";
    proxy_set_header X-BES-Legacy-Identity true;
    proxy_set_header X-BES-Context-Id $cookie___host_beslegacycontext;
    proxy_set_header X-BES-Tenant "";
    proxy_set_header X-BES-Project "";
    # The registered admission feature, not a legacy permission decision.
    proxy_set_header X-Original-Method GET;
    proxy_set_header X-Original-URI /legacy-entry;
    proxy_set_header X-Original-Host $host;
    proxy_set_header X-BES-Deployment-Version legacy-v1;
    proxy_ssl_server_name on;
    proxy_ssl_name ${CONTROL_HOST};
    proxy_ssl_verify on;
    proxy_ssl_trusted_certificate /run/secrets/control-ca;
    proxy_ssl_certificate /run/secrets/gateway-cert;
    proxy_ssl_certificate_key /run/secrets/gateway-key;
}
location / {
    if ($cookie___host_beslegacycontext = "") { return 403; }
    auth_request /_bes_validate;
    auth_request_set $bes_person $upstream_http_x_bes_person_id;
    auth_request_set $bes_external $upstream_http_x_bes_external_id;
    auth_request_set $bes_tenant $upstream_http_x_bes_tenant_id;
    proxy_set_header X-BES-Person-Id $bes_person;
    proxy_set_header X-BES-External-Id $bes_external;
    proxy_set_header X-BES-Tenant-Id $bes_tenant;
    proxy_set_header Authorization "";
    proxy_set_header Cookie "";
    proxy_set_header X-Forwarded-Access-Token "";
    proxy_set_header X-Auth-Request-Access-Token "";
    proxy_set_header X-Forwarded-User "";
    proxy_set_header X-Forwarded-Email "";
    proxy_set_header X-Forwarded-Groups "";
    proxy_set_header X-Forwarded-Preferred-Username "";
    proxy_set_header X-BES-Context "";
    proxy_pass ${LEGACY_UPSTREAM};
}
4.3

旧产品读三个请求头

在哪里做:代理和网关的配置

旧产品读网关传来的三个请求头,得到 BES 用户、租户和对应的旧用户 id。按它们认人和取数据。

  • X-BES-Person-Id:BES 的 person_id。
  • X-BES-Tenant-Id:验证过的租户。
  • X-BES-External-Id:此人在本产品、本租户的旧用户 id,UTF-8 百分号编码,先解码;没有对应时为空。
  • 只信网关发来的这三个头,外部请求带来的同名头丢掉。
  • X-BES-External-Id 为空表示旧账号没导入,按这个状态显示。
  • 对应关系只在 Control 主机上导入旧账号时写入:bes-control import-people,文件格式 control/legacy-import.jsonl.schema.json。

做完这一部分,手里有:选好租户的用户,每个请求经网关核对后带着三个身份头到旧产品。

5. 其他功能

做完这一部分,手里有:需要时查到的某个人在本产品、本租户的旧用户 id。