news 2026/9/10 20:43:33

使用 Java 调用 JumpServer PAM 账号密钥查询 API:集成应用鉴权与 HMAC-SHA256 签名实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 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 Key(KEY_ID / KEY_SECRET)

在发起调用之前,需要先在 JumpServer 管理端创建集成应用以获取一对凭证:

  1. 进入PAM - 应用管理(即集成应用管理界面);
  2. 创建应用,填写名称并关联允许访问的账号;
  3. 创建成功后,系统自动生成KEY_ID(应用 ID,UUID 格式)与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 准备组织 ID(ORG_ID)

JumpServer 是多组织架构,签名串中需要携带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(r'integration-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条件必填资产 ID
accountstr条件必填账号名称
account_idUUID条件必填账号 ID

校验逻辑要点(IntegrationAccountSecretSerializer.validate):

  • 若提供了account_id,则直接通过校验;
  • 否则要求assetasset_id至少提供其一、accountaccount_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_ID;
  • secret:目标账号的明文密钥。

需要注意,服务端会受全局安全配置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集成应用 ID
API_KEY_SECRET6fuSO7P1m4cj8SSlgaYdblOjNAmnxDVD7tr8集成应用密钥
ORG_ID00000000-0000-0000-0000-000000000002组织 ID
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");

生产环境务必通过环境变量传入真实值,避免将密钥硬编码。

4.2 拼接查询串与完整 URL

参数使用URLEncoder.encode进行 UTF-8 百分号编码,防止资产名/账号名中的特殊字符破坏 URL:

String 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 GMT):

String 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: <组织 ID>

4.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="...",algorithm="hmac-sha256",headers="(request-target) accept date x-jms-org",signature="..."签名鉴权头

Authorization 头遵循 HTTP Signature 规范格式,其中:

  • keyId:集成应用 ID(KEY_ID);
  • algorithm:固定为hmac-sha256
  • headers:参与签名的请求头列表(与签名串的构造顺序对应);
  • 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 时返回响应体,否则打印错误码:

HttpResponse<String> 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 HttpResponse<String> 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_URL=https://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 Demo

main 方法中示例查询的是资产ubuntu_docker上的账号root,请按实际资产/账号名称修改。预期输出为包含idsecret字段的 JSON 字符串。

6. 服务端处理流程与安全机制

理解服务端实现有助于排查调用问题。get_account_secret动作的完整处理链路如下(见 apps/accounts/api/account/application.py):

  1. 参数校验:序列化器IntegrationAccountSecretSerializer校验 query 参数,非法时直接返回 400;
  2. 账号定位:调用request.user.get_account(...),即集成应用对象的get_account()方法,按资产名/账号名(或 ID)过滤,且限定在该应用关联的账号范围内;
  3. 审计记录:命中账号后,向IntegrationApplicationLog写入一条访问日志,记录来源 IP、服务名称、账号(含用户名)与资产(含地址),实现凭据访问的全程可追溯;
  4. 敏感信息策略:根据SECURITY_DISABLE_VIEW_SECRET全局配置决定是否返回真实密钥;
  5. 权限控制:该动作要求accounts.view_integrationapplication权限(对应视图集中的rbac_perms配置)。

此外,集成应用模型(apps/accounts/models/application.py)还包含is_active启停开关、ip_group来源 IP 白名单等属性——当应用被停用或来源 IP 不在白名单内时,请求同样会被拒绝,这也是排查"鉴权通过但请求失败"时需要检查的维度。

7. 其他语言实现参考

JumpServer 为同一接口提供了多语言示例,便于跨技术栈团队对照移植,签名逻辑完全一致,仅 HTTP 库与字符串处理语法不同:

  • curl 示例:最精简的签名流程演示,适合调试与脚本化;
  • Go 示例 及封装库 jms_pam.go;
  • Node.js 示例;
  • Python 示例 及封装库 jms_pam/main.py。

在 JumpServer 的"应用管理"详情页中,还可通过 SDK 信息接口按语言直接拉取对应 README 与示例代码(服务端实现在get_sdks_info动作中按language参数读取 apps/accounts/demos 目录下的文件)。

8. 常见问题(FAQ)

Q: 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 20:41:53

CANN/GE CreateVector函数API文档

CreateVector 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前…

作者头像 李华
网站建设 2026/9/10 20:40:00

Hadoop电商大数据分析实战:架构设计与性能优化

1. 项目概述&#xff1a;当电商遇上Hadoop大数据分析 2012年&#xff0c;某头部电商平台首次公开其基于Hadoop的PB级用户行为分析系统架构&#xff0c;单日处理日志量突破100TB。十年后的今天&#xff0c;Hadoop已成为电商数据分析的标配技术栈。我们团队最近为一家年GMV超50亿…

作者头像 李华
网站建设 2026/9/10 20:38:45

深度学习Dataset类核心原理与实战优化技巧

1. Dataset类基础概念与核心价值在数据处理和机器学习领域&#xff0c;Dataset类是我们每天都要打交道的核心工具之一。简单来说&#xff0c;它就像是一个智能化的数据容器&#xff0c;不仅能够存储原始数据&#xff0c;还能帮我们高效地组织、预处理和批量读取数据。想象一下你…

作者头像 李华
网站建设 2026/9/10 20:38:39

花店小程序开发的好处和相关功能介绍

不少人都会在日常生活中去购买鲜花&#xff0c;并不是因为花香&#xff0c;而是享受生活。对于花店而言想要保持良好的销量&#xff0c;不仅要保持鲜花的品质&#xff0c;更要扩宽鲜花销售的渠道。店面所能销售的范围是有限的&#xff0c;而线上销售的渠道却不会受此限制&#…

作者头像 李华
网站建设 2026/9/10 20:36:16

uniApp iOS打包常见问题与解决方案

1. 问题现象与背景分析最近在将uniApp项目打包成iOS应用时&#xff0c;遇到了一个棘手的报错问题。具体表现为&#xff1a;在Xcode编译阶段控制台输出红色错误信息&#xff0c;导致最终无法生成.ipa文件。这种情况在实际开发中相当常见&#xff0c;尤其是当我们使用跨平台框架进…

作者头像 李华