Ошибка создания токена SPNEGO

Я попытался сгенерировать токен, который можно использовать в качестве заголовка HTTP для аутентификации по URL-адресу HDFS WebHDFS и URL-адресу Oozie REST API. Я сослался на приведенный ниже URL-адрес, чтобы получить приведенный ниже код для создания токена Negotiate. https://www.ibm.com/support/knowledgecenter/en/SS7JFU_8.5.5/com.ibm.websphere.express.doc/ae/tsec_SPNEGO_token.html

public class TokenCreation {
  private static final String SPNEGO_OID = "1.3.6.1.5.5.2";
  private static final String KERBEROS_OID = "1.2.840.113554.1.2.2";
  public static byte[] genToken(String principal) {
    System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
    byte[] spnegoToken = new byte[0];
    try {
      Oid spnegoMechOid = new Oid(SPNEGO_OID);
      Oid krb5MechOid = new Oid(KERBEROS_OID);
      GSSCredential clientGssCreds = null;
      GSSManager manager = GSSManager.getInstance();
      GSSName gssUserName = manager.createName(principal, GSSName.NT_USER_NAME, krb5MechOid);
      clientGssCreds = manager.createCredential(gssUserName.canonicalize(krb5MechOid),
          GSSCredential.INDEFINITE_LIFETIME,
          krb5MechOid,
          GSSCredential.INITIATE_ONLY);
      clientGssCreds.add(gssUserName,
          GSSCredential.INDEFINITE_LIFETIME,
          GSSCredential.INDEFINITE_LIFETIME,
          spnegoMechOid, GSSCredential.INITIATE_ONLY);
      GSSName gssServerName = manager.createName(principal, GSSName.NT_USER_NAME);
      GSSContext clientContext = manager.createContext(gssServerName.canonicalize(spnegoMechOid),
          spnegoMechOid,
          clientGssCreds,
          GSSContext.DEFAULT_LIFETIME);
      // optional enable GSS credential delegation
      clientContext.requestCredDeleg(true);
      // create a SPNEGO token for the target server
      spnegoToken = clientContext.initSecContext(spnegoToken, 0, spnegoToken.length);
    } catch (GSSException e) {
      e.printStackTrace();
    }
    return spnegoToken;
  }

Но после запуска приведенного выше кода я всегда получал следующее приглашение:

2019-09-25 14:12:51 760 [INFO] [pool-2-thread-1] c.s.n.c.u.security.KrbUtils - after loginUserFromKeytab............AtoimcUser:HTTP/[email protected]
2019-09-25 14:12:51 760 [INFO] [pool-2-thread-1] c.s.n.app.oozie.OozieAppCaller - ->>>>>>User Name is HTTP/[email protected]
2019-09-25 14:12:51 760 [INFO] [pool-2-thread-1] c.s.n.app.oozie.OozieAppCaller - ->>>>>>Mode is KERBEROS
>>>KinitOptions cache name is /tmp/krb5cc_0
Kerberos username [root]: ^C^C^C
Kerberos password for root:

Вы можете увидеть в конце приведенного выше журнала вывода. «Имя пользователя Kerberos» всегда запрашивается для запроса имени пользователя.

Также я попытался вручную запустить kinit keytab. и вышеприведенный класс может успешно сгенерировать токен. Но запускать kinit вручную НЕ так, как я хотел.

Не могли бы вы помочь? Спасибо.


person JMS    schedule 25.09.2019    source источник
comment
Я провел поиск и обнаружил, что указанный выше класс TokenCreation НЕ может генерировать токен после запуска UserGroupInformation.loginUserFromKeytab?   -  person JMS    schedule 25.09.2019
comment
Не могли бы вы объяснить, почему вы возитесь с низкоуровневым управлением учетными данными Kerberos, в то время как собственный HTTP-клиент Java поддерживает SPNego из коробки? (другие HTTP-клиенты, например, от Apache, могут иметь ошибки)   -  person Samson Scharfrichter    schedule 25.09.2019
comment
Hadoop UserGroupInformation использует пользовательскую библиотеку, которая (частично) переопределяет стандартную Java-реализацию Kerberos (внутри JAAS) по ряду хороших и плохих причин (обе реализации ограничены, ненадежны и трудны для отладки) — и когда вы используете UGI для входа из keytab всегда создает частный билет, который не помещается в кеш билетов. Следовательно, вы не можете использовать его из JAAS.   -  person Samson Scharfrichter    schedule 25.09.2019
comment
Чтобы включить трассировку отладки в JAAS (выгружается в StdOut / StdErr), используйте -Dsun.security.krb5.debug=true и -Djava.security.debug=gssloginconfig,configfile,configparser‌​,logincontext ›› вывод НЕ красивый, как вы можете ожидать от криптографической библиотеки...   -  person Samson Scharfrichter    schedule 25.09.2019
comment
...а также -Dsun.security.spnego.debug=true при использовании собственного HTTP-клиента   -  person Samson Scharfrichter    schedule 25.09.2019
comment
PS: когда я упоминаю SPNego с собственным HTTP-клиентом Java, я имею в виду что-то вроде фрагмента кода в stackoverflow.com/questions/36287733/   -  person Samson Scharfrichter    schedule 25.09.2019
comment
PPS: для конфигурации JAAS, которая запрашивает создание частного билета Kerberos из keytab (в этом случае для JDBC, но это не имеет значения), см. stackoverflow.com/questions/42477466/   -  person Samson Scharfrichter    schedule 25.09.2019
comment
Предупреждение: если вы используете IBM JDK, синтаксис конфигурации JAAS отличается от стандартного синтаксиса, используемого OpenJDK и Oracle.   -  person Samson Scharfrichter    schedule 25.09.2019
comment
@SamsonScharfrichter Большое спасибо, так как я новичок в Kerberos, и по исторической причине я не могу использовать собственный HTTP-клиент Java в нашем коде, а могу только получить токен spnego в качестве заголовка HTTP для отправки запросов. Я проверил ваш пример выше, используя метод doAs UserGroupInformation для получения куста доступа, но знаете ли вы, как я могу получить токен, который можно использовать для доступа к URL-адресу webhdfs kerberos (XXXX:50070/..) от ugi?   -  person JMS    schedule 26.09.2019
comment
У меня уже есть доступ к файлу keytab и использование UserGroupInformation.loginUserFromKeytabAndReturnUGI(userCode,keytabPath);   -  person JMS    schedule 26.09.2019
comment
PS: я пытаюсь получить токен из UGI и прикрепить его как webhdfs rest api http‹ip:50070/webhdfs/v1/..›заголовок HTTP-запроса, например Authorization: Negotiate ‹Token›, чтобы перечислить удаленные файлы hdfs, не уверен если этот способ может работать. Так много. @СамсонШарфрихтер   -  person JMS    schedule 26.09.2019


Ответы (1)


К сожалению, поддержка Kerberos и SPNEGO в Java громоздка.

Я создал небольшую библиотеку, чтобы упростить некоторые варианты использования Kerberos: https://github.com/bedrin/kerb4j Вы можете использовать его для генерации токена SPNEGO следующим образом:

SpnegoClient spnegoClient = SpnegoClient.loginWithKeyTab("svc_consumer", "/opt/myapp/consumer.keytab");
URL url = new URL("http://api.provider.acme.com/api/operation1");
SpnegoContext context = spnegoClient.createContext("http://provider.acme.com"); // Will result in HTTP/provider.acme.com SPN
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Authorization", context.createTokenAsAuthroizationHeader());
person bedrin    schedule 26.09.2019
comment
Извините, я новичок в kerberos, если я хочу получить доступ к URL-адресу httpfs, например. http‹://ip_address:50070/webhdfs/v1/user/hive/data/partition=1?&op=LISTSTATUS›, какая часть URL-адреса в вашем коде должна быть заменена? Могу я узнать, что означает http‹://provider.acme.com›? спасибо - person JMS; 26.09.2019
comment
Поскольку по историческим причинам я должен полагаться на исходный клиент из Джерси в своем проекте, поэтому я просто хочу извлечь токен, а затем добавить его в заголовок запроса, например Authorization: Negotiate ‹Token›, не уверен, что он работает. - person JMS; 26.09.2019
comment
context.createTokenAsAuthroizationHeader() вернет вам токен. Чтобы сгенерировать его, вам необходимо указать имя целевого хоста, которое зарегистрировано на сервере Kerberos (Active Directory?) — в этом смысл provider.acme.com - person bedrin; 26.09.2019
comment
Я использовал ваш способ для генерации токена, но обнаружил, что только один экземпляр в моем коде может успешно пройти авторизацию, но другие всегда возвращают «Запрос является повтором 34, у вас есть идея, как это исправить? спасибо - person JMS; 07.10.2019