2012-01-07 2 views
7

Я разрабатываю приложение для Android, которое обращается к веб-сервису Sharepoint и обрабатывает SOAP. Я пробовал различные способы, такие как JCIFS и т. Д. Ничего не работало.NTLM Authentication in Android

Может ли кто-нибудь помочь мне здесь? Я придумываю это с многих дней, но все, у кого есть эта проблема, расстроены.

Спасибо, Индраджит

ответ

5

Я не эксперт в NTLM, но я успешно подключился к нашему серверу с помощью библиотеки JCIFS и некоторую ручную работу с заголовками.

Я также использую библиотеку OkHttp 3 для сетевого подключения, но вы, вероятно, можете адаптировать мой код к другим библиотекам.

Основная идея заключается в том, что вам необходимо договориться с подключенным сервером.

Шаг 1:

При попытке подключения 1-й раз, когда вы будете потерпеть неудачу и получить некоторую информацию в заголовке:

WWW-Authenticate: Negotiate

WWW-Authenticate: NTLM

Шаг 2:

Вам необходимо сгенерировать ключ типа 1 (с дополнительными параметрами домена &) с помощью библиотеки jcifs и попытаться снова подключиться. Вы будете не раз, но получить некоторую полезную информацию в заголовке:

WWW-Authenticate: NTLM very_long_challenge_key

Шаг 3:

Вам необходимо сгенерировать ключ типа 3 с этой проблемой key + login + password, используя библиотеку jcifs. Тогда соединение будет успешным!

Теперь некоторый код, добавить зависимость к библиотекам в build.gradle файле вашего приложения:

compile files('libs/jcifs-1.3.18.jar') 
compile 'com.squareup.okhttp3:okhttp:3.4.1' 

Сосуд можно найти здесь: https://jcifs.samba.org/src/

Тогда класс NTLMAuthenticator

import android.support.annotation.NonNull; 

import java.io.IOException; 
import java.util.List; 

import jcifs.ntlmssp.NtlmFlags; 
import jcifs.ntlmssp.Type1Message; 
import jcifs.ntlmssp.Type2Message; 
import jcifs.ntlmssp.Type3Message; 
import jcifs.util.Base64; 
import okhttp3.Authenticator; 
import okhttp3.Credentials; 
import okhttp3.Request; 
import okhttp3.Response; 
import okhttp3.Route; 

/** 
* Created by Arnaud Guyon on 07.02.17. 
*/ 

public class NTLMAuthenticator implements Authenticator { 

    private static final int TYPE_1_FLAGS = 
      NtlmFlags.NTLMSSP_NEGOTIATE_56 | 
        NtlmFlags.NTLMSSP_NEGOTIATE_128 | 
        NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 | 
        NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | 
        NtlmFlags.NTLMSSP_REQUEST_TARGET; 

    private String mLogin; 
    private String mPassword; 
    private String mDomain; 
    private String mWorkstation; 

    public NTLMAuthenticator(@NonNull String login, @NonNull String password) { 
     this(login, password, "", ""); 
    } 

    public NTLMAuthenticator(@NonNull String login, @NonNull String password, @NonNull String domain, @NonNull String workstation) { 
     mLogin = login; 
     mPassword = password; 
     mDomain = domain; 
     mWorkstation = workstation; 
    } 

    @Override 
    public Request authenticate(Route route, Response response) throws IOException { 

     List<String> authHeaders = response.headers("WWW-Authenticate"); 
     if (authHeaders != null) { 
      boolean negociate = false; 
      boolean ntlm = false; 
      String ntlmValue = null; 
      for (String authHeader : authHeaders) { 
       if (authHeader.equalsIgnoreCase("Negotiate")) { 
        negociate = true; 
       } 
       if (authHeader.equalsIgnoreCase("NTLM")) { 
        ntlm = true; 
       } 
       if (authHeader.startsWith("NTLM ")) { 
        ntlmValue = authHeader.substring(5); 
       } 
      } 

      if (negociate && ntlm) { 
       String type1Msg = generateType1Msg(mDomain, mWorkstation); 
       String header = "NTLM " + type1Msg; 
       return response.request().newBuilder().header("Authorization", header).build(); 
      } else if (ntlmValue != null) { 
       String type3Msg = generateType3Msg(mLogin, mPassword, mDomain, mWorkstation, ntlmValue); 
       String ntlmHeader = "NTLM " + type3Msg; 
       return response.request().newBuilder().header("Authorization", ntlmHeader).build(); 
      } 
     } 

     if (responseCount(response) <= 3) { 
      String credential = Credentials.basic(mLogin, mPassword); 
      return response.request().newBuilder().header("Authorization", credential).build(); 
     } 

     return null; 
    } 

    private String generateType1Msg(@NonNull String domain, @NonNull String workstation) { 
     final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation); 
     byte[] source = type1Message.toByteArray(); 
     return Base64.encode(source); 
    } 

    private String generateType3Msg(final String login, final String password, final String domain, final String workstation, final String challenge) { 
     Type2Message type2Message; 
     try { 
      byte[] decoded = Base64.decode(challenge); 
      type2Message = new Type2Message(decoded); 
     } catch (final IOException exception) { 
      exception.printStackTrace(); 
      return null; 
     } 
     final int type2Flags = type2Message.getFlags(); 
     final int type3Flags = type2Flags 
       & (0xffffffff^(NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER)); 
     final Type3Message type3Message = new Type3Message(type2Message, password, domain, 
       login, workstation, type3Flags); 
     return Base64.encode(type3Message.toByteArray()); 
    } 

    private int responseCount(Response response) { 
     int result = 1; 
     while ((response = response.priorResponse()) != null) { 
      result++; 
     } 
     return result; 
    } 

} 

После этого при создании вашего OkHttpClient необходимо добавить аутентификатор:

OkHttpClient okHttpClient = new OkHttpClient.Builder() 
     .authenticator(new NTLMAuthenticator(login, password)) 
     // .some other init here if necessary 
     .build(); 

И затем выполняйте ваши запросы, как обычно.

+0

Не могли бы вы подробно остановиться на «делать ваши запросы, как обычно»? Я не знаком с OkHttp –

+1

. Здесь есть простые примеры для OkHttp: https://square.github.io/okhttp/#overview –

Смежные вопросы