3

我需要使用来自 android 的 NAV Web 服务。我得到了带有“上”功能的“字母”代码单元。

我已经在我的 PC 上设置了 NAV Web 服务(NTLM 身份验证)并关闭了防火墙(以便该服务在我的网络中的其他设备上可见)。输入登录名/密码(我的 PC 帐户)后,可以在浏览器(也可以在 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 (ok) 如何发送凭据?我尝试了几种方法,但结果总是一样的......你有这个问题的经验吗?

4

5 回答 5

1

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,"username","password","domainName","SystemName" 调用此方法

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

4) 发送包含肥皂信封的肥皂请求。

 ntlm.call("namespace/methodname", soapEnvelope);
于 2014-01-24T07:52:50.003 回答
1

我使用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();
        }
于 2013-09-21T12:22:11.447 回答
0

嗨,你试过用吗

var httpClient = new DefaultHttpClient();
NTCredentials nt = new NTCredentials("user", "pass", "", "domain");
httpClient.GetCredentialsProvider().SetCredentials(AuthScope.ANY, nt);
于 2013-03-20T20:42:28.897 回答
0

这就是 http 身份验证的工作方式。您可以使用 fiddler 来查看您的中介在做什么。如果你想摆脱它,你将不得不做同样的事情:)

话虽如此,使用 Navision 进行身份验证并不是一件容易的事,因为它使用的是 SPNEGO 或 NTLM。如果您可以配置 NTLM,您也许可以使用android-ntlm来完成工作。看起来类似于 pungggi 的答案,除了httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());

于 2013-03-30T07:25:45.470 回答
0

通过创建另一个 Web 服务(用 C# 编写,在运行 NAV 服务器的同一台 PC 上运行)来解决,该服务读取 NAV 服务并且 android 通过此 C# WS 与 NAV WS 通信

于 2013-03-15T11:48:16.827 回答