如何使用 Windows Java 客户端保存 Kerberos 服务票证?

Posted

技术标签:

【中文标题】如何使用 Windows Java 客户端保存 Kerberos 服务票证?【英文标题】:How to save Kerberos Service Ticket using a Windows Java client? 【发布时间】:2016-07-17 05:04:06 【问题描述】:

我编写了一个在 Windows 下运行的简单 Java HTTP 客户端。客户端与需要通过 SPNego 进行 Kerberos 身份验证的 Web 服务器通信。

我遇到了两个问题:

服务票证未存储在我的凭据缓存中。执行请求后,我希望看到一个 Kerberos 服务票证存储在我的凭据缓存中 C:\Users\<user>\krb5cc_<user> 下 - 我假设 Java 将服务票证存储在凭据缓存中是错误的吗?我想重用在客户端 A 中获得的服务票证用于客户端 B 中的请求(其中两个客户端都是同一台机器上的 Java 应用程序)。 Java可以做到吗?

如果我在循环中运行代码低于一百次,它只能工作 n 次(其中 n 是 1 到 100 之间的随机数)。失败的请求返回 401 错误消息,因为 Java 无法检索服务票证(请记住:由于我的应用程序不会在请求之间存储服务票证,它会尝试从 TGT 为每个请求获取新的服务票证) .我已将错误消息添加到此问题的底部。

我已经通过 kinit 在我的 JDK 的 bin 文件夹中创建了一个 TGT。以下代码 sn -p 用于发出简单的 GET 请求:

  static void testJavaHttpKerberosAuthentication() throws IOException 
    URL obj = new URL(URI);
    HttpURLConnection con = (HttpURLConnection) obj.openConnection();
    int responseCode = con.getResponseCode();
    System.out.println("\nSending 'GET' request to URL : " + URI);
    System.out.println("Response Code : " + responseCode);

    BufferedReader in = new BufferedReader(
    new InputStreamReader(con.getInputStream()));
    String inputLine;
    StringBuffer response = new StringBuffer();
    while ((inputLine = in.readLine()) != null) 
      response.append(inputLine);
    
    in.close();

    //print result
    System.out.println(response.toString());
  

这是我的 jaas.conf 的内容(如 here 所述):

com.sun.security.jgss.krb5.initiate 
com.sun.security.auth.module.Krb5LoginModule required doNotPrompt=false useTicketCache=true;
;

我正在使用以下参数运行我的应用程序:

-Djava.security.auth.login.config=D:\jaas.conf
-Dsun.security.krb5.debug=true
-Djavax.security.auth.useSubjectCredsOnly=false

我没有使用 krb5.ini,因为我的客户端从域配置中获取了正确的 KDC。

我可以通过以下命令为我的凭据缓存生成 TGT:

C:\Program Files\Java\jdk1.8.0_77\bin>kinit
Password for <user>@<domain>:
New ticket is stored in cache file C:\Users\<user>\krb5cc_<user>

最后,这里是授权失败情况下的异常和 Kerberos 调试输出(参考问题 2)。请注意,ctime 显然是错误的。我进行了许多不同的尝试,ctime 的时间跨度从 1970 年到 2040 年不等。有趣的是,并非每个请求都会发生这种情况。

>>>KRBError:
 cTime is Wed Jun 07 12:24:03 CEST 2017 1496831043000
 sTime is Tue Mar 29 16:38:24 CEST 2016 1459262304000
 suSec is 283371
 error code is 34
 error Message is Request is a replay
 sname is HTTP/<spn>@<domain>
 msgType is 30
 KrbException: Request is a replay (34) - PROCESS_TGS

我已经尝试使用Subject.doAs 与 JAAS 一起工作,但这会导致同样的问题。通过浏览器访问服务器可以正常工作(尽管这无法比较,因为浏览器使用的是 Windows 本机凭据缓存 AFAICT)。

如果您能提供一些关于如何调试此类问题的建议,我将不胜感激。

