You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows环境下如何持久化或复用Impersonate Token?基于JNA

实现Windows令牌的提取、持久化与复用(基于JNA)

核心思路

Windows的令牌句柄(HANDLE)是进程内的资源,无法直接序列化存储或跨进程传输。必须将其转换为序列化的令牌字节数组(可导出的二进制数据),才能持久化到数据库或通过JSON发送,之后再从字节数组还原为可用的令牌句柄。

步骤1:提取并序列化令牌

1.1 复制令牌为可导出类型

原LogonUserW返回的令牌可能没有导出权限,需要先复制出一个具备导出权限的令牌:

// 从PointerByReference获取原始令牌句柄
HANDLE hOriginalToken = new HANDLE(phToken.getValue());

PointerByReference hExportableToken = new PointerByReference();
// 调用DuplicateTokenEx创建可导出的令牌
boolean success = Advapi32.INSTANCE.DuplicateTokenEx(
    hOriginalToken,
    WinNT.TOKEN_ALL_ACCESS,
    null,
    WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
    WinNT.TOKEN_TYPE.TokenPrimary,
    hExportableToken
);
if (!success) {
    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
HANDLE hTokenToExport = new HANDLE(hExportableToken.getValue());

1.2 获取序列化的令牌字节数组

通过GetTokenInformation的TokenSerialization类型获取序列化数据:

// 第一步:获取所需缓冲区大小
IntByReference bufferSize = new IntByReference();
Advapi32.INSTANCE.GetTokenInformation(
    hTokenToExport,
    WinNT.TOKEN_INFORMATION_CLASS.TokenSerialization,
    null,
    0,
    bufferSize
);

// 第二步:分配缓冲区并获取序列化数据
byte[] tokenBuffer = new byte[bufferSize.getValue()];
Pointer buffer = new Memory(bufferSize.getValue());
success = Advapi32.INSTANCE.GetTokenInformation(
    hTokenToExport,
    WinNT.TOKEN_INFORMATION_CLASS.TokenSerialization,
    buffer,
    bufferSize.getValue(),
    bufferSize
);
if (!success) {
    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
buffer.read(0, tokenBuffer, 0, tokenBuffer.length);

// 转换为Base64字符串,方便存储和JSON传输
String serializedTokenBase64 = Base64.getEncoder().encodeToString(tokenBuffer);

1.3 关闭临时句柄

用完复制出来的令牌句柄后要关闭,避免资源泄漏:

Kernel32.INSTANCE.CloseHandle(hTokenToExport);
Kernel32.INSTANCE.CloseHandle(hOriginalToken); // 如果原令牌不再使用也可以关闭

步骤2:持久化与传输

  • 把serializedTokenBase64字符串存入数据库,或直接作为JSON字段发送给其他服务(比如JSON结构{"token": "xxx..."})。
  • 安全注意:序列化令牌包含敏感身份信息,存储需加密(如数据库字段加密),传输必须用HTTPS,防止泄露。

步骤3:从序列化数据还原令牌

当需要复用令牌时,将Base64字符串转回字节数组,通过LsaImportLogonUser导入为可用的令牌句柄:

// 从Base64还原字节数组
byte[] tokenBuffer = Base64.getDecoder().decode(serializedTokenBase64);

// 准备LSA导入所需的参数
LSA_UNICODE_STRING originName = new LSA_UNICODE_STRING();
originName.Buffer = null;
originName.Length = 0;
originName.MaximumLength = 0;

PointerByReference hImportedToken = new PointerByReference();
NTSTATUS status = LsaWin32.INSTANCE.LsaImportLogonUser(
    null, // LSA句柄,null使用默认
    new LSA_STRING(), // 认证包名称,空使用默认
    WinNT.SECURITY_LOGON_TYPE.Interactive, // 登录类型,和LogonUserW一致
    originName,
    tokenBuffer,
    tokenBuffer.length,
    null,
    hImportedToken
);

if (status.intValue() != WinNT.STATUS_SUCCESS) {
    throw new Win32Exception(LsaWin32.INSTANCE.LsaNtStatusToWinError(status.intValue()));
}

// 拿到可用的令牌句柄,可用于ImpersonateLoggedOnUser等操作
HANDLE hReusedToken = new HANDLE(hImportedToken.getValue());

关键注意事项

  • 令牌有效期:Windows令牌有有效期,默认取决于域策略,过期后无法复用,需要重新登录生成新令牌。
  • 权限控制:确保操作令牌的进程具备足够权限(如管理员权限或对应的身份权限),否则API调用会失败。
  • 资源泄漏:所有获取的HANDLE必须在使用完毕后调用Kernel32.INSTANCE.CloseHandle()关闭,避免系统资源耗尽。

内容的提问来源于stack exchange,提问作者Ravi Soni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 22:57:46