简体   繁体   中英

Call web service with authentication (login and password) from android app

From the past few days, I have been working on an Android code to call a local web service. I am using ksoap2 libraries for Android "ksoap2-android-assembly-2.5.7-jar-with-dependencies.jar" to call my SOAP web service created in .NET The issue is that before request for a particular function I need to authenticate a client using basic http request.

This is my code :

public String CallSoapConnexion(String login, String mdp) {
    SoapObject request = new SoapObject(nms, mth);
    PropertyInfo pi = new PropertyInfo();
    pi.setName("login");
    pi.setValue(login);
    pi.setType(String.class);
    request.addProperty(pi);
    pi = new PropertyInfo();
    pi.setName("mdp");
    pi.setValue(mdp);
    pi.setType(String.class);
    request.addProperty(pi);

    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
            SoapEnvelope.VER11);

    envelope.dotNet = true;

    envelope.setOutputSoapObject(request);

    HttpTransportSE httpTransport = new HttpTransportSE(urlHttp);

    List<HeaderProperty> headerList = new ArrayList<HeaderProperty>();

    headerList
            .add(new HeaderProperty("Authorization", "Basic "
                    + org.kobjects.base64.Base64.encode("login:pasword"
                            .getBytes())));

    Object response = null;
    try {

        httpTransport.call(act, envelope, headerList);
        response = envelope.getResponse();

    } catch (Exception ex) {

        return ex.toString;

    }
    return ex.toString;
}

But I get the following Exception :

org.xmlpull.v1.XmlPullParserException: expected: START_TAG {http://schemas.xmlsoap.org/soap/envelope/}Envelope (position:START_TAG <html>@2:7 in java.io.InputStreamReader@44f66cb0)

Ksoap2 gave me a lot of trouble, I ended up making my own parser using SAXparser wich is the fastest way to parse XML. This is how I did it.

First in a class I saved all the constant parameter the WebServices will needs. They are all public and static so I can access them anywhere in the project without having to instanciate the class. Let's call it MyConstants.class

//######################## LOGIN WEB SERVICE CONSTANTS
/**
 * This string constructs the SOAP Envelope you need to use to Use the DoLogin WebService.
 * Use this string with String.format in the following way.
 * @param sUserName (String)
 * @param sPassword (String)
 * @Example String env = String.format(WebserviceConsumer.LOGIN_ENVELOPE, username, password);
 * */
public static final String LOGIN_ENVELOPE = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
  "<soap:Body>" +
    "<Login xmlns=\"webServiceNamespace\">" +
      "<sUsername>%s</sUsername>" +
      "<sPassword>%s</sPassword>" +
    "</Login>" +
  "</soap:Body>" +
"</soap:Envelope>";
public static final String LOGIN_SOAP_ACTION = "webServiceNamespace/Login";
public static final String LOGIN_URL = "https://ws.yoururl.com/YourLoginPage.asmx?op=Login";
public static final int LOGIN = 100;

And this is the parser I "did". It's based on another parser I found, so I don't know if it's perfect, but it works very well.

[EDIT] I changed the parser after some time because SAX parser is not a good way to go. It is fast but it's not efficient, because it creates a lot of objects that then have to be collected by the Garbage Collector. Google recommends to use XMLPullParser to do this job. It's usage is even easier than that of SAX. [/EDIT]

package com.yourproject.parsers;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.os.Bundle;

/**Callback Example
 * @example
 * <?xml version="1.0" encoding="utf-8"?>
 * <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
 *     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 *     xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
 *     <soap:Body>
 *         <LoginResponse xmlns="https://ws.yoururl.com/" >
 *             <LoginResult>
 *                 <ROOT xmlns="" >
 *                     <PassportKey>
 *                         <Key>653215989827346254362544623544652321</Key>
 *                         <otherLoginInfo>585051</otherLoginInfo>
 *                     </PassportKey>
 *                 </ROOT>
 *             </LoginResult>
 *         </LoginResponse>
 *     </soap:Body>
 * </soap:Envelope>
 */

/**
 * The loginParser saves the parsed data in a bundle. Once the parsing is done, call the 
 * getResutl() function to get a bundle that has the login authentication result.
 * */
public class LoginParser extends DefaultHandler {
    private String foundData = "";
    private String authCode = "";
    private String otherInfo = "";
    private Bundle result;

    public synchronized void parse(InputStream is) throws ParserConfigurationException, SAXException, IOException{
        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = spf.newSAXParser();
        XMLReader xr = sp.getXMLReader();
        xr.setContentHandler(this);

        xr.parse(new InputSource(is));
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        foundData += new String(ch, start, length);
    }

    @Override
    public void startDocument() throws SAXException{
        // Nothing to do
    }

    @Override
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException{
        foundData = "";
    }

    @Override
    public void endElement(String namespaceURI, String localName, String qName) throws SAXException{
        if (qName.equals("Key")) {
            authCode = foundData;
        } else if (qName.equals("otherLoginInfo")){
            otherInfo = foundData;
        }
    }

    @Override
    public void endDocument() throws SAXException{
        // Nothing to do.
    }

    public Bundle getResult(){
        result = new Bundle();
        result.putString("authCode", authCode);
        result.putString("otherInfo", otherInfo);
        return result;
    }
}

And now the class that uses the parser. Once you have this you can call this anywhere in the project you like.

package com.yourproject.web;

import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;


import com.yourproject.parsers.LoginParser;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;

/**
 * Public class used to send a SOAP message to our Webservices.
 * 
 * Code from
 * http://mobileorchard.com/android-app-development-calling-web-services/
 * */
public class WebserviceCall{
    LoginParser lp;
    [... other parsers ...]


    [... other parsers implementation ...]

    public Bundle CallWebServiceParseAndReturn(String url, String soapAction, String envelope, int callerRequest) throws Exception {
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httpRequest = null;
        HttpResponse httpResponse = null;
        Bundle result = null;

        httpRequest = new HttpPost(url);
        if (envelope != null) {
            httpRequest.setEntity(new StringEntity(envelope));
            httpRequest.addHeader("Content-Type", "text/xml; charset=utf-8");
        }

        httpResponse = httpclient.execute(httpRequest);
        switch (callerRequest) {
        case MyConstants.LOGIN:
            lp = new LoginParser();
            lp.parse(httpResponse.getEntity().getContent());
            result = lp.getResult();
            break;
        case MyConstants.OTHERPARSERS:
        case default:
            break;
        return result;
    }
}

Finally, here is an example of how you use this in your project.

private void callWebserviceLogin(String userName, String password) {
    try{
        Bundle result = null;
        WebserviceCall wsCall = new WebserviceCall();
        String wsEnvelope = String.format(MyConstants.LOGIN_ENVELOPE, userName, password);
        result = wsCall.CallWebServiceParseAndReturn(MyConstants.LOGIN_URL, MyConstants.LOGIN_SOAP_ACTION, wsEnvelope, MyConstants.LOGIN);
        authenticationResult = result.getString("authCode");
    } catch (Exception e) {
        authenticationResult = "";
        Log.e("Login error", e.getMessage());
    }
}

If you see the envelope constant I have, there are some %s marks in the string. When I use String.format, it replaces each mark with the parameter you pass after the first one (wich is the string holding the markers) in order of appearance.

I know this is not what you where expecting, but I hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM