为了在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安全或Apache HttpClient)无济于事,因为它们都将遭受此问题的困扰。
任何帮助或指示将不胜感激。
UPDATE1:
我发现在Oracle的错误数据库中有一个RFE。Oracle员工也就此事提交了补丁,JDK邮件列表中也讨论了此功能。这并没有让我变得更聪明,除了据我所知,这在当前的Java 7中不可用,甚至不是实验性的。右?
UPDATE2:
这个问题现在再次出现在OpenJDK安全开发邮件列表中。
安全开发邮件列表中引用我的线程;-)我的中期目标是通过认可的类路径使这个补丁可用于Java 6+。您可能对我最近创建的这张华夫饼票感兴趣:https://github.com/dblock/waffle/issues/50
我也评估过华夫饼,但它不像Java-GSS,必须创建重复的代码,这是我想要避免的事情。
这整个问题并不完全是甲骨文的错。Microsoft只是通过 LSA CallPackage
函数阻止对会话票证的任何调用。借口是安全。我真的很想知道当我无法合理访问 TGT 时,SSPI 如何能够创建服务票证。因此,这样的闭源解决方案很糟糕。
目前,您只有三个选择:
- 通过Java手段再次获取TGT。
- 尝试华夫饼
- 编写自定义代码
我已经埋葬了蹩脚的注册表项,因为它无论如何都不适用于具有域帐户的本地管理员。就我而言,Windows上的Tomcat开发我在此期间求助于Java的kinit。
从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 月,它尚未实现。
非常好的解决方案包含在Apache HTTP客户端中,使用JNA从本机SSPI API获取票证。看到这个答案:
https://stackoverflow.com/a/22865583/381161
对于寻找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>