我正在使用 jTDS 连接到 SQLServer。jTDS 在内部使用 GSS 获取 kerberos 的服务票证并建立安全上下文。由于我的应用程序寿命很长,并且我的连接在整个过程中保持活动状态,因此我需要更新 kerberos 的服务票证,以允许 SQL 服务器自行更新它们(kdc 策略设置为在 12 小时后过期所有票证)。
jTDS 为获取 kerberos 令牌所做的(或多或少)如下:
GSSManager manager = GSSManager.getInstance();
// Oids for Kerberos5
Oid mech = new Oid("1.2.840.113554.1.2.2");
Oid nameType = new Oid("1.2.840.113554.1.2.2.1");
// Canonicalize hostname to create SPN like MIT Kerberos does
GSSName serverName = manager.createName("MSSQLSvc/" + host + ":" + port, nameType);
GSSContext gssContext = manager.createContext(serverName, mech, null, GSSContext.DEFAULT_LIFETIME);
gssContext.requestMutualAuth(false);
gssContext.requestCredDeleg(true);
byte[] ticket = gssContext.initSecContext(new byte[0], 0, 0);
我怀疑我得到的票是不可更新的。我正在通过执行以下操作来检查:
ExtendedGSSContext extendedContext = (ExtendedGSSContext) gssContext;
boolean[] flags = (boolean[]) extendedContext.inquireSecContext(InquireType.KRB5_GET_TKT_FLAGS);
System.out.println("Renewable = " + flags[8]);
在我们的特定配置中,GSS 正在从 JAAS 登录模块获取 kerberos TGT。我们将以下变量设置为 false -Djavax.security.auth.useSubjectCredsOnly=false
,并且在 login.cfg 文件中我们配置了以下登录模块:
com.sun.security.jgss.krb5.initiate {
com.sun.security.auth.module.Krb5LoginModule required
useKeytTab=true
keyTab="/home/batman/.batman.ktab"
principal="batman@GOTHAMCITY.INT"
storeKey=true
doNotPrompt=true
debug=false
};
我注意到的另一件事是该getLifetime()
方法GSSContext
似乎不起作用。无论票证的实际生命周期是多少,它总是返回 2147483647(最大整数)。
我对分支 jTDS 驱动程序感到满意,因此如果需要,我可以修改它建立 GSS 上下文的方式。
我尝试了什么:
使用 GSS api 的本机实现:
这在获取可更新票证方面对我来说很好,但它带来了另一组问题(在确保正确设置票证缓存并且其中的票证正确更新方面)。如果我可以绕过这个选项,那就太好了。我在这里观察到的事情是,该getLifetime()
方法实际上以票证的秒数返回了真实的生命周期。
重新实现 KerberosLoginModule:
基于对这个问题的回答Jaas - Requesting Renewable Kerberos Tickets我重新实现了 LoginModule 以在请求 TGT 之前设置KDCOption
RENEW KrbAsReqBuilder
。从我获得可更新 TGT 的意义上说,这很好用,但 GSS 从该 TGT 获得的票仍然不可更新。如果我在 KDCOption 对象的构造函数中设置断点并在每个请求上手动设置 RENEW 标志(即使KrbTgsReq
是由 GSS 完成的)它也可以工作,但要使该更改富有成效需要对 GSS 进行重大重写,我对此感到不舒服。