Java中的简单Kerberos客户端?

45

像谷歌的Chrome和IE这样的应用程序可以透明地处理Kerberos身份验证;然而,我无法找到一个“简单”的Java解决方案来匹配这种透明性。我发现的所有解决方案都需要krb5.conf文件和login.conf文件的存在,而上述应用程序似乎都不需要。

建立具有Kerberos SSO功能且能够正常工作的Java应用程序的最佳方法是什么?

[更新]: 明确一下,我需要一个客户端端的解决方案来创建票证而不是验证票证。此外,似乎SPNEGO是默认的“包装”协议,最终将委派给Kerberos,但我也需要能够处理SPNEGO协议。


1
我没有详细检查过,但可以看一下https://projects.apache.org/project.html?directory-kerby - 也许有帮助(也可能没有)。 - Make42
我知道这是一个10年前的问题,但对于我过去两周一直在尝试解决的问题来说非常重要。为了未来的搜索者,我回答一下。对我有效的解决方案是:https://dev59.com/prD3oIgBc1ULPQZFCYGU - Pawel
9个回答

16
现在有一个简单的解决方案,使用Apache HTTP Components Client 4.5或更高版本。虽然这在4.5中仍被标记为实验性质,但在企业环境中对我来说工作得很好。除了HC 4.5客户端jar文件外,您还需要将httpclient-win、jna和jna-platform jar文件放在类路径上,这些都是与http-component-client一起提供的。然后,您可以按以下方式构建启用Kerberos的HC客户端:
CloseableHttpClient httpclient = WinHttpClients.createDefault();

或者使用构建器:

HttpClientBuilder clientBuilder = WinHttpClients.custom();

在构建客户端之前,可以根据需要进行定制:

CloseableHttpClient client = clientBuilder.build();

这种解决方案无需任何外部配置,最重要的是解决了内建JRE机制在拥有本地管理员权限的Windows 7+用户中崩溃的问题。这是可能的,因为Kerberos票据是通过JNA直接从SSPI API检索而不是通过JRE提供的GSSAPI传递的。

来自http-components团队的示例代码

所有这些都得益于Daniel DoubrovkineTimothy WallRyan McKinley的出色工作。


你确定这个能用吗?我检出了4.4-SNAPSHOT并在本地构建和安装了它(到我的Maven仓库),尝试了上面的代码,但无法进行身份验证。这个示例中是否有缺失,还是我做错了什么? - carlspring
它在我的环境中运行正常。出了什么问题?你是否已经打开了 HC 的调试输出?你确定你有正确的服务主体来进行身份验证吗? - Malcolm Smith
2
我猜这个解决方案只有在你的Windows/Active Directory凭据(用户名/密码)与Kerberos服务器的用户名/密码相同时才有效。我有一个情景,那种情况并不适用于我,并且上述代码对我来说无效。有没有一种方法可以提供正确的用户名/密码? - quux00
@bacar https://github.com/apache/httpclient/blob/trunk/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java - Malcolm Smith
1
@NicholasDiPiazza 不需要用户/密码,这就是重点!它通过JNA从操作系统获取当前登录用户的Kerberos令牌。 - Malcolm Smith
显示剩余4条评论

10

在David Roussels关于特定URL的基于HTTP的Kerberos身份验证的答案中,此处进一步解释:

你的代码有效的原因是因为你的目标SPN(服务器端主体)配置为HTTP/serverhostname.realm.com@DOMAIN.COM。在这种情况下,它会工作,因为你没有显式设置令牌。URLConnection在内部使用该SPN设置令牌。

1. 执行步骤(参考我的上一个答案)以获取一个Subject

2. 使用gss api初始化安全上下文生成上下文令牌。有很多教程可以帮助你完成这个步骤

3. 对令牌进行Base 64编码

4. 将令牌附加到URLConnection:

URL url = new URL("http://myhost/myapp")
HttpURLConnection urlConn = (HttpURLConnection)url.openConnection(); = 
urlConn.setRequestProperty("Authorization", "Negotiate " + encodedToken);

5 实现特权操作:

//this internally calls the getInputStream
public class PrivilegedGetInputStream implements PrivilegedExceptionAction<InputStream>

用Subject.doAs包装整个内容

