人们如何在Windows上使Java SPNEGO客户端工作?

16
为了在Windows上使用Java进行客户端HTTP SPNEGO身份验证,您需要设置Windows注册表键allowtgtsessionkey。这一点已经有很好的文档记录。但我不明白的是,人们是如何绕过这个问题的?大多数公司网站绝不会为了一个软件而改变Windows的注册表键。而且想想如果需要在组织中的每台工作站上更改这个键,会带来多大的麻烦。但这只是理论,因为到目前为止,我还没有能说服我们的任何客户改变这个注册表键。
我不怪他们。大多数公司管理员会认为这是放宽了安全性,因此会反对这样做。
我读到了这个: 在Java中或者使用本机SSPI API的命令行工具中,有没有一种方法来获取一个服务的Kerberos票证? 但这已经相当陈旧了。
所以我真的,真的不明白人们是如何让Windows + Java客户端 + Kerberos在除了大学环境、家庭用户等之外的其他环境中工作的。
企业管理员问我的问题是:“为什么我们需要设置这个注册表键,而像IE和Firefox这样的应用程序在不设置这个键的情况下可以正常执行SPNEGO?”嗯,我知道答案。这是因为(很可能)像IE和Firefox这样的应用程序是基于Windows本地GSS API(SSPI),而Sun的Java使用了自己的实现。
我假设使用类似WAFFLE这样的东西可以解决问题,但我更倾向于纯Java解决方案。我还假设使用基于Java的解决方案,如Spring Security或Apache HttpClient,也无法解决这个问题。
非常感谢任何帮助或指导。 更新1: 我发现在Oracle的错误数据库中有一个关于这个问题的RFE。还有一个由Oracle员工提交的关于这个问题的补丁,以及关于这个功能的JDK邮件列表上的讨论。除了我能理解的这个功能在当前的Java 7中不可用,甚至没有试验性的版本,这并没有让我更明智。对吗?
更新2:
现在,在OpenJDK安全开发邮件列表上,这个问题又有了新的进展。

现在Apache HTTP客户端中已经包含了一个非常好的解决方案,使用JNA从本地SSPI API获取票证。请参考以下答案:https://dev59.com/DW025IYBdhLWcg3wyJCV#22865583 - Malcolm Smith
4个回答

10

感谢您参考我的安全开发人员邮件列表中的主题;-) 我的中期目标是通过认可类路径使此补丁适用于Java 6+。您可能会对我最近创建的WAFFLE票证感兴趣:https://github.com/dblock/waffle/issues/50

我也评估了WAFFLE,但它与Java GSS非常不相似,因此必须创建重复的代码,这是我绝对要避免的事情。

整个问题并不完全是Oracle的错。 Microsoft只是阻止通过LSA CallPackage函数调用会话票证。借口是安全。我真的想知道当我无法合理访问TGT时,SSPI如何能够创建服务票证。因此,这样的闭源解决方案很糟糕。

现在,您只有三个选择:

  1. 通过Java手段再次获取TGT
  2. 尝试WAFFLE
  3. 编写自定义代码

我已经隐藏了垃圾注册表键,因为它对于具有域帐户的本地管理员无效。 在我的情况下,Tomcat dev on Windows,我暂时采用调用Java的kinit。


非常感谢您的回复。虽然有点令人沮丧。 :-( - peterh
在我的情况下,我使用Windows上的Tomcat开发,我已经暂时采用调用Java的kinit的方法。你能详细说明一下吗?这是在Windows客户端上的一个潜在解决方案还是什么? - peterh
@nolan6000,请在Github问题中留下评论。关于“kinit”问题:是的,我可以解决,你的用例是什么?你有一个Java的fat客户端吗? - Michael-O
@nolan6000,我会选择第一种选项。从用户那里收集upn和密码,调用“LoginContext”,获取“Subject”并执行所有操作;或者你可以尝试使用Waffle,但正如我之前提到的那样,这个解决方案太陌生了。 - Michael-O
如果我提示用户输入凭据,那么我可能会使用LDAP。毕竟,我认为处理Kerberos的复杂性唯一的优势就是它允许无需提示的SSO。 - peterh
你可以执行LDAP绑定,但如果你使用Kerberos,会有更多的选择。例如,你可以进行相互认证和凭证委派。其他系统没有这个功能。如果你只需要用户认证,我建议你尝试评估Waffle或编写JNI代码,或直接使用JNA到SSPI。 - Michael-O

2
截至Java 13,JDK现在内置了对Windows自己的GSS API(也称为SSPI)的支持。
请参见Java 13发布说明以及JDK-6722928
更新: 现在已经将此功能回溯到Java 11。您需要使用Java 11.0.10或更高版本。如果您的JDK分发版包含位于bin目录中的名为sspi_bridge.dll的文件,则可以识别出它是否支持SSPI。
甚至有一个回溯到Java 8的票据,但截至2021年11月尚未实现。

1

如果有人想要使用Java 8来寻找简单的解决方案,Waffle 是最好的选择。

代码:

import java.net.URI;
import java.util.Base64;

import waffle.windows.auth.IWindowsSecurityContext;
import waffle.windows.auth.impl.WindowsSecurityContextImpl;

public class KerberosCredentiels {

  /**
   * Generate current user token using Kerberos ticket
   */
  public String retrieveToken(URI uri) {
    IWindowsSecurityContext clientContext = WindowsSecurityContextImpl.getCurrent("Kerberos", convertToSPN(uri));
    byte[] token = clientContext.getToken();

    return Base64.getEncoder().encodeToString(token);
  }

  private static String convertToSPN(URI uri) {
    StringBuilder builder = new StringBuilder();

    builder.append("http/");
    builder.append(uri.getHost());

    return builder.toString();
  }

}

Maven 依赖项:

<dependency>
    <groupId>com.github.waffle</groupId>
    <artifactId>waffle-jna</artifactId>
    <version>1.8.3</version>
</dependency>

<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna-platform</artifactId>
    <version>4.3.0</version>
</dependency>

<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>4.3.0</version>
</dependency>

有趣。点赞分享。然而,自JDK v13起,它已经在JDK本身中得到了完全支持,而且您很快就会被迫放弃Java 8,所以也许可以尝试在Java 17上进行这项工作? - peterh
抱歉,不需要Java 17如所述。JDK中对SSPI的本地支持现已被回溯到Java 11 - peterh

1

非常有趣。我之前不知道这个。对我来说,Apache 的人们选择了唯一明智的路径:从 SSPI API 获取票据。我希望核心 JRE 也能做到同样的事情。我唯一担心的是为什么这与一个 HTTP 库(Apache HTTP Client)相关联,而不是更普遍的安全库。顺便说一句:Java 9 中有一个新的 HTTP 客户端即将推出,但我敢打赌这不在他们的议程中。 - peterh
如果您不想/不需要使用HTTP,可以使用JNA自己创建Kerberos票据。请查看WindowsNegotiateScheme源代码 https://hc.apache.org/httpcomponents-client-ga/httpclient-win/xref/org/apache/http/impl/auth/win/WindowsNegotiateScheme.html - Malcolm Smith
3
阅读此回答的人:除非您知道自己在使用什么,否则不要依赖此模块。该模块是实验性的且存在缺陷。我已经在HttpComponents Dev邮件列表中多次提到过这一点。 - Michael-O

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接