2012-01-07 33 views
7

Tôi đang phát triển một ứng dụng Android truy cập Dịch vụ web Sharepoint và xử lý SOAP. Tôi đã thử nhiều cách khác nhau như JCIFS, v.v. Không có gì hiệu quả.Xác thực NTLM trong Android

Có ai giúp tôi ở đây không? Tôi googling nó từ nhiều ngày, nhưng tất cả những người có vấn đề này là thất vọng.

Cảm ơn, Indrajit

Trả lời

5

Tôi không phải là chuyên gia về NTLM nhưng tôi đã kết nối thành công với chương trình phụ trợ của chúng tôi bằng cách sử dụng thư viện JCIFS và một số công việc thủ công với tiêu đề.

Tôi cũng sử dụng thư viện OkHttp 3 để kết nối mạng, nhưng bạn có thể thích ứng mã của tôi với các thư viện khác.

Ý tưởng chính là bạn phải thương lượng với máy chủ để kết nối.

Bước 1:

Khi bạn cố gắng để kết nối lần 1 bạn sẽ thất bại và nhận một số thông tin trong tiêu đề:

WWW-Authenticate: Negotiate

WWW-Authenticate: NTLM

Bước 2:

Bạn cần tạo Khóa loại 1 (với tên miền tùy chọn & thông số máy trạm) bằng cách sử dụng thư viện jcifs và thử kết nối lại. Bạn sẽ thất bại một lần nữa nhưng nhận được một số thông tin hữu ích trong tiêu đề:

WWW-Authenticate: NTLM very_long_challenge_key

Bước 3:

Bạn cần phải tạo ra một khóa loại 3 với thách thức đó key + login + password, sử dụng thư viện jcifs. Sau đó kết nối sẽ thành công!

Bây giờ một số mã, thêm sự phụ thuộc vào các thư viện trong file build.gradle của ứng dụng của bạn:

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

Các jar có thể được tìm thấy ở đây: https://jcifs.samba.org/src/

Sau đó, lớp 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; 
    } 

} 

Sau đó, khi bạn tạo OkHttpClient, hãy thêm trình xác thực này:

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

Và sau đó thực hiện yêu cầu của bạn như bình thường.

+0

Bạn có thể giải thích chi tiết về "yêu cầu của bạn như thường lệ" không? Tôi không quen thuộc với OkHttp –

+1

Có những mẫu đơn giản cho OkHttp tại đây: https://square.github.io/okhttp/#overview –

Các vấn đề liên quan