接入旧产品
做完后,不改旧产品(product)的代码,用户用 BES 账号登录:代理用登录客户端(client)登录,网关带服务账号(service account)的证书在每个请求上问 Control,旧产品只读三个请求头。
| 环境 | 登录地址 | 带证书调用的接口地址 |
|---|---|---|
local | https://id.local.globalbes.sg | https://id.local.globalbes.sg:19443 |
test | https://id.test.globalbes.sg | https://id.test.globalbes.sg:9443 |
staging | 待定 | 待定 |
prod | 待定 | 待定 |
Sign in to send requests to see which of these steps are done. Sign in
创建产品
在哪里做:测试台
创建旧产品,得到 product_id。后面登记客户端、服务账号、功能和角色都要填它。
在测试台点本步的 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 已被占用,换一个。 |
登记代理的登录客户端
在哪里做:测试台
登记代理用的登录客户端 cost-proxy,得到 client_secret。存成代理读取的文件,代理换令牌时用。
在测试台点本步的 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 已被占用,换一个。 |
启用登录客户端
在哪里做:测试台
把 cost-proxy 改成 active,得到可用的登录客户端。用户从此能通过代理登录。
在测试台点本步的 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"
}
创建网关的服务账号
在哪里做:测试台
创建网关的服务账号,得到 service_account_id。生成证书请求时写进证书。
在测试台点本步的 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,登记证书后再启用。
生成私钥和证书请求
在哪里做:代理和网关的服务器
跑一条 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放进部署的密钥存储。
签发证书
在哪里做:客户端 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"
算指纹和有效期
在哪里做:代理和网关的服务器
从证书算出指纹和起止时间,得到三个值。下一步登记证书时填进 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。
登记网关的证书
在哪里做:测试台
把指纹和有效期登记到网关的服务账号上,得到登记了证书的账号。之后 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 |
这张证书已登记给别的账号,重新生成证书。 |
启用网关的服务账号
在哪里做:测试台
把网关的服务账号改成 active,得到可用的服务账号。网关从此能带证书调 Control。
在测试台点本步的 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 给代理登录,证书和私钥给网关用。
登记进入功能
在哪里做:测试台
登记一个进入功能 legacy.entry,得到只有这一项的功能清单。网关按它检查每个请求。
在测试台点本步的 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"
}
]
- 只登记这一个功能:
codelegacy.entry、methodGET、route_template/legacy-entry、deployment_versionlegacy-v1、risklow。 - 进入后能做什么,仍由旧产品自己决定。
定义角色
在哪里做:测试台
定义含 legacy.entry 的角色,得到 role_id。给人分配进入旧产品的权限时填它。
在测试台点本步的 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。
部署 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,用户经它登录,令牌留在代理的会话里。
让用户选租户
在哪里做:代理和网关的配置
代理带用户的 access_token 发起选租户,用户选好后得到 context_id。写进 cookie __Host-BESLegacyContext,网关每次带上。
程序里发这个请求(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 登录已结束,重新登录后再发起。 |
每个请求问 Control
在哪里做:代理和网关的配置
网关在每个请求上带证书、access_token、context_id 问 Control,通过就得到这个人的身份头。网关把它们传给旧产品。
程序里发这个请求(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};
}
旧产品读三个请求头
在哪里做:代理和网关的配置
旧产品读网关传来的三个请求头,得到 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。
做完这一部分,手里有:选好租户的用户,每个请求经网关核对后带着三个身份头到旧产品。
查某个人对应的旧用户
在哪里做:代理和网关的配置
在用户请求之外按 person_id 查对应的旧用户,得到 external_id。程序在没有请求头的场合用它找旧账号。
程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
GET https://id.test.globalbes.sg:9443/internal/control/v1/identity-links?product_id=8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246&tenant_id=7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11&person_id=5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475
返回:
HTTP/1.1 200 OK
{
"external_id": "legacy-user-10482"
}
external_id: null表示没有有效的对应。
做完这一部分,手里有:需要时查到的某个人在本产品、本租户的旧用户 id。