使用 Java 调用 JumpServer PAM 账号密钥查询 API:集成应用鉴权与 HMAC-SHA256 签名实战
使用 Java 调用 JumpServer PAM 账号密钥查询 API集成应用鉴权与 HMAC-SHA256 签名实战【免费下载链接】jumpserverJumpServer is an open-source Privileged Access Management (PAM) platform that provides DevOps and IT teams with on-demand and secure access to SSH, RDP, Kubernetes, Database and RemoteApp endpoints through a web browser.项目地址: https://gitcode.com/GitHub_Trending/ju/jumpserver导读本文面向需要将 JumpServer 的资产凭据能力集成进自有系统的 Java 开发者围绕GET /api/v1/accounts/integration-applications/account-secret/接口完整讲解如何在 Java 11 环境下使用标准库HttpClient构造带 HMAC-SHA256 签名的 RESTful 请求实时查询 PAM 中指定资产的账号密码。读完本文你将掌握集成应用的创建与密钥获取、请求签名串的构造规则、以及对应的服务端校验与审计逻辑可直接复制示例代码落地运行。1. 接口能力概述JumpServer 作为开源特权访问管理PAM平台将资产Asset与账号Account的凭据统一托管。account-secret接口是 JumpServer 提供给第三方业务系统即集成应用的凭据查询通道调用方以资产名称 账号名称作为查询条件服务端在校验签名与权限后返回该账号的明文密钥响应体为标准的 JSON 格式。该接口具备三个显著特点RESTful 风格使用GET请求参数通过 URL Query String 传递签名鉴权调用方必须携带基于 HMAC-SHA256 的Authorization: Signature头服务端据此确认调用方身份全程审计每次成功的查询都会写入集成应用访问日志便于事后追溯。官方同时在仓库的apps/accounts/demos目录下提供了 Java、Go、Node.js、Python、curl 五种语言/工具的实现示例本文聚焦 Java 实现其余语言可对照参考。2. 环境要求与前置准备2.1 运行环境依赖版本要求说明Java11需使用java.net.http.HttpClient该 API 自 Java 11 起成为标准库HttpClientJDK 内置java.net.http包无需引入第三方 HTTP 库构建工具任意示例为单文件可直接用javac编译运行示例使用 Java 标准库完成 URL 编码、时间戳格式化、HMAC-SHA256 签名与 HTTP 请求全程零第三方依赖便于直接移植。2.2 获取 API KeyKEY_ID / KEY_SECRET在发起调用之前需要先在 JumpServer 管理端创建集成应用以获取一对凭证进入PAM - 应用管理即集成应用管理界面创建应用填写名称并关联允许访问的账号创建成功后系统自动生成KEY_ID应用 IDUUID 格式与KEY_SECRET密钥36 位随机字符串。从源码看KEY_SECRET 由模型层的refresh_secret()方法生成self.secret random_string(36)即 36 位随机字符串见 apps/accounts/models/application.py创建应用时序列化器会在create中自动调用该方法完成密钥初始化见 apps/accounts/serializers/account/service.py。注意KEY_SECRET 属于敏感凭据仅在创建时展示请妥善保管如泄露可在应用详情中执行刷新密钥操作使其失效重建对应服务端refresh-secret接口。2.3 准备组织 IDORG_IDJumpServer 是多组织架构签名串中需要携带X-JMS-ORG请求头指定组织。默认组织 ID 为00000000-0000-0000-0000-000000000002实际使用时请替换为目标组织的 UUID。3. 接口定义与参数说明3.1 请求方式GET /api/v1/accounts/integration-applications/account-secret/该路由在 apps/accounts/urls.py 中注册router.register(rintegration-applications, api.IntegrationApplicationViewSet, integration-apps)对应视图集为IntegrationApplicationViewSet下的get_account_secret动作见 apps/accounts/api/account/application.py。3.2 请求参数原文档定义的必填参数如下参数名类型必填说明assetstr是资产名称accountstr是账号名称结合服务端序列化器 apps/accounts/serializers/account/service.py 的实现实际支持四个查询参数且校验规则更灵活参数名类型必填说明assetstr条件必填资产名称asset_idUUID条件必填资产 IDaccountstr条件必填账号名称account_idUUID条件必填账号 ID校验逻辑要点IntegrationAccountSecretSerializer.validate若提供了account_id则直接通过校验否则要求asset与asset_id至少提供其一、account与account_id至少提供其一否则返回 400 错误并提示At least one of the following fields must be provided未匹配到账号时服务端返回Not found错误JMSException。查询时服务端以asset匹配Asset.name、以account匹配Account.name并且只会在该集成应用已关联的账号范围内查找通过RelatedManager.get_to_filter_qs过滤见 apps/accounts/models/application.py。因此调用方需要确保目标账号已添加到集成应用的账号列表中。3.3 响应示例{ id: 72b0b0aa-ad82-4182-a631-ae4865e8ae0e, secret: 123456 }字段说明id调用方集成应用的 ID即 KEY_IDsecret目标账号的明文密钥。需要注意服务端会受全局安全配置SECURITY_DISABLE_VIEW_SECRET控制当该配置为True时出于安全考虑即使鉴权通过也不会返回真实密码secret字段为null见 apps/accounts/api/account/application.py。该配置项默认值为False定义于 apps/jumpserver/conf.py可在系统设置中调整。4. Java 客户端实现详解本节逐段拆解官方示例 apps/accounts/demos/java/demo.java 的完整实现帮助你理解签名机制的每一个环节。4.1 配置项与环境变量示例允许通过环境变量注入四个关键配置未设置时使用内置默认值便于本地联调环境变量默认值作用API_URLhttp://127.0.0.1:8080JumpServer 服务地址API_KEY_ID72b0b0aa-...-ae4865e8ae0e集成应用 IDAPI_KEY_SECRET6fuSO7P1m4cj8SSlgaYdblOjNAmnxDVD7tr8集成应用密钥ORG_ID00000000-0000-0000-0000-000000000002组织 IDprivate static final String API_URL System.getenv().getOrDefault(API_URL, http://127.0.0.1:8080); private static final String KEY_ID System.getenv().getOrDefault(API_KEY_ID, 72b0b0aa-ad82-4182-a631-ae4865e8ae0e); private static final String KEY_SECRET System.getenv().getOrDefault(API_KEY_SECRET, 6fuSO7P1m4cj8SSlgaYdblOjNAmnxDVD7tr8); private static final String ORG_ID System.getenv().getOrDefault(ORG_ID, 00000000-0000-0000-0000-000000000002);生产环境务必通过环境变量传入真实值避免将密钥硬编码。4.2 拼接查询串与完整 URL参数使用URLEncoder.encode进行 UTF-8 百分号编码防止资产名/账号名中的特殊字符破坏 URLString queryString asset URLEncoder.encode(asset, StandardCharsets.UTF_8) account URLEncoder.encode(account, StandardCharsets.UTF_8); String url API_URL /api/v1/accounts/integration-applications/account-secret/? queryString;4.3 生成 RFC 1123 时间戳请求需要携带Date头采用 RFC 1123 格式如Tue, 09 Sep 2026 02:14:42 GMTString date ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME);4.4 构造签名串signing string签名串由多个header名: 值行拼接而成核心是(request-target)——即小写的 HTTP 方法 空格 带查询参数的完整路径String requestTarget get /api/v1/accounts/integration-applications/account-secret/? queryString; String signingString (request-target): requestTarget \n accept: application/json\n date: date \n x-jms-org: ORG_ID;签名串共四行覆盖了请求方法、路径、查询参数以及三个关键请求头确保请求的任何部分被篡改都会导致签名校验失败(request-target): get /api/v1/accounts/integration-applications/account-secret/?asset...account... accept: application/json date: RFC 1123 时间 x-jms-org: 组织 ID4.5 计算 HMAC-SHA256 签名以 KEY_SECRET 为密钥、对上述签名串做 HMAC-SHA256 计算结果做 Base64 编码private String sign(String data, String key) throws Exception { Mac mac Mac.getInstance(HmacSHA256); SecretKeySpec secretKeySpec new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), HmacSHA256); mac.init(secretKeySpec); byte[] rawHmac mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(rawHmac); }4.6 组装请求头与 Authorization请求共携带五个请求头请求头值说明Acceptapplication/json声明接受 JSON 响应DateRFC 1123 时间戳与签名串中的 date 一致X-JMS-ORG组织 ID与签名串中的 x-jms-org 一致X-Sourcejms-pam标识调用来源AuthorizationSignature keyId...,algorithmhmac-sha256,headers(request-target) accept date x-jms-org,signature...签名鉴权头Authorization 头遵循 HTTP Signature 规范格式其中keyId集成应用 IDKEY_IDalgorithm固定为hmac-sha256headers参与签名的请求头列表与签名串的构造顺序对应signature第 4.5 节计算出的 Base64 签名。HttpRequest request HttpRequest.newBuilder() .uri(URI.create(url)) .header(Accept, application/json) .header(Date, date) .header(X-JMS-ORG, ORG_ID) .header(X-Source, jms-pam) .header(Authorization, Signature keyId\ KEY_ID \,algorithm\hmac-sha256\,headers\(request-target) accept date x-jms-org\,signature\ signature \) .build();4.7 发送请求与错误处理使用同步发送方式响应体按字符串读取状态码 200 时返回响应体否则打印错误码HttpResponseString response httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() 200) { return response.body(); } else { System.err.println(API request failed: response.statusCode()); return null; }5. 完整可运行代码清单以下为官方示例的完整代码apps/accounts/demos/java/demo.java可直接保存为Demo.java编译运行import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.net.URLEncoder; public class Demo { private static final String API_URL System.getenv().getOrDefault(API_URL, http://127.0.0.1:8080); private static final String KEY_ID System.getenv().getOrDefault(API_KEY_ID, 72b0b0aa-ad82-4182-a631-ae4865e8ae0e); private static final String KEY_SECRET System.getenv().getOrDefault(API_KEY_SECRET, 6fuSO7P1m4cj8SSlgaYdblOjNAmnxDVD7tr8); private static final String ORG_ID System.getenv().getOrDefault(ORG_ID, 00000000-0000-0000-0000-000000000002); public static void main(String[] args) throws Exception { APIClient client new APIClient(); String result client.getAccountSecret(ubuntu_docker, root); System.out.println(result); } static class APIClient { private final HttpClient httpClient HttpClient.newHttpClient(); public String getAccountSecret(String asset, String account) throws Exception { // Encode URL parameters String queryString asset URLEncoder.encode(asset, StandardCharsets.UTF_8) account URLEncoder.encode(account, StandardCharsets.UTF_8); // Complete URL with parameters String url API_URL /api/v1/accounts/integration-applications/account-secret/? queryString; // Get the current UTC time String date ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME); // Build (request-target), including query parameters String requestTarget get /api/v1/accounts/integration-applications/account-secret/? queryString; // Generate the signing string String signingString (request-target): requestTarget \n accept: application/json\n date: date \n x-jms-org: ORG_ID; String signature sign(signingString, KEY_SECRET); // Build the HTTP request HttpRequest request HttpRequest.newBuilder() .uri(URI.create(url)) .header(Accept, application/json) .header(Date, date) .header(X-JMS-ORG, ORG_ID) .header(X-Source, jms-pam) .header(Authorization, Signature keyId\ KEY_ID \,algorithm\hmac-sha256\,headers\(request-target) accept date x-jms-org\,signature\ signature \) .build(); // Send the request HttpResponseString response httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() 200) { return response.body(); } else { System.err.println(API request failed: response.statusCode()); return null; } } // Calculate the HMAC-SHA256 signature private String sign(String data, String key) throws Exception { Mac mac Mac.getInstance(HmacSHA256); SecretKeySpec secretKeySpec new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), HmacSHA256); mac.init(secretKeySpec); byte[] rawHmac mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(rawHmac); } } }运行方式# 方式一使用环境变量注入真实配置 API_URLhttps://your-jumpserver.example.com \ API_KEY_ID你的KEY_ID \ API_KEY_SECRET你的KEY_SECRET \ ORG_ID你的组织ID \ javac Demo.java java Demo # 方式二本地联调直接运行使用示例默认值 javac Demo.java java Demomain 方法中示例查询的是资产ubuntu_docker上的账号root请按实际资产/账号名称修改。预期输出为包含id与secret字段的 JSON 字符串。6. 服务端处理流程与安全机制理解服务端实现有助于排查调用问题。get_account_secret动作的完整处理链路如下见 apps/accounts/api/account/application.py参数校验序列化器IntegrationAccountSecretSerializer校验 query 参数非法时直接返回 400账号定位调用request.user.get_account(...)即集成应用对象的get_account()方法按资产名/账号名或 ID过滤且限定在该应用关联的账号范围内审计记录命中账号后向IntegrationApplicationLog写入一条访问日志记录来源 IP、服务名称、账号含用户名与资产含地址实现凭据访问的全程可追溯敏感信息策略根据SECURITY_DISABLE_VIEW_SECRET全局配置决定是否返回真实密钥权限控制该动作要求accounts.view_integrationapplication权限对应视图集中的rbac_perms配置。此外集成应用模型apps/accounts/models/application.py还包含is_active启停开关、ip_group来源 IP 白名单等属性——当应用被停用或来源 IP 不在白名单内时请求同样会被拒绝这也是排查鉴权通过但请求失败时需要检查的维度。7. 其他语言实现参考JumpServer 为同一接口提供了多语言示例便于跨技术栈团队对照移植签名逻辑完全一致仅 HTTP 库与字符串处理语法不同curl 示例最精简的签名流程演示适合调试与脚本化Go 示例 及封装库 jms_pam.goNode.js 示例Python 示例 及封装库 jms_pam/main.py。在 JumpServer 的应用管理详情页中还可通过 SDK 信息接口按语言直接拉取对应 README 与示例代码服务端实现在get_sdks_info动作中按language参数读取 apps/accounts/demos 目录下的文件。8. 常见问题FAQQ: API Key 如何获取A: 在 JumpServer 的PAM - 应用管理中创建应用即可生成 KEY_ID 与 KEY_SECRET 一对凭证创建后请及时将密钥保存到安全的配置中心或环境变量中。Q: 返回结果中secret为 null 是什么原因A: 通常是系统开启了SECURITY_DISABLE_VIEW_SECRET安全配置默认为False该配置开启后所有凭据查询接口都不会返回真实密码。Q: 提示Account not found怎么排查A: 确认资产名称、账号名称拼写正确并检查目标账号是否已添加到该集成应用的账号关联列表中同时确认请求头中的组织 ID 与账号所属组织一致。Q: 鉴权失败的常见原因有哪些A: 主要包括KEY_SECRET 与创建时不一致可能被刷新过、签名串与请求头不一致Date、X-JMS-ORG 或查询参数被改动、服务端时间与调用方时间偏差过大导致日期校验失败、应用被停用或来源 IP 不在白名单内。9. 版本历史版本变更内容日期1.0.0初始版本2025-02-11示例代码由 JumpServer 团队随集成应用功能同步维护Java 版本使用 JDK 内置 HttpClient 实现无第三方依赖可持续跟进仓库 apps/accounts/demos/java 目录获取最新更新。【免费下载链接】jumpserverJumpServer is an open-source Privileged Access Management (PAM) platform that provides DevOps and IT teams with on-demand and secure access to SSH, RDP, Kubernetes, Database and RemoteApp endpoints through a web browser.项目地址: https://gitcode.com/GitHub_Trending/ju/jumpserver创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考