问题描述
请注意:- 我只想验证是否可以使用 JAAS/GSSAPI 实现以下目标。我找不到任何指针。
让我首先清除对我的应用程序的限制:
- 我们不能有静态的 krb.conf 文件。它是动态的,不断变化的。因此,无法在内存中缓存 LoginContext 对象,因为一旦 krb.conf 文件被修改,主题的凭据就会失效。
- 我们想要管理大量我们没有任何先验信息的领域,因此无法使用静态 krb.conf 文件。
- “
sun.security.krb5.internal.tools.Kinit
”是专有的,总是收到警告,它可能会在未来的版本中被删除。所以我们不能使用它来生成缓存,因为存在运行时问题的风险。注意:即使 krb.conf 文件本机更改,缓存的 TGT 也不会过期。但这里的问题是,我们必须使生成和维护 krb.conf 文件的代码变得非常复杂。我们想避免这种情况 - 我们保留使用 Runtime#exec(...) 运行 kinit 工具的选项作为生成 TGT 缓存文件的最后手段(这将减少对 sun.security.krb5.internal.tools.Kinit 的依赖包),但会使我们的 krb.conf 生成和维护逻辑更加复杂。
- 我正在寻找更简单的解决方案。
- 我们当前的实现非常简单:我们更改 krb.conf 文件以适应当前领域和域信息,然后删除该文件。一切都非常动态,效率很高,但问题是,我们无法缓存 TGT 和服务票证,这会增加 KDC 服务器的负载并导致性能下降!
通过全面的介绍,让我们转向我正在尝试的 PoC。请提供指点或:
- 我是否朝着正确的方向前进?
- 我所设想的事情是否可能实现?
- 如果你们中有人遇到过这种情况,你们采用了什么策略?
We are connecting to Active Directory DCs to form a LdapContext/ DirContext to fetch the AD object Data.
当没有可用的缓存数据时,照常继续创建 LoginContext:
System.setProperty("java.security.krb5.conf","C:\\Windows\\krb5.ini");
System.setProperty("sun.security.jgss.debug","true");
LoginContext lc = null;
try {
lc = new LoginContext(LdapCtxGSSAPIEx.class.getName(),new LoginCallbackHandler(username,password));
// Attempt authentication
// You might want to do this in a "for" loop to give
// user more than one chance to enter correct username/password
lc.login();
} catch (LoginException le) {
System.err.println("Authentication attempt Failed" + le);
System.exit(-1);
}
解决方案 #1:
创建有效的 LoginContext
后,使用 GSSAPI 获取 ldap 的服务票证。我知道在 PrivilegedAction#run() 中形成 LdapContext 的另一种方式(实际上是我们当前的实现,并在解决方案#2 中显示)。但这对缓存没有帮助。因此,尝试使用 PoC 来存储服务票证而不是 TGT。 获取ldap服务票如下:
// servicePrincipalName = ldap/ad01.example.lab@EXAMPLE.LAB
GSSManager manager = GSSManager.getInstance();
GSSName serverName = manager.createName( servicePrincipalName,GSSName.NT_HOSTBASED_SERVICE);
final GSSContext context = manager.createContext( serverName,krb5OID,null,GSSContext.DEFAULT_LIFETIME);
// The GSS context initiation has to be performed as a privileged action.
byte[] serviceTicket = Subject.doAs( subject,new PrivilegedAction<byte[]>() {
public byte[] run() {
try {
byte[] token = new byte[0];
// This is a one pass context initialisation.
context.requestMutualAuth( false);
context.requestCredDeleg( false);
return context.initSecContext( token,token.length);
}
catch ( GSSException e) {
e.printstacktrace();
return null;
}
}
});
问题:
- 我如何使用此服务票据来形成
LdapContext
? - 我可以将此票证存储在文件中(编码),然后以相同的方式使用票证来形成 LdapContext 吗?
解决方案 #2: 在大多数教程和我们当前的实现中都可以看到创建 LdapContext:
DirContext ctx = Subject.doAs(lc.getSubject(),new JndiAction<DirContext>(args,providerURL ));
class JndiAction<DirContext> implements java.security.PrivilegedAction<DirContext> {
.......
public DirContext run() {
return performJndioperation(args,this.providerURL);
}
public DirContext performJndioperation(String[] args,String providerURL){
......
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
// Must use fully qualified hostname
env.put(Context.PROVIDER_URL,providerURL);
// Request the use of the "GSSAPI" SASL mechanism
// Authenticate by using already established Kerberos credentials
env.put(Context.Security_AUTHENTICATION,"GSSAPI");
// Request mutual authentication
env.put("javax.security.sasl.server.authentication","true");
DirContext ctx = new InitialDirContext(env);
return ctx;
......
}
.......
}
当上面代码中的这一行:DirContext ctx = new InitialDirContext(env);
被执行时:Subject
被填充了一个新的 PrivateCredential
,其服务主体为:ldap/ad01.example.lab@EXAMPLE.LAB
问题:我可以存储此服务票证以供进一步参考以创建 LdapContext 而不是一遍又一遍地执行所有身份验证步骤吗?
什么是委托凭证?他们会帮助解决我的问题吗?
更新: 为什么在缓存中存储服务票而不是 TGT? ---> 明确避免 kinit。存储服务票据将很容易适应我们当前的解决方案。此外,这将是一个临时解决方案,因为我们将根据客户的需要询问 krb.conf 文件,并免除创建 krb.conf 文件的责任。但现在,我们必须这样做!
请帮忙!
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)