我们有一个可以与外部服务对话的服务器端应用程序。这取决于我们的配置,我们是使用调用我们的用户的凭据、使用预配置的凭据还是根本不使用这些服务对这些服务进行身份验证。
外部服务可以使用 HTTP 协商身份验证。对于我们的自定义 HTTP/WebDAV 请求,我们使用 Apache HttpClient,我们可以在其中控制自己的凭据处理。但是对于 JAX-WS 调用(或由 3rd 方库调用的纯 HTTP URL),Java 的 HttpUrlConnection 自己处理身份验证。这就是事情变得奇怪的地方。
Java 应该总是尝试使用来自当前主题的 Kerberos 凭据进行协商。这很好,并且有效。它还可以使用票证缓存(即 kinit 会话或系统会话,如果可访问),但如果我正确阅读了文档(*),它应该只在两个条件下这样做:
*) https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/lab/part6.html
- javax.security.auth.useSubjectCredentialsOnly 显式设置为 false
- 提供了一个自定义 JAAS 配置,它明确地将 useTicketCache 设置为 true
这不是发生的事情。在我们的可重现测试中,如果主题为空,则默认始终使用系统凭据。更糟糕的是,将 useSubjectCredentialsOnly 显式设置为 true(这应该是默认设置)不会改变这种行为。到目前为止,我们发现的唯一解决方法是显式提供自定义 JAAS 配置,将 useTicketCache 设置为 false(这又应该是默认设置)。
对 Krb5LoginModule 的调试表明,除非我们配置此变通方法,否则登录模块实际上会使用 useTicketCache=true 进行调用。
我们可以在我们所有的 Windows 系统上重现这一点。似乎 Linux 可能表现良好,但我无法详细验证这一点(由于域问题)。
我读错了文档吗?还是Java实现中存在错误?还是我们的 Windows 系统不稳定?
我们应该告诉我们的客户始终配置一种解决方法,以防止 Java 使用服务用户而不是向我们的服务发送请求的用户的凭据秘密调用远程服务,这似乎非常不理想。