编辑:通过KRB5CCNAME 环境变量明确指定凭证缓存的路径,不会改变行为。似乎 TGT 是从凭据缓存中获取的,但服务票证并未存储在那里。

【问题讨论】:

顺便说一句,有两个额外的跟踪标志可以证明是有用的:-Djava.security.debug=gssloginconfig,configfile,configparser,logincontext 用于调试 JAAS 配置问题,当然还有-Dsun.security.spnego.debug=true 【参考方案1】:

JAAS 不会将票据保存到缓存中,您必须使用 kinit 或通过您的代码以编程方式调用 kinit 代码。 我已经写了一个关于这个问题的问题/答案here。

【讨论】:

【参考方案2】:

关于偶尔出现的随机时间值:我们发现在krb5.ini 中设置udp_preference_limit = 1 可以解决问题。这有效地告诉 Kerberos 始终尝试首先使用 TCP 发送包。显然切换到UDP时出现问题(不确定是UDP的问题还是协议之间的切换)。

【讨论】:

呃 - 看起来 UDP 涉及许多看似无关的问题 steveloughran.gitbooks.io/kerberos_and_hadoop/content/sections/…【参考方案3】:

关于缓存 >> 看起来您没有指定系统上的默认缓存是什么(参见环境变量 KRB5CCNAME),因此 Java 和 kinit 恢复为硬编码默认值。这不是相同的默认值...

您的kinit 版本显然使用Linux 标准,即FILE: Java 通常使用 Windows 标准,即由 MIT-Kerberos-for-Windows 服务管理的 API:

可能的解决方法:要么使用 Windows 上的 Kerberos UI 创建 TGT,要么通过设置 KRB5CCNAME 强制 Java 使用文件缓存。

参考: MIT Kerberos documentation 尤其是最后一个关于硬编码默认值的链接

~~~~~~~

关于随机时间值>>我不知道。

【讨论】:

感谢您的回复,萨姆森。我的印象是默认情况下 Java 也使用 Linux 标准作为凭证缓存(参考 The Initial Credentials)。至少 Java 已经能够在用户文件夹的凭证缓存中找到 TGT。尽管如此,我已经尝试通过KRB5CCNAME 明确指定凭据缓存,但它并没有改变行为。我的 HTTP 请求正在获取服务票证,但它没有存储在凭据缓存中。 Hmm... 您的 HTTP 库使用不同的 JAAS 配置条目来管理服务票证(例如 Client ... ),同时将 TGT 的管理委托给默认的 JAAS 例程,该例程使用默认配置条目(即com.sun.security.jgss.krb5.initiate ... )?!? 我只使用 java.net 组件并考虑到this documentation 我不认为jaas.conf 中的任何其他条目应该与手头的示例相关。我找不到任何描述使用 Java 在凭据缓存中存储服务票证的过程的来源,要么是默认行为,由于某种原因对我不起作用,要么似乎不可能。 旁注:docs.oracle.com/javase/8/docs/jre/api/security/jaas/spec/com/… 详细介绍了 (a) Windows 上票证缓存的搜索顺序以及 (b) Java只希望缓存 TGT - 与服务票证无关 其他注意事项:您可以跳过kinit 步骤并使用“keytab”文件自动创建 TGT - 私有 TGT 或缓存 TGT,具体取决于 JAAS conf。诀窍是你需要一个 Linux 机器来创建带有ktutil 的“keytab”。当然,您必须谨慎地限制对“keytab”的访问...... [警告:AFAIK Java 总是创建不可更新的 TGT,但由于您可以随时访问“keytab”,这没什么大不了的]

以上是关于如何使用 Windows Java 客户端保存 Kerberos 服务票证?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 中保存自定义 Java 对象?

Windows 在哪里保存 JDK 位置参考?

如何保存使用 OAuth 2 (Spring) 登录的用户?

如何在Java / Kotlin客户端库中为Google上的操作将一些数据保存到userStorage中

response.getwriter()页面的信息如何保存到本地文件

如何从客户端(android-java app)发送数据到解析服务器,处理它,将其保存在parse-dashboard中并将结果发送给客户端?