//use prev answer instructions to get subject
Subject.doAs(subject, new PrivilegedGetInputStream(urlConnection)

我制作了一个示例项目:https://github.com/nddipiazza/spnego-http-client,展示了这种方法。虽然还不完美,但它可以工作。 - Nicholas DiPiazza

9
Oracle有使用Java的SaslClient的示例。虽然我不是Java程序员,但曾经向一个Java程序员指出过这一点,他们很快就能让它工作起来了。它可能仍然需要某个“conf”文件(注意:Kerberos使用环境变量,通常以KRB5_开头,以知道在哪里查找此类文件)。还要注意,Kerberos本身不包括任何传输方式--您的应用程序需要知道如何发送和接收Kerberos有效载荷,以使服务器按预期进行认证(这取决于您尝试进行身份验证的服务器而有所不同)。
编辑:您已编辑了您的问题,因此这里提供了一个与Java中SPNEGO相关的链接,可能会有所帮助: http://download.oracle.com/javase/6/docs/technotes/guides/security/jgss/lab/part5.html

7

您实际上不需要做任何事情。在Java 6中,在Windows客户端上,您可以执行以下操作:

new URL("http://myhost/myapp").openStream();

谈判认证非常好用。至少我觉得是这样的。而且我测试时所用的服务器只支持谈判认证,不支持NTLM认证。


这与创建Kerberos票证有什么关系? - Andrew White
原始问题是“构建具有Kerberos SSO功能的Java应用程序的最佳方法是什么?”我指出Java 6在Windows上为您执行SPNEGO身份验证,因此您无需处理krb5.conf。 Kerberos在下面使用。它只是工作,比我预期的要好得多。请注意,这意味着您必须使用默认的HTTP客户端,而不能使用apache commons http libs。 - David Roussel
现在我明白你在说什么了。我得找时间试一试。 - Andrew White
我觉得我很迟钝,但是URL似乎没有getInputStream()方法,你是指openStream()吗(但是在你描述的相同设置上,这对我不起作用)。 - James Robinson
啊,我是指openStream()。您安装JRE时用的是安装程序还是仅解压缩了它?也许安装程序做了些什么来使它工作。 - David Roussel

4

如果您想避免使用login.conf文件,您需要进行不同的编码:

//define your own configuration
import javax.security.auth.login.Configuration;
public class CustomLoginConfiguration extends Configuration

//pass certain parameters to its constructor
//define an config entry
import javax.security.auth.login.AppConfigurationEntry;
private AppConfigurationEntry configEntry;

//define a map of params you wish to pass and fill them up
//the map contains entries similar to one you have in login.conf
Map<String, String> params = new HashMap<String, String>();

//define the configuration
configEntry = new AppConfigurationEntry(
            "com.sun.security.auth.module.Krb5LoginModule",
            AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, params);

//implement getappconfig method
public AppConfigurationEntry[] getAppConfigurationEntry() {
    return new AppConfigurationEntry[] { configEntry };
}

现在,一旦你完成了这个定义,就可以使用它来从kdc获取票据。
//get ticket in login context
LoginContext lc = null;
    lc = new LoginContext("lc", null, callback, new CustomLoginConfiguration(argumentlist));
    lc.login();

现在你可以获取jaas主体,并基本上执行大量的身份验证操作。

如果需要更多的指针,请留言评论。


2
您可以使用系统属性来指定KDC主机名和服务名称,而不是配置文件,但这些内容(至少)是强制性的...
Waffle实际上会为您提供大部分属性设置所需的信息,即使它不能为您获取票证。查看WindowsAuthProviderImpl类(Waffle.chm帮助文件显示API)。
我使用JAAS以两个步骤从Active Directory中获取服务票证:
1.使用Krb5LoginModule检索缓存的TGT并将其添加到Subject中。
2.使用Subject和GSS-API从KDC检索服务票证。
The Java Way of Active Directory网站上有很多有用的信息和示例代码。

1
我创建了一个小工具,简化了使用httpclient连接到kerberos的过程,你可能想试一试。 https://github.com/DovAmir/httpclientAuthHelper
DefaultHttpClient httpclient = new DefaultHttpClient();
AuthUtils.securityLogging(SecurityLogType.KERBEROS,true);
CredentialsUtils.setKerberosCredentials(client, new UsernamePasswordCredentials("xxx", "xxx"), "domain", "kdc");
client.executeMethod(httpget);

假设我在*nix中运行一个客户端应用程序,有一个Active Directory令牌,现在我需要验证它,并从令牌/AD服务器获取用户@域和组列表。这是否可能? - Luciano

0

那是针对服务器端的,不是客户端。 - Alex
它也支持客户端。 - Yamikuronue

0

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