ClickHouse 可以使用 JSON Web Token (JWT) 对用户进行身份验证。与其他外部身份验证器 (如 LDAP 或 Kerberos) 不同,JWT 身份验证不会验证预先存在用户的身份。相反,它会根据嵌入在每个令牌中的声明动态创建临时用户。这些用户仅存在于内存中,其访问权限由令牌声明派生,并会在令牌过期后自动移除。
这使得 JWT 身份验证与基于密码或证书的方法有本质区别:不存在 CREATE USER ... IDENTIFIED WITH jwt 语句,尝试这样做会引发异常。JWT 用户完全由令牌的生命周期管理。
身份验证流程如下:
- 客户端通过支持的传输机制之一提交已签名的 JWT (HTTP
Authorization: Bearer 请求头、TCP 原生协议,或 gRPC jwt 字段) 。
- ClickHouse 验证令牌签名。
- 验证必需的声明 (
exp、iat、iss、sub、aud) 。
- 在内存中创建一个临时用户,其访问权限由令牌声明
clickhouse:grants 和 clickhouse:roles 推导得出,并与权限上限取交集。
- 当令牌过期时,后台垃圾回收任务会移除该用户。
提交给 ClickHouse 的每个 JWT 都必须包含以下声明:
| 声明 | 说明 |
|---|
alg | 签名算法 (请求头声明) 。支持的值:HS256、RS256、ES256。 |
exp | 过期时间。用于设置临时用户的 valid_until。 |
iat | 签发时间。用于防止同一身份的旧令牌被重放。 |
iss | 签发方。会与提供商预期的签发方进行匹配。 |
sub | 主体。会成为生成用户名的一部分。 |
aud | 受众。会与提供商预期的受众进行匹配。 |
使用基于 JWKS 的密钥解析时,还必须提供 kid (密钥 ID) 请求头声明。
JWKS 模式仅支持 RSA 密钥静态密钥提供商可以接受 HS256、RS256 或 ES256 中的任意一种,而基于 JWKS 的提供商只接受 kty 为 RSA 的 JWK (即使用 RS256 签名的令牌) 。使用 HMAC (HS256) 或 EC (ES256) 密钥签名的令牌无法通过 JWKS 端点验证,因此会被拒绝。
| 声明 | 描述 |
|---|
nbf | 生效时间。在此声明不是必需的;但如果提供了该声明,则在该时间之前,令牌会被拒绝。 |
jti | 保留字段。令牌中可以包含该声明,但当前不会对其进行验证或使用。 |
| Claim | 默认名称 | 描述 |
|---|
| 授权 | clickhouse:grants | 由 SQL GRANT 片段组成的 JSON 数组,例如 ["SELECT ON db.*", "INSERT ON db.table1"]。每个元素都会被解析为 GRANT 语句的主体。 |
| 角色 | clickhouse:roles | 要分配的角色名称 JSON 数组,例如 ["analyst", "reader"]。 |
| 如果身份提供商使用不同的命名约定,可以将默认的 claim 名称重映射为自定义 claim 名称。 | | |
{
"alg": "RS256",
"kid": "my-key-id"
}
{
"iss": "https://idp.example.com",
"sub": "jane.doe",
"aud": "my-clickhouse-cluster",
"exp": 1719504000,
"iat": 1719500400,
"clickhouse:grants": ["SELECT ON analytics.*", "INSERT ON analytics.events"],
"clickhouse:roles": ["analyst"]
}
JWT 用户与常规 ClickHouse 用户在几个重要方面有所不同。
每个 JWT 用户都会获得一个根据 iss、sub 和 aud claims 计算出的确定性 UUID。该 UUID 在不同登录之间是稳定的。同一用户即使用不同的令牌多次登录 (只要签发方、主体和受众相同) ,获得的 UUID 也始终相同。
不过,用户名是易变的。其构造方式如下:
JWT::<issuer>::<audience>::<subject>::<claims_hash>
<claims_hash> 部分会在 clickhouse:roles 或 clickhouse:grants 声明发生变化时相应变化。这意味着,即使是同一身份,具有不同角色或授权集合的令牌也会生成不同的用户名。
有效访问权限按如下方式计算:
effective_rights = permission_limit ∩ (token_grants ∪ token_roles)
其中,permission_limit 是作为上限参考而配置的角色或用户所拥有的访问权限集合。令牌请求的任何超出该上限的权限都会被静默丢弃。
ClickHouse 会跟踪每个稳定身份最近一次通过身份验证的令牌的 iat (签发时间) 声明。如果提供的令牌其 iat 等于或早于已存储的值,服务器会复用现有的临时用户,而不会重新评估声明。这样可以防止旧令牌降低用户权限。
临时用户会在令牌首次通过身份验证时创建,并在 valid_until (由 exp 推导得出) 到期后,由后台垃圾回收任务删除。GC 间隔由 gc_interval 参数控制 (默认值:5 分钟) 。
在两次 GC 运行之间,已过期的用户可能仍会显示在 system.users 中,但已无法再进行身份验证。
由于 UUID 是固定的,你可以使用 SQL 语句将 profile、配额、行策略和列脱敏策略分配给 JWT 用户。这些分配会持久保存在 access control 存储中 (磁盘上或 ZooKeeper 中) ,并且在令牌过期和重新身份验证后依然有效。
通过用户当前的用户名引用该用户:
ALTER SETTINGS PROFILE my_profile ADD TO 'JWT::ClickHouse::my-service-id::jane.doe::<claims-hash>';
对于给定的 identity,在用户处于活跃状态时,可在 system.users 的 name 和 id 列中找到其用户名和 UUID。
请注意,ALTER USER 不能直接用于 JWT 用户,因为它们是只读的。要分配 settings profile、配额或策略,请使用上文所示的 ALTER SETTINGS PROFILE、ALTER QUOTA 或 ALTER ROW POLICY 语句。
| Feature | JWT 用户 | 普通用户 |
|---|
| 创建 | 根据标记声明自动创建 | CREATE USER 语句 |
| 存储 | 仅驻留于内存中 (临时) | 磁盘、ZooKeeper 或配置文件 |
CREATE USER ... IDENTIFIED WITH jwt | 不支持 (会抛出异常) | 支持所有其他认证类型 |
ALTER USER / DROP USER | 不支持 | 支持 |
| 备份和恢复 | 不包含 | 包含 |
| 用户名 | 自动生成、可变 | 由管理员指定、固定 |
| UUID | 根据 iss+sub+aud 确定性生成 | 创建时随机生成 |
| 生命周期 | 受标记 exp 限定 | 直到被显式删除 |
| 访问权限 | 从标记声明派生,并受权限上限约束 | 通过 GRANT 显式授予 |
| 主机限制 | 按提供商网络配置 | 按用户 HOST 子句设置 |
| 设置 profile | 可按 UUID 分配 (持久) | 可直接配置 |
| 配额和行策略 | 可按 UUID 分配 (持久) | 可直接配置 |
| 默认角色 | 不可配置 | 可配置 |
当临时 JWT 用户使用 SQL SECURITY DEFINER 创建视图时,服务器会自动为该用户创建一个持久的影子副本,作为该视图的定义者。该影子用户:
- 名称为
<original_jwt_username>:definer
- 具有
NO_AUTHENTICATION (不能用于登录)
- 保留创建视图时原始 JWT 用户拥有的相同访问权限
这样可以确保在临时用户的令牌过期、原始用户被垃圾回收机制清理后,视图仍能继续正常工作。
使用 clickhouse-client 的 --jwt 选项,通过预先获取的令牌进行身份验证:
clickhouse-client --host your-instance.clickhouse.cloud --secure --jwt '<your_jwt_token>'
--jwt 标志与 --user 互斥。指定 --jwt 时,用户名将从令牌中获取。
在 Authorization 请求头中以 Bearer 令牌的形式发送该令牌:
curl -H 'Authorization: Bearer <your_jwt_token>' \
'https://your-instance.clickhouse.cloud:8443/?query=SELECT+currentUser()'
始终通过 HTTPS 发送 JWT。通过明文 HTTP 发送的 Bearer 令牌会暴露给网络路径上的任何人,这等同于泄露凭证。
clickhouse-client 支持通过 --login 标志使用交互式 OAuth2 设备代码流程。对于 ClickHouse Cloud 端点,客户端会自动执行令牌交换,以获取 ClickHouse 专用 JWT。令牌会在会话期间自动刷新,整个过程对用户透明。获取到新令牌后,客户端会自动重新连接。
clickhouse-client --host your-instance.clickhouse.cloud --login
ClickHouse Cloud 内置 JWT 身份验证器
每个 ClickHouse Cloud 服务都带有一个预定义的 JWT 身份验证器,供 SQL 控制台和 clickhouse-client 的 --login 流程使用。该身份验证器配置如下:
| 参数 | 值 |
|---|
iss (签发方) | ClickHouse |
aud (受众) | 服务 UUID (可在 Cloud 控制台 URL 中看到) |
sub (主体) | 你的 ClickHouse Cloud 账户邮箱地址 |
此内置身份验证器的权限上限设置为 default_role 角色和 default 用户。这意味着,任何 JWT 用户的实际权限都会与这两个实体所拥有的授权取交集,因此令牌的权限绝不会提升到超出 default_role 和 default 允许范围的级别。
使用此身份验证器无需进行任何配置。服务创建时会自动为其预配。
当查询被转发到另一分片或副本时,JWT 令牌会包含在服务器间协议中。远程节点会独立重新验证该令牌,并创建自己的临时用户。
- 未授予访问权限: 引用的角色或用户可能缺少所需的授权。请确保
clickhouse:roles 中引用的角色存在,并且已授予相应权限。
- 令牌被拒绝: 请验证令牌中的
iss、aud 以及签名算法是否与 JWT 提供商的要求一致。如果使用 JWKS,请确保令牌的 kid 与提供商密钥集中的某个密钥匹配。
- 用户在两次查询之间消失: 临时用户会在令牌过期后被移除。对于长时间运行的会话,请使用支持令牌刷新的客户端 (例如
--login 模式) 。
CREATE USER ... IDENTIFIED WITH jwt 失败: 这是预期行为。JWT 用户无法通过 DDL 创建。它们完全由令牌生命周期管理。