- 数据工程
- 数据湖
- 大数据
- 对象存储
- 后端
【免费下载链接】lakeFS
lakeFS - Data version control for your data lake | Git for data
本文以 lakeFS 仓库中由 OpenAPI Generator 自动生成的 CredentialsWithSecret.md 模型文档为核心,结合服务端 Go 实现与 Java SDK 源码,系统讲解 lakeFS 访问凭证(Access Key / Secret Key)的完整生命周期:该模型如何在创建凭证时返回明文 Secret、各字段的语义与 JSON 映射、Java 客户端调用方式,以及服务端如何生成、加密与存储这些凭证。读完本文,你将掌握 lakeFS 认证体系中"创建凭证 → 获取一次性明文 Secret → 客户端鉴权使用"这条完整链路,并能正确、安全地在 Java 项目中集成 lakeFS 凭证管理。
一、CredentialsWithSecret 是什么
CredentialsWithSecret是 lakeFS HTTP API 中定义的一个响应模型,用于表示创建凭证接口返回的完整凭证信息。与普通的Credentials模型不同,它额外携带了secret_access_key(密钥),因此命名为 "WithSecret"——即"带密钥的凭证"。
在 lakeFS 中,凭证由一对密钥组成:
- Access Key ID(访问密钥 ID):对外公开的标识符,用于标识调用者身份;
- Secret Access Key(密钥):与 Access Key ID 配套的机密字符串,用于签名请求、验证身份,必须严格保密。
CredentialsWithSecret就是这一对密钥加上创建时间戳的组合。它典型出现在两个场景的响应中:
- 为用户创建新凭证:
POST /auth/users/{userId}/credentials,对应 Java SDK 的AuthApi.createCredentials; - 初始化 lakeFS 安装(Setup):
POST /setup_lakefs创建初始管理员用户时返回。
二、字段语义详解
原文档 CredentialsWithSecret.md 给出了该模型的完整属性表,本节在此基础上逐字段展开,并结合 api/swagger.yml 中的 OpenAPI 定义补充类型、必填约束与语义。
| 属性(Java 侧) | JSON 字段 | 类型 | 说明 | 必填 |
|---|---|---|---|---|
accessKeyId | access_key_id | String | 访问密钥 ID,凭证的公开标识符 | 是 |
secretAccessKey | secret_access_key | String | 密钥(Secret),仅在此响应中明文返回一次 | 是 |
creationDate | creation_date | Long(int64) | 凭证创建时间,Unix Epoch 秒数(Unix Epoch in seconds) | 是 |
2.1 accessKeyId(访问密钥 ID)
用于标识凭证持有者的公开字符串。服务端生成规则位于 pkg/auth/keys/keys.go:
func GenAccessKeyID() string { const accessKeyLength = 14 key := KeyGenerator(accessKeyLength) return fmt.Sprintf("%s%s%s", "AKIAJ", key, "Q") }生成的 Access Key ID 采用类似 AWS 的AKIAJ前缀风格,长度为 20 字符,中间 14 个字符取自AKIA字母表(ABCDEFGHIJKLMNOPQRSTUVWXYZ234567),整体形态形如AKIAJXXXXXXXXXXXXQ。从源码注释可知,这一字母表刻意排除了0和1等易混淆字符。
2.2 secretAccessKey(密钥)
与 Access Key ID 配对的机密字符串。生成规则同样位于 pkg/auth/keys/keys.go:
func GenSecretAccessKey() string { const secretKeyLength = 30 return Base64StringGenerator(secretKeyLength) }即对 30 字节的密码学安全随机数(crypto/rand)做 Base64 编码,最终得到约 40 字符的随机密钥字符串。Secret 仅在创建凭证的响应中出现一次,之后服务端只保存其加密形式,无法再以明文查询——这是理解CredentialsWithSecret存在意义的关键(详见下文"安全注意事项")。
2.3 creationDate(创建时间)
凭证的签发时间,类型为Long,单位为秒的 Unix 时间戳。服务端在写入凭证时以time.Now()记录签发时间,见 pkg/auth/basic_service.go;响应中通过credentials.IssuedDate.Unix()转换为秒级时间戳输出,见 pkg/api/controller.go。
2.4 必填约束
OpenAPI 定义(api/swagger.yml)将access_key_id、secret_access_key、creation_date三个字段全部标记为required。这意味着:
- 服务端在序列化响应时,三个字段一定存在;
- 客户端反序列化时,如果响应缺少任一字段,将抛出异常(详见 CredentialsWithSecret.java 中的
validateJsonElement校验逻辑)。
三、Java SDK 中的模型实现与 JSON 映射
3.1 类结构与序列化注解
Java SDK 中的对应类位于 clients/java/src/main/java/io/lakefs/clients/sdk/model/CredentialsWithSecret.java,由 OpenAPI Generator 基于api/swagger.yml自动生成,使用 Gson 完成序列化。类的开头即声明了三个字段与 JSON 键名的映射:
public static final String SERIALIZED_NAME_ACCESS_KEY_ID = "access_key_id"; @SerializedName(SERIALIZED_NAME_ACCESS_KEY_ID) private String accessKeyId; public static final String SERIALIZED_NAME_SECRET_ACCESS_KEY = "secret_access_key"; @SerializedName(SERIALIZED_NAME_SECRET_ACCESS_KEY) private String secretAccessKey; public static final String SERIALIZED_NAME_CREATION_DATE = "creation_date"; @SerializedName(SERIALIZED_NAME_CREATION_DATE) private Long creationDate;注意 Java 侧采用驼峰命名(accessKeyId),而 JSON 侧采用下划线命名(access_key_id),二者通过@SerializedName建立映射,这是 OpenAPI 生成客户端中的常见约定。
3.2 方法能力
该类提供了一组完整的能力,便于开发与调试:
- 链式构造器(setter 返回自身):
accessKeyId(String)、secretAccessKey(String)、creationDate(Long),支持流式构建; - Getter:
getAccessKeyId()、getSecretAccessKey()、getCreationDate(),三个字段均标注@Nonnull,与 OpenAPI 的 required 约束一致; - 附加属性支持:
putAdditionalProperty(String, Object)与getAdditionalProperties()用于容纳服务端未来新增、当前 SDK 版本尚未声明的 JSON 字段,保证前后端兼容(CredentialsWithSecret.java); - JSON 工具方法:静态方法
fromJson(String)可从 JSON 字符串反序列化,实例方法toJson()可序列化输出(CredentialsWithSecret.java)。
一个典型的手动反序列化示例:
import io.lakefs.clients.sdk.model.CredentialsWithSecret; String json = "{\"access_key_id\":\"AKIAJXXXXXXXXXXXXQ\",\"secret_access_key\":\"...\",\"creation_date\":1700000000}"; CredentialsWithSecret creds = CredentialsWithSecret.fromJson(json); System.out.println(creds.getAccessKeyId()); System.out.println(creds.getSecretAccessKey()); System.out.println(creds.getCreationDate());四、在哪些 API 响应中出现
4.1 为用户创建凭证(AuthApi.createCredentials)
CredentialsWithSecret最核心的使用场景是创建凭证接口。在 AuthApi.md 中可以看到该接口的完整契约:
POST /auth/users/{userId}/credentials请求无需请求体,路径参数为userId;成功时返回 HTTP201,响应体即为CredentialsWithSecret。Java SDK 对应方法位于 clients/java/src/main/java/io/lakefs/clients/sdk/AuthApi.java,其调用签名与使用方式:
CredentialsWithSecret result = apiInstance.createCredentials(userId) .execute();服务端实现位于 pkg/api/controller.go。该处理器首先执行权限校验——调用者必须拥有对目标用户 ARN 的CreateCredentialsAction权限,随后调用Auth.CreateCredentials生成凭证,最后组装并返回CredentialsWithSecret:
response := apigen.CredentialsWithSecret{ AccessKeyId: credentials.AccessKeyID, SecretAccessKey: credentials.SecretAccessKey, CreationDate: credentials.IssuedDate.Unix(), } writeResponse(w, r, http.StatusCreated, response)4.2 初始化安装(Setup)
CredentialsWithSecret还出现在 lakeFS 首次初始化时创建初始管理员用户的响应中。调用POST /setup_lakefs且未显式提供密钥时,服务端会生成随机凭证并返回,实现见 pkg/api/controller.go:
response := apigen.CredentialsWithSecret{ AccessKeyId: cred.AccessKeyID, SecretAccessKey: cred.SecretAccessKey, CreationDate: cred.IssuedDate.Unix(), } writeResponse(w, r, http.StatusOK, response)若在 Setup 请求体中携带了自定义密钥(body.Key),则使用CreateInitialAdminUserWithKeys以指定密钥创建管理员(pkg/auth/setup/setup.go)。
五、完整 Java 调用示例
以下示例完整展示了如何在 Java 项目中配置认证并调用createCredentials,代码取自 AuthApi.md 并补充了关键注释:
import io.lakefs.clients.sdk.ApiClient; import io.lakefs.clients.sdk.ApiException; import io.lakefs.clients.sdk.Configuration; import io.lakefs.clients.sdk.auth.*; import io.lakefs.clients.sdk.model.CredentialsWithSecret; import io.lakefs.clients.sdk.AuthApi; public class CreateCredentialsExample { public static void main(String[] args) { ApiClient defaultClient = Configuration.getDefaultApiClient(); defaultClient.setBasePath("http://lakefs.example.com/api/v1"); // 方式一:HTTP Basic 认证(用户名 + 密码) HttpBasicAuth basic_auth = (HttpBasicAuth) defaultClient.getAuthentication("basic_auth"); basic_auth.setUsername("YOUR ADMIN USERNAME"); basic_auth.setPassword("YOUR ADMIN PASSWORD"); // 方式二:JWT Bearer Token HttpBearerAuth jwt_token = (HttpBearerAuth) defaultClient.getAuthentication("jwt_token"); jwt_token.setBearerToken("BEARER TOKEN"); AuthApi apiInstance = new AuthApi(defaultClient); String userId = "user_example"; // 为目标用户创建凭证 try { CredentialsWithSecret result = apiInstance.createCredentials(userId) .execute(); // 拿到一次性明文密钥 System.out.println("Access Key ID: " + result.getAccessKeyId()); System.out.println("Secret Access Key: " + result.getSecretAccessKey()); System.out.println("Creation Date (epoch seconds): " + result.getCreationDate()); } catch (ApiException e) { System.err.println("Exception when calling AuthApi#createCredentials"); System.err.println("Status code: " + e.getCode()); System.err.println("Reason: " + e.getResponseBody()); System.err.println("Response headers: " + e.getResponseHeaders()); e.printStackTrace(); } } }该接口的认证方式支持basic_auth、cookie_auth、jwt_token三种(见 AuthApi.java),响应头要求Accept: application/json。可能的错误状态码包括400(Bad Request)、401(Unauthorized)、404(用户不存在)、429(请求过于频繁)等。
六、服务端实现原理:从生成到加密存储
要深入理解CredentialsWithSecret,需要看清服务端凭证的完整处理流程。核心逻辑位于 pkg/auth/basic_service.go:
- 生成密钥对:
GenAccessKeyID()生成 Access Key ID,GenSecretAccessKey()生成明文 Secret; - 校验用户存在:
GetUser确认目标用户存在,不存在则返回错误; - 配额校验:
AddCredentials会检查该用户已有凭证数量,超过MaxCredentialsPerUser上限时拒绝创建(pkg/auth/basic_service.go); - 加密存储:调用
model.EncryptSecret使用SecretStore对明文 Secret 加密(pkg/auth/model/model.go),数据库中仅保存加密后的字节SecretAccessKeyEncryptedBytes(见 pkg/auth/model/model.go 中BaseCredential的结构:SecretAccessKey字段标注json:"-",明文不参与任何持久化与 JSON 输出); - 返回明文一次:
CreateCredentials处理器将明文SecretAccessKey组装进CredentialsWithSecret响应,这是明文 Secret 唯一一次出现在 API 输出中。
服务端对应的 Go 结构体定义在 pkg/api/apigen/lakefs.gen.go,与 Java 模型一一对应:
// CredentialsWithSecret defines model for CredentialsWithSecret. type CredentialsWithSecret struct { AccessKeyId string `json:"access_key_id"` // Unix Epoch in seconds CreationDate int64 `json:"creation_date"` SecretAccessKey string `json:"secret_access_key"` }七、安全注意事项
由于CredentialsWithSecret携带明文密钥,使用时有几条必须遵守的安全准则:
- 一次性获取:Secret 只在创建凭证的响应中明文返回,之后任何查询接口(如
GetCredentials、ListUserCredentials)返回的都是不含 Secret 的Credentials模型。如果丢失了 Secret,只能删除该凭证并重新创建,无法找回。 - 妥善保管:拿到
CredentialsWithSecret后应立即持久化到安全的密钥管理设施(如环境变量、密钥管理系统),避免写入日志、代码仓库或前端。 - 服务端只存密文:服务端以加密形式(
SecretAccessKeyEncryptedBytes)落库,密钥解密依赖配置的SecretStore,降低了数据库泄露导致密钥暴露的风险。 - 最小权限:创建凭证本身是敏感操作,服务端要求调用方持有
CreateCredentialsAction权限(pkg/api/controller.go),生产环境应将此类操作限制在管理员账号。
八、与相关模型的区分
在 clients/java/docs 目录下,还有两个与凭证相关的模型,容易与CredentialsWithSecret混淆:
| 模型 | 方向 | 关键字段 | 典型场景 |
|---|---|---|---|
CredentialsWithSecret | 响应(出参) | access_key_id、secret_access_key、creation_date | 创建凭证、初始化管理员时的返回 |
Credentials | 响应(出参) | access_key_id、creation_date | 列出 / 查询凭证(不含 Secret),见 Credentials.md |
AccessKeyCredentials | 请求(入参) | access_key_id、secret_access_key | Setup 时指定初始管理员的自定义密钥,见 AccessKeyCredentials.md |
区分要点:带 Secret 的模型只作为创建成功后的响应出现,且只出现一次;日常查询用Credentials;需要自定义密钥初始化时用AccessKeyCredentials作为输入。
九、小结
CredentialsWithSecret是 lakeFS 认证体系中一个短小但关键的模型:它定义了凭证创建场景下"Access Key ID + 明文 Secret + 创建时间"的响应契约。通过本文可以掌握:
- 三个字段的类型、JSON 键名与必填约束(api/swagger.yml);
- Java SDK 中对应的类、序列化映射与工具方法(CredentialsWithSecret.java);
- 它在
createCredentials与setup_lakefs两个接口中的使用方式; - 服务端生成随机密钥、加密存储、一次性返回明文的完整链路(basic_service.go、keys.go)。
理解这个模型,是安全使用 lakeFS Java SDK 管理用户凭证、构建自动化访问控制流程的基础。
- 数据工程
- 数据湖
- 大数据
- 对象存储
- 后端
【免费下载链接】lakeFS
lakeFS - Data version control for your data lake | Git for data
相关推荐
lakeFS Java SDK 的 Credentials 模型:字段定义、JSON 映射与凭据生命周期
lakeFS Java SDK 的 Credentials 模型:字段定义、JSON 映射与凭据生命周期 导读 本文面向使用 lakeFS 官方 Java SD
数据工程数据湖大数据对象存储后端lakeFS Java SDK 认证令牌 AuthenticationToken 详解:JWT 字段结构、登录流程与底层实现
lakeFS Java SDK 认证令牌 AuthenticationToken 详解:JWT 字段结构、登录流程与底层实现 导读 Authentication
数据工程数据湖大数据对象存储后端lakeFS Java SDK AuthApi 完整指南:用户、组、策略、凭证与外部主体的认证授权管理
lakeFS Java SDK AuthApi 完整指南:用户、组、策略、凭证与外部主体的认证授权管理 本篇技术指南以 lakeFS 官方 Java SDK(
数据工程数据湖大数据对象存储后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考