Hen*_*rik 4 java kerberos delegation
我有一个Java Web应用程序,可以在Windows Active Directory环境中对客户端进行SPNEGO身份验证。为了验证用户身份,我们使用了很好的旧SPNEGO SourceForge项目中的代码。
String encodedAuthToken = (String) credentials;
LOG.debug("Encoded auth token: " + encodedAuthToken);
byte[] authToken = B64Code.decode(encodedAuthToken);
GSSManager manager = GSSManager.getInstance();
try {
Oid krb5Oid = new Oid("1.3.6.1.5.5.2");
GSSName gssName = manager.createName(_targetName, null);
GSSCredential serverCreds = manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME, krb5Oid, GSSCredential.INITIATE_AND_ACCEPT);
GSSContext gContext = manager.createContext(serverCreds);
if (gContext != null) {
while (!gContext.isEstablished()) {
authToken = gContext.acceptSecContext(authToken, 0, authToken.length);
}
if (gContext.isEstablished()) {
// Login succeeded!
String clientName = gContext.getSrcName().toString();
}
}
}
Run Code Online (Sandbox Code Playgroud)
身份验证效果很好,但我们还需要使用约束委派将用户凭据委派给后端服务(Exchange EWS)。在我们的广告中配置此功能时,看起来差别很小,但不是。请参阅: AD委托设置
此处描述了差异:msdn.microsoft.com/zh-cn/library/cc246080.aspx?f=255&MSPPError=-2147217396使用不受约束的委派,我们可以在调用后端服务时简单地使用可用的委派凭据,并且它将一切都很好:
GSSCredential delegatedCreds = gContext.getDelegCred()
SpnegoHttpURLConnection conn = new SpnegoHttpURLConnection(clientCreds);
Run Code Online (Sandbox Code Playgroud)
使用受约束的委派,我们无法访问用户TGT,并且似乎我们需要使用Java 8应该支持的MS-SFU(S4U2proxy)Kerberos扩展。我能找到的唯一示例就是这个示例:https : //github.com/ymartin59/java-kerberos-sfudemo(感谢Yves Martin!)
现在解决我的问题...验证之后,我基本上得到的是验证用户的用户名(请参见上面的代码中的“ clientName”)。
我们真的需要在这里使用S4U2self机制来模拟用户吗?客户端刚刚向我们发送了它的Kerberos服务票证(包裹在我无法解码的SPNEGO令牌中)。理想情况下,我们应该能够使用该服务票证和我自己的服务的TGT来验证用户身份(使用S4U2proxy机制)?但是我不知道如何。
因此,现在我想知道是否可以将我们的SPNEGO身份验证与S4U2proxy委托联系在一起?
非常感谢您对此的任何投入。
我对 Kerberos 约束委派进行了大量调查,最后我找到了使用 Java 执行此操作的正确方法。
域控制器上的设置
1) 无委派:不信任此帐户进行委派
您(服务用户)无法获得用户的委派凭据。这意味着您不能代表最终用户执行任何任务。您最多可以做的是接受来自用户(通常是浏览器)的传入票证,并通过将其传递给 KDC 来对其进行验证。作为响应,KDC 会告诉您此票证是针对哪个用户(或委托人)颁发的,但不会传递任何凭据。
2) 无约束委派:信任此帐户以委派给任何服务(仅限 Kerberos)
使用此选项,您(服务用户)可以获得用户的委派凭据。而且,你得到的是用户的TGT。使用此 TGT,您可以代表用户为任何服务请求 TGS(服务票证)。
3) 信任此帐户以委托给指定的服务(仅限 Kerberos)
在这里,您可以指定可以使用委派凭据的服务。这意味着启用此选项后,您将获得委托凭据,但是,您只能使用它们来获取指定服务的最终用户的 TGS。
另一个重要的点是,您必须拥有最终用户的 TGS(您的 Web 应用程序的最终用户的 TGS)。然后使用此 TGS,您可以向 KDC 请求最终用户的 TGS 以获取另一项服务。
4) 信任此帐户以委托给指定的服务(任何协议)
这也称为协议转换。在此选项中,您还需要指定可以代表用户向 KDC 请求 TGS 的服务。
您(服务用户)可以“模拟”最终用户,而无需最终用户提供任何类型的票证。您可以模拟任何用户,并获得指定服务的 TGS。 此选项对于无法进行最终用户交互的后台流程或日程安排非常有用。
Java 代码示例
1)获得委托凭证(在上述选项 2 和 3 中有用)
// ---------------------------------
// step 1: Login using service user credentials and get its TGT
// ---------------------------------
Subject subject = new Subject();
Krb5LoginModule krb5LoginModule = new Krb5LoginModule();
Map<String,String> optionMap = new HashMap<String,String>();
optionMap.put("keyTab", "c:\\ticket\\sapuser.keytab");
optionMap.put("principal", "HTTP/TEST"); // SPN you mapped to the service user while creating the keytab file
optionMap.put("doNotPrompt", "true");
optionMap.put("refreshKrb5Config", "true");
optionMap.put("useTicketCache", "true");
optionMap.put("renewTGT", "true");
optionMap.put("useKeyTab", "true");
optionMap.put("storeKey", "true");
optionMap.put("isInitiator", "true"); // needed for delegation
optionMap.put("debug", "true"); // trace will be printed on console
krb5LoginModule.initialize(subject, null, new HashMap<String,String>(), optionMap);
krb5LoginModule.login();
krb5LoginModule.commit();
// ---------------------------------
// Step 2: Use login context of this service user, accept the kerberos token (TGS) coming from end user
// ---------------------------------
public GSSCredential validateTicket(byte[] token) {
try {
return Subject.doAs(this.serviceSubject, new KerberosValidateAction(token));
}
catch (PrivilegedActionException e) {
throw new BadCredentialsException("Kerberos validation not successful", e);
}
}
private class KerberosValidateAction implements PrivilegedExceptionAction<GSSCredential> {
byte[] kerberosTicket;
public KerberosValidateAction(byte[] kerberosTicket) {
this.kerberosTicket = kerberosTicket;
}
@Override
public GSSCredential run() throws Exception {
byte[] responseToken = new byte[0];
GSSName gssName = null;
GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
while (!context.isEstablished()) {
responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
gssName = context.getSrcName();
if (gssName == null) {
throw new BadCredentialsException("GSSContext name of the context initiator is null");
}
}
//check if the credentials can be delegated
if (!context.getCredDelegState()) {
SecurityLogger.getLogger().error("Credentials can not be delegated. Please make sure that delegation is enabled for the service user. This may cause failures while creating Kerberized application.");
return null;
}
// only accepts the delegated credentials from the calling peer
GSSCredential clientCred = context.getDelegCred(); // in case of Unconstrained Delegation, you get the end user's TGT, otherwise TGS only
return clientCred;
}
}
// ---------------------------------
// Step 3: Initiate TGS request for another service using delegated credentials obtained in previous step
// ---------------------------------
private Object getServiceTicket(GSSCredential clientCred) throws PrivilegedActionException {
Object o = Subject.doAs(new Subject(), (PrivilegedExceptionAction<Object>) () -> {
GSSManager manager = GSSManager.getInstance();
Oid SPNEGO_OID = new Oid("1.3.6.1.5.5.2");
Oid KRB5_PRINCIPAL_OID = new Oid("1.2.840.113554.1.2.2.1");
GSSName servicePrincipal = manager.createName("HTTP/TEST", KRB5_PRINCIPAL_OID); // service to which the service user is allowed to delegate credentials
ExtendedGSSContext extendedContext = (ExtendedGSSContext) manager.createContext(servicePrincipal, SPNEGO_OID, clientCred, GSSContext.DEFAULT_LIFETIME);
extendedContext.requestCredDeleg(true);
byte[] token = new byte[0];
token = extendedContext.initSecContext(token, 0, token.length); // this token is the end user's TGS for "HTTP/TEST" service, you can pass this to the actual HTTP/TEST service endpoint in "Authorization" header.
return token;
});
return o;
}
Run Code Online (Sandbox Code Playgroud)
2) 获取模拟凭据(在上述选项 4 中很有用)
初始步骤与上面步骤 1 中提到的类似。您需要使用服务用户凭据登录。'run' 方法有一些小的变化,如下所示:
@Override
public GSSCredential run() throws Exception {
GSSName gssName = null;
GSSManager manager = GSSManager.getInstance();
GSSCredential serviceCredentials = manager.createCredential(GSSCredential.INITIATE_ONLY);
GSSName other = manager.createName("bhushan", GSSName.NT_USER_NAME, kerberosOid); // any existing user
GSSCredential impersonatedCredentials = ((ExtendedGSSCredential) serviceCredentials).impersonate(other);
return impersonatedCredentials;
}
}
Run Code Online (Sandbox Code Playgroud)
您可以看到在这种情况下我们不需要用户的 TGS。
代表用户为其他服务获取 TGS,与上面代码中给出的步骤 3 中提到的相同。只需传递这些 impersonatedCredentials 而不是 delegatedCredentials。
我希望这会有所帮助。
谢谢,
布山
| 归档时间: |
|
| 查看次数: |
1757 次 |
| 最近记录: |