OPA 鉴权策略
概述
OPA 鉴权策略是云开发提供的 环境级请求鉴权能力,基于开源策略引擎 Open Policy Agent (OPA) 和策略语言 Rego 构建。通过在云开发控制台编写一段策略代码,可以为整个环境的 HTTP API 和 HTTP 访问服务 请求定义统一的访问控制规则。
云开发会在每次收到请求时执行策略,根据 用户身份、HTTP 请求、环境上下文 三类信息做出"放行 / 拦截"决策,再将请求转发到实际调用的资源。
用户请求
↓
云开发
↓
构造 input(subject + request + cloudbase)
↓
执行 OPA 策略评估
├─ allow = true 且 deny = false → 放行 → 访问实际资源
└─ deny = true → 拒绝,返回 403 + 原因
默认情况下,云开发为 HTTP API 和 HTTP 访问服务内置一套平台默认策略,覆盖各类资源在不同身份下的默认放通与拒绝规则,具体策略规则见 附录 A:平台默认策略。
用户策略可在平台默认策略之上进行追加调整:
- 用户策略中定义的
allow(放通)可放开被平台默认拒绝的请求 - 用户策略中定义的
deny(拒绝)可拒绝被平台默认放通的请求(deny优先级高于allow)
通过用户策略的调整,可灵活、细粒度地定义基于 HTTP 请求、用户身份、访问资源或多种条件组合的鉴权规则,实现高度可定制的权限管控。通过编写 OPA 策略,可实现下列需求:
- IP 白名单访问控制
- 按用户身份控制读写权限
- 保护管理后台接口
- 按访问入口执行不同规则
- ...
关于完整实现示例,请参考 实战案例。
核心能力
| 能力 | 说明 |
|---|---|
| 多条件组合鉴权 | 可基于用户身份、请求特征、环境信息等多维度条件灵活组合鉴权规则,实现细粒度访问控制 |
| 统一策略入口 | 同一份策略同时覆盖 HTTP API 和 HTTP 访问服务(云函数/云托管/静态托管等)入口,统一管控公网流量 |
| 拒绝原因可溯源 | 拒绝请求时可向调用方返回可读的拒绝原因,便于快速排查权限问题 |
| 在平台默认策略基础上定制 | 平台已内置各资源类型的默认放通/拒绝规则,你只需编写策略调整需要覆盖的规则,无需从零开始 |
| 支持标准 Rego 语法 | 基于 OPA 开源策略引擎,使用通用的 Rego 策略语言,学习成本低,社区资源丰富 |
与其他安全能力的关系
云开发提供了多层安全能力,OPA 鉴权策略位于请求入口的 访问控制 层,与其他能力协作形成防护体系:
| 能力 | 作用域 | 控制维度 | 何时选用 |
|---|---|---|---|
| OPA 鉴权策略 | 环境内所有 HTTP API 和 HTTP 访问服务 请求 | 用户 + 请求 + 环境上下文 | 需要复杂条件组合、基于请求级别、可编程的访问控制 |
| 限频设置 | 云函数、云托管 | QPS(资源维度 / 客户端维度) | 防刷、防过载 |
| 静态托管安全配置 | 静态托管资源 | Referer / IP / QPS | 静态资源防盗链 |
| 安全来源 | 客户端 SDK 调用 | 域名白名单 | 限制 SDK 调用来源 |
OPA 鉴权策略与上述能力可同时启用——请求会先通过安全来源校验与 OPA 策略评估,再进入限频等后续环节。
快速开始
以下为一段最小可用策略:
package authz.user
# 默认拒绝
default allow := false
# 管理员访问 /v1/ 路径放行
allow if {
input.subject.auth_type == "administrator"
startswith(input.request.path, "/v1/")
}
# 未认证用户禁止 DELETE
deny contains "DELETE requires authentication" if {
input.request.method == "DELETE"
input.subject.auth_type == "unauthenticated"
}
示例中的 startswith 是 Rego 内建函数。完整语法与内建函数清单见 Rego 语言参考 与 Rego 内建函数列表;出于安全考虑,CloudBase 仅启用其中的一个白名单子集,常见禁用函数见附录 B。
策略输入 input 由云开发在每次请求时构造,完整结构如下,字段详细定义请参考 策略上下文:
{
"subject": {
"user_id": "c-user-123",
"auth_type": "administrator",
"groups": ["developer"]
},
"request": {
"method": "GET",
"raw_host": "env-xxx.api.tcloudbasegateway.com",
"host": "env-xxx.api.tcloudbasegateway.com",
"path": "/v1/functions/foo",
"query": {"page": "1"},
"client_ip": "10.1.2.3",
"header": {"X-Env-Id": ["env-xxx"]},
"header_map": {"X-Env-Id": "env-xxx"}
},
"cloudbase": {
"env_id": "env-xxx",
"region": "ap-shanghai",
"entrypoint_type": "tcbopenapi",
"resource_type": "functions"
}
}
决策矩阵速查:
deny | allow | 最终结果 |
|---|---|---|
true | 任意 | 拒绝 |
false | true | 允许 |
false | false | 拒绝 |
策略上下文(input)
input 是策略可读取的唯一外 部数据,固定包含三个顶层字段:
input.subject # 请求主体(用户)
input.request # 当前 HTTP 请求
input.cloudbase # CloudBase 环境上下文
用户信息(subject)
| 字段 | 类型 | 说明 |
|---|---|---|
user_id | string | C 端用户 ID;未登录为 "" |
auth_type | string | 内置身份认证类型,见下表;区分大小写 |
groups | []string | 请求关联的用户角色,对应请求 JWT 中的 groups 字段 |
auth_type 的全部取值:
| 值 | 含义 |
|---|---|
administrator | 管理员 |
internal | 内部用户 |
external | 外部用户 |
anonymous | 匿名登录用户 |
unauthenticated | 未登录 / 无 AccessToken |
service_role | (仅使用 PostgreSQL 环境存在)service_role,超级权限 |
anon | (仅使用 PostgreSQL 环境存在)匿名角色 |
authenticated | (仅使用 PostgreSQL 环境存在)已认证角色 |
请求信息(request)
| 字段 | 类型 | 说明 |
|---|---|---|
method | string | HTTP 方法,已统一为大写 |
raw_host | string | 原始 Host 请求头,未经规整 |
host | string | 规整后的 Host(去除端口、统一小写) |
path | string | 请求路径,保留 URL 编码 |
query | object<string,string> | URL query 参数;多值参数以 & 拼接 |
client_ip | string | 客户端 IP,可能为 "" / IPv4 / IPv6 |
header | object<string,array<string>> | 请求头,保留多值 |
header_map | object<string,string> | 请求头 |
header 与 header_map 的差别:
header | header_map | |
|---|---|---|
| 值类型 | array<string> | string |
| 读法 | input.request.header["X-Foo"][0] | input.request.header_map["X-Env-Id"] |
Header key 必须使用 HTTP Canonical 形式(如 X-Env-Id、Content-Type),小写或全大写均无法读取。Authorization / Cookie 等敏感头已被云开发剔除,策略无法访问。
环境信息(cloudbase)
| 字段 | 类型 | 说明 |
|---|---|---|
env_id | string | 当前环境 ID |
region | string | 环境所属地域,如 "ap-shanghai" |
entrypoint_type | string | 接入入口类型,见下 |
resource_type | string | 当前请求命中的资源类型,见下 |
entrypoint_type 取值:
| 值 | 含义 |
|---|---|
tcbopenapi | HTTP API 链路,域名为 {envid}.api.tcloudbasegateway.com |
tcbgateway | HTTP 访问服务 / 云托管 / 静态托管 / 云存储等 HTTP 访问服务,默认域名包括 .app.tcloudbase.com / .service.tcloudbase.com(HTTP 访问服务)、.tcloudbaseapp.com(静态托管)、.tcb.qcloud.la(云存储)、.run.tcloudbase.com(云托管) 等;也包括为上述链路绑定的自定义域名 |
resource_type 取值:
| 资源标识 | 标识 | 说明 |
|---|---|---|
| 云存储 | storages | 对象存储操作 |
| 云函数 | functions | 云函数调用 |
| 云托管 | cloudrun | 云托管服务访问 |
| 大模型 | ai | AI 大模型接入 |
| AI 智能体 | aibot | AI 智能体服务 |
| 数据模型 | model | 数据模型管理 |
| 知识库 | knowledge | 知识库管理 |
| MySQL 访问 | rdb | MySQL 数据库访问 |
输出:allow / deny
策略需在 package authz.user 下声明 allow 或 deny,至少其一:
| 规则 | 类型 | 写法 |
|---|---|---|
allow | bool | default allow := falseallow if { ... } |
deny | bool 或 set of string | deny if { ... } 或 deny contains "拒绝原因" if { ... } |
deny 支持两种写法:
- 布尔模式
deny if { ... }— 拦截,不返回原因。 - 集合模式
deny contains "msg" if { ... }— 拦截,并把消息回显给终端用户:Access denied by policy. Reason: msg。
引擎独立求值每条规则,最终根据 allow 和 deny 的结果决定是否放行。
策略编写注意点
- 强制 Rego v1:无需写
import rego.v1;v0 已废弃语法(如allow { ... })在保存时会被拒绝。 - package 必须精确为
authz.user:不符合要求的包名在保存时均会被拒绝。 - 策略大小 ≤ 2 KiB(含注释和空行)。
- 非
default规则数 ≤ 20 条:每个allow if {...}、deny if {...}、deny contains "msg" if {...}各算一条;default声明不计入。 deny原因不应包含敏感信息:deny集合中的消息会回显给终端用户,避免拼接内部 userID、SQL、token 等内容。- 严格模式:保存策略时启用 OPA strict mode,未使用的
import与未使用的函数参数(非通配符)会被拒绝。 - 可用的内建函数受限:本引擎采用白名单机制,常见的被禁用函数见附录 B。
- 不会经过 OPA 策略评估的请求:云开发平台内部请求和集成中心回调请求不会经过策略评估。
实战案例
按用户身份控制读写权限
package authz.user
default allow := false
# 未认证用户只能读
allow if {
input.subject.auth_type == "unauthenticated"
input.request.method in {"GET", "HEAD", "OPTIONS"}
}
# 已认证 / 匿名 C 端用户可读写
allow if {
input.subject.auth_type in {"internal", "external", "anonymous"}
input.request.method in {"GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"}
}
# 管理员不限方法
allow if input.subject.auth_type == "administrator"