Использование Android веб-сервиса navision

Мне нужно использовать веб-службу NAV с Android. Я получил кодовый модуль "Буквы" с функцией "Верхний".

Я настроил веб-службу NAV (аутентификация NTLM) на своем ПК и отключил брандмауэр (чтобы эта служба была видна на других устройствах в моей сети). Доступ к сервису можно получить в браузере (также на устройстве Android) после ввода логина/пароля (учетной записи моего ПК). Если я использую этот код, он падает при "вызове" с END_DOCUMENT нулевое исключение... Это из-за аутентификации?

Если я использую этот код для подключения

HttpGet request = new
HttpGet("http://[myPC'sIP]:7047/DynamicsNAV/WS/SystemService");
HttpResponse response = client.execute(request);

я получаю ошибку 401, но просто указываю IP

HttpGet request = new HttpGet("http://[myPC'sIP]");
HttpResponse response = client.execute(request);

возвращает код 200 (хорошо) Как я могу отправить учетные данные? Я пробовал несколько способов, но результат всегда один... У вас есть опыт в этом вопросе?


person user1390456    schedule 25.02.2013    source источник


Ответы (5)


Я использую soap_action для того же, и он работает как шарм, посмотрите, поможет ли вам приведенный ниже код:

String namespace = "urn:microsoft-dynamics-schemas/codeunit/NavisionWS";
String url = "http://IP:7047/DynamicsNAV/WS/Codeunit/NavisionWS";   
String soap_action = "urn:microsoft-dynamics-schemas/codeunit/NavisionWS:GetLoginInfo";
String method_name = "GetLoginInfo";
try
        {
              SoapObject request = new SoapObject(namespace, method_name);    
              SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

               envelope.dotNet = true;   
               envelope.setOutputSoapObject(request);   
               HttpTransportSE transport = new HttpTransportSE(url);    
               transport.call(soap_action, envelope); // Receive Error here!
                    SoapObject result = (SoapObject) envelope.getResponse();
                    great = result.toString();
        }
        catch (Exception e)
        {
            e.printStackTrace();
                 great = e.toString();
                 Toast.makeText(this, great, Toast.LENGTH_LONG).show();
        }
person Ravi_Parmar    schedule 21.09.2013
comment
Но разве он не запрашивает логин/пароль? Когда вы вводите IP:7037/DynamicsNAV/WS/SystemService в браузере, он запрашивает учетные данные. ... Под какой учетной записью работает ваш сервер NAV? - person user1390456; 25.09.2013
comment
Вы можете использовать java.net.Authenticator для этого;) - person Patrick Aleman; 28.11.2013

1) Добавьте банки ниже jcifs-1.3.17.jar ksoap2-android-assembly-3.0.0-jar-with-dependencies.jar

2) Создайте эти классы Java JCIFSEngine.java.

package com.demo.Authentication;
import jcifs.ntlmssp.NtlmFlags;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.util.Base64;
import org.apache.http.impl.auth.NTLMEngine;
import org.apache.http.impl.auth.NTLMEngineException;

import java.io.IOException;

/**
 * Class taken from http://hc.apache.org/httpcomponents-client-ga/ntlm.html
 */
public final class JCIFSEngine implements NTLMEngine {

    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;

    public String generateType1Msg(final String domain, final String workstation)
            throws NTLMEngineException {
        final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
        return Base64.encode(type1Message.toByteArray());
    }

    public String generateType3Msg(final String username, final String password,
            final String domain, final String workstation, final String challenge)
            throws NTLMEngineException {
        Type2Message type2Message;
        try {
            type2Message = new Type2Message(Base64.decode(challenge));
        } catch (final IOException exception) {
            throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
        }
        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,
                username, workstation, type3Flags);
        return Base64.encode(type3Message.toByteArray());
    }
}

NtlmTransport.java

package com.demo.Authentication;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeFactory;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.auth.NTLMScheme;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.ksoap2.HeaderProperty;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.ServiceConnection;
import org.ksoap2.transport.Transport;
import org.xmlpull.v1.XmlPullParserException;


public class NtlmTransport extends Transport {

    static final String ENCODING = "utf-8";

    private final DefaultHttpClient client = new DefaultHttpClient();
    private final HttpContext localContext = new BasicHttpContext();
    private String urlString;
    private String user;
    private String password;
    private String ntDomain;
    private String ntWorkstation;
    public static String AuthenticationCode;

    public NtlmTransport(String url, String user, String password,
            String domain, String workStation) {
this.urlString = url;
this.user = user;
this.password = password;
this.ntDomain = domain;
this.ntWorkstation = workStation;

}

    public void setCredentials(String url, String user, String password,
                               String domain, String workStation) {
        this.urlString = url;
        this.user = user;
        this.password = password;
        this.ntDomain = domain;
        this.ntWorkstation = workStation;

    }

    public List call(String targetNamespace, SoapEnvelope envelope, List headers)
            throws IOException, XmlPullParserException {
        return call(targetNamespace, envelope, headers, null);
    }

    public List call(String soapAction, SoapEnvelope envelope, List headers, File outputFile)
            throws IOException, XmlPullParserException {
        if (outputFile != null) {
            // implemented in HttpTransportSE if you are willing to port..
            throw new RuntimeException("Writing to file not supported");
        }
        HttpResponse resp = null;

        setupNtlm(urlString, user, password);

        try {
            // URL url = new URL(urlString);
            HttpPost httppost = new HttpPost(urlString);

            setHeaders(soapAction, envelope, httppost, headers);

            resp = client.execute(httppost, localContext);
            HttpEntity respEntity = resp.getEntity();


            InputStream is = respEntity.getContent();
            parseResponse(envelope, is);

        } catch (Exception ex) {
             ex.printStackTrace();
        }

        System.out.println("RESPONSE STATUS CODE :"+resp.getStatusLine().getStatusCode());
        if (resp != null) {
            return Arrays.asList(resp.getAllHeaders());
        } else {
            return null;
        }
    }

    private void setHeaders(String soapAction, SoapEnvelope envelope, HttpPost httppost, List headers) {
        byte[] requestData = null;
        try {
            requestData = createRequestData(envelope);
        } catch (IOException iOException) {
        }
        ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
        httppost.setEntity(byteArrayEntity);
        httppost.addHeader("User-Agent", org.ksoap2.transport.Transport.USER_AGENT);
        // SOAPAction is not a valid header for VER12 so do not add
        // it
        // @see "http://code.google.com/p/ksoap2-android/issues/detail?id=67
        if (envelope.version != SoapSerializationEnvelope.VER12) {
            httppost.addHeader("SOAPAction", soapAction);
        }

        if (envelope.version == SoapSerializationEnvelope.VER12) {
            httppost.addHeader("Content-Type", Transport.CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
        } else {
            httppost.addHeader("Content-Type", Transport.CONTENT_TYPE_XML_CHARSET_UTF_8);
        }

        // Pass the headers provided by the user along with the call
        if (headers != null) {
            for (int i = 0; i < headers.size(); i++) {
                HeaderProperty hp = (HeaderProperty) headers.get(i);
                httppost.addHeader(hp.getKey(), hp.getValue());
            }
        }
    }


    // Try to execute a cheap method first. This will trigger NTLM authentication
    public void setupNtlm(String dummyUrl, String userId, String password) {
        try {

            ((AbstractHttpClient) client).getAuthSchemes().register("ntlm", new NTLMSchemeFactory());

            NTCredentials creds = new NTCredentials(userId, password, ntWorkstation, ntDomain);
            client.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);

            HttpGet httpget = new HttpGet(dummyUrl);

            HttpResponse response1 = client.execute(httpget, localContext);
            HttpEntity entity1 = response1.getEntity();
            Header[] hArray = response1.getAllHeaders();
            int size = hArray.length;
            AuthenticationCode = String.valueOf(response1.getStatusLine().getStatusCode());
            System.out.println("AUTHENTICATION STATUS CODE :"+response1.getStatusLine().getStatusCode());
           /* for (int i = 0; i < size; i ++) {
                Header h = hArray[i];
                if (h.getName().equals("WWW-Authenticate")) {
                    entity1.consumeContent();
                    throw new Exception("Failed Authentication");
                }
            }*/

            entity1.consumeContent();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

        //NTLM Scheme factory
    private class NTLMSchemeFactory implements AuthSchemeFactory {
        public AuthScheme newInstance(final HttpParams params) {
        // see http://www.robertkuzma.com/2011/07/
        // manipulating-sharepoint-list-items-with-android-java-and-ntlm-authentication/
            return new NTLMScheme(new JCIFSEngine());
        }
    }

    public ServiceConnection getServiceConnection() throws IOException
    {
        throw new IOException("Not using ServiceConnection in transport");
    }

    public String getHost() {
        String retVal = null;
        try {
            retVal = new URL(url).getHost();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return retVal;
    }

    public int getPort() {
        int retVal = -1;
        try {
            retVal = new URL(url).getPort();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return retVal;
    }

    public String getPath() {
        String retVal = null;
        try {
            retVal = new URL(url).getPath();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return retVal;
    }
}

3) Вызовите этот метод с URL-адресом параметра, «имя пользователя», «пароль», «domainName», «SystemName».

 NtlmTransport ntlm = new NtlmTransport(url, "username", "password", "domainName","SystemName");

4) Отправить запрос мыла, который состоит из мыльного конверта.

 ntlm.call("namespace/methodname", soapEnvelope);
person i.n.e.f    schedule 24.01.2014

Решается созданием другой веб-службы (написанной на C#, работающей на том же ПК, где работает сервер NAV), которая считывает службу NAV, а Android взаимодействует с NAV WS через эту C# WS.

person user1390456    schedule 15.03.2013

Привет, ты пробовал использовать

var httpClient = new DefaultHttpClient();
NTCredentials nt = new NTCredentials("user", "pass", "", "domain");
httpClient.GetCredentialsProvider().SetCredentials(AuthScope.ANY, nt);
person pungggi    schedule 20.03.2013

Вот как HTTP-аутентификация должна работать. Вы можете использовать fiddler, чтобы увидеть, что делает ваш посредник. Если вы хотите избавиться от него, вам придется сделать то же самое :)

При этом аутентификация с помощью Navision не является пикником, поскольку она использует SPNEGO или NTLM. Если вы можете настроить NTLM, вы можете использовать android-ntlm для выполнения этой работы. Похоже на ответ punggi, за исключением httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());

person João Bragança    schedule 30.03.2013