1

我客户的 Java 代码(胖客户端)需要通过网络代理访问 Web 服务。网络代理需要身份验证。客户希望以当前 Windows 用户身份进行静默身份验证(单点登录,无密码提示!)。我做了很多研究,似乎大多数人在他们的服务器端使用 NTLMv2 或 Kerberos。但我需要它在客户端进行网络代理身份验证。

我看到 Java 似乎支持开箱即用的 Kerberos ——这里看起来不错(但这——我没有当前用户的密码)。

Java 似乎还支持开箱即用的 NTLM——这个这个看起来很有趣。

不幸的是,这两种解决方案都需要我的代码提供用户名和密码。我不知道密码。我必须提示用户,这是我做不到的——根据规范,它必须是静默的。到目前为止我发现的两种解决方案都不是沉默的!不过,我绝对需要静默单点登录

因此,我想知道,我是否可以以某种方式使用Waffle或任何其他本机 (=> JNA) 库来解决我的问题(需要用户 + pw),用于基于 NTLMv2 和基于 Kerberos 的代理身份验证。如果这样的库可以为我们节省编写 kerberos 配置文件的工作,那也会很酷。

但我在 Waffle 文档中没有找到类似的内容。Waffle 是我目前发现的唯一一个原生库。我假设它必须是本机代码,因为其他任何东西都应该有安全屏障,阻止它为当前用户获取 Kerberos 票证(或 NTLMv2 中的类似令牌)。但是,恕我直言,本机代码应该能够访问这样的身份验证令牌,因为本机 OS-lib 可以访问仅将当前用户的 NTLM/Kerberos 令牌分发给当前用户的 Windows-API。

AFAIK IE 和其他本地程序能够通过 NTLM/Kerberos 保护的代理访问 Web 服务。因此,它必须以某种方式成为可能。

有关如何使用网络代理进行静默身份验证的任何提示?

如果还没有现成的东西,也许您可​​以给我一些提示,如何使用 Waffle 或任何其他(本机,JNI/JNA)库实现解决方案?

如果真的没有解决方案,我什至会自己使用 JNA 或 JNI 实现一个解决方案——你能给我一些正确方向的提示吗?我是一个 GNU/Linux 人,不得不承认我什至不知道从哪里开始在 Windows-API 文档中搜索它。

顺便提一句。我已经在 Waffle-community 中问过这个问题,但还没有得到任何答案。也许华夫饼不是最好的解决方案,无论如何?也许有更好的方法?

4

1 回答 1

0

你写了很多,很少实质性的。让我为你分解事情。

Java 似乎还支持开箱即用的 NTLM——这个和这个看起来很有趣。

那是错误的。这是使用 SSPI 专门用于HttpUrlConnection. 忘记两者。

忘记 NTLM,它是基于连接的,并且是专有的。使用 Kerberos!

我不知道您使用的是什么 HTTP 库,但该方法非常简单,您不需要 Waffle,而只需要 JNA:

import java.io.IOException;
import java.util.Base64;

import org.apache.commons.io.HexDump;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import com.sun.jna.platform.win32.Secur32;
import com.sun.jna.platform.win32.Sspi;
import com.sun.jna.ptr.IntByReference;

public class SspiAuth {

  public static void main(String[] args)
      throws ArrayIndexOutOfBoundsException, IllegalArgumentException, IOException {

    Sspi.CredHandle cred = new Sspi.CredHandle();

    int status = Secur32.INSTANCE.AcquireCredentialsHandle(null, "Negotiate",
        Sspi.SECPKG_CRED_OUTBOUND, null, null, null, null, cred, null);
    System.out.println(status);

    Sspi.CtxtHandle ctxt = new Sspi.CtxtHandle();

    Sspi.SecBufferDesc bufferDesc = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN,
        Sspi.MAX_TOKEN_SIZE);
    IntByReference ctxtAttrs = new IntByReference();

    status = Secur32.INSTANCE.InitializeSecurityContext(cred, null,
        "HTTP/<fqdn of the proxy>",
        Sspi.ISC_REQ_MUTUAL_AUTH, 0, Sspi.SECURITY_NATIVE_DREP, null, 0, ctxt, bufferDesc,
        ctxtAttrs, null);

    System.out.println(status);
    HexDump.dump(bufferDesc.getBytes(), 0, System.out, 0);

    HttpClientBuilder builder = HttpClientBuilder.create();

    try (CloseableHttpClient httpClient = builder.build()) {

      HttpGet method = new HttpGet("https://deblndw024v.ad001.siemens.net:8444/manager/html");
      method.addHeader("Authorization",
          "Negotiate " + Base64.getEncoder().encodeToString(bufferDesc.getBytes()));
      CloseableHttpResponse response = httpClient.execute(method);
      EntityUtils.consumeQuietly(response.getEntity());
      response.close();

      System.out.println(response.getStatusLine());
      String responseToken = response.getFirstHeader("WWW-Authenticate").getValue()
          .substring(10);
      System.out.println(responseToken);
      byte[] rawResponseToken = Base64.getDecoder().decode(responseToken);
      Sspi.SecBufferDesc bufferDesc2 = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN,
          rawResponseToken);
      Sspi.CtxtHandle ctxt2 = new Sspi.CtxtHandle();
      Sspi.SecBufferDesc bufferDesc3 = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN,
          Sspi.MAX_TOKEN_SIZE);
      status = Secur32.INSTANCE.InitializeSecurityContext(cred, ctxt,
          "HTTP/<fqdn of the proxy>",
          Sspi.ISC_REQ_MUTUAL_AUTH, 0, Sspi.SECURITY_NATIVE_DREP, bufferDesc2, 0, ctxt2,
          bufferDesc3, ctxtAttrs, null);
      System.out.printf("0x%x%n", status);
    }

  }

}

考虑释放句柄以避免内存泄漏!

于 2020-10-05T08:28:20.520 回答