【问题标题】:android consumption of navision web servicenavision web服务的android消费
【发布时间】:2013-02-10 20:07:24
【问题描述】:

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

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

【问题讨论】:

    标签: android service web navision dynamics-nav


    【解决方案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);
    

    【讨论】:

      【解决方案2】:

      我使用 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();
              }
      

      【讨论】:

      • 但它不要求登录/通过吗?当您在浏览器中输入“IP:7037/DynamicsNAV/WS/SystemService”时,它会要求我提供凭据...您的 NAV 服务器在哪个帐户下运行?
      • 你可以使用 java.net.Authenticator ;)
      【解决方案3】:

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

      也就是说,使用 Navision 进行身份验证并不简单,因为它使用 SPNEGO 或 NTLM。如果您可以配置 NTLM,您也许可以使用android-ntlm 来完成工作。除了httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());

      之外,看起来类似于 pungggi 的答案

      【讨论】:

        【解决方案4】:

        您好,您是否尝试过使用

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            猜你喜欢
            • 2016-03-17
            • 1970-01-01
            • 2014-12-01
            • 2013-09-22
            • 1970-01-01
            • 2010-09-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多