[Из песочницы] Добавляем Basic Auth в SOAP запрос средствами ksoap2-android

habr.png

Так получилось, что в рамках своей работы я связался с проектом по разработке приложения для общения Android и 1С. Быстрый поиск в интернете дал достаточно четкие инструкции и куски кода, которые очень быстро оформились в готовую программу, но запускаться она не хотела. Здесь я расскажу основные тонкости и способы их решения.

Первоисточники кода расписаны здесь и тут. Необходимо только подключить стороннюю библиотеку ksoap2-android, чтобы стали доступны классы SoapObject и HttpTransportSE. Старый проверенный способ скачать jar с официального репозитория и положить его в app/libs почему-то не увенчался успехом, и я стал смотреть, как подключить библиотеку используя современный Gradle. Почти на всех ресурсах было написано, что простое добавление

dependencies {
    compile 'com.google.code.ksoap2-android:ksoap2-android:3.6.1'
}


должно «подтягивать» нужные ресурсы. Но этого не произошло, т.к. сам ресурс не находится в стандартных репозиториях. Значит, надо указать, откуда его скачивать. Делается это добавлением в Gradle файл проекта следующих строк:

buildTypes {
    repositories {
        maven { url 'https://oss.sonatype.org/content/repositories/ksoap2-android-releases' }
    }
} 


После этих простых действий просто синхронизируем проект, и Gradle закачает все, что нужно.
Вот код, который начинает работать после вышеописанных процедур:

public class DataLoader extends AsyncTask {
    private static final String NAMESPACE = "namespace";
    private static final String URL = "http://host/wsdlAcceptor?wsdl";
    private static final String SOAP_ACTION = "http://host/wsdlAcceptor";
    private static final String METHOD_NAME = "testOperation";

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected String doInBackground(Void... params) {
        try {
            SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
            SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER10);
            request.addProperty("IsFirstRequest", true);
            envelope.setOutputSoapObject(request);
            HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
            androidHttpTransport.debug = true;
            try {
                androidHttpTransport.call(SOAP_ACTION, envelope);
                SoapObject resultsRequestSOAP = (SoapObject) envelope.bodyIn;
                System.out.println("Response::"+resultsRequestSOAP.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
    }
}


Не секрет, что работа с любой сетевой активностью должна делаться из отдельной асинхронной задачи. doInBackground — это стандартный метод AsyncTask. Если еще не знакомы, то вот здесь про него написано просто и очень понятно.

Сама MainActivity выглядит следующим образом:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DataLoader dl = new DataLoader();
        dl.execute();
    }
}


На этом приключения не заканчиваются. В моем случае запрос к серверу был закрыт Basic Auth, поэтому потребовалось куда-то вводить логин и пароль. И вот на этот вопрос однозначного ответа в интернете я не нашел. Во многих статьях был упомянут некий класс HttpTransportBasicAuthSE, в конструктор которого и передается логин с паролем. Но его было не найти в ksoap2-android, пришлось искать в интернете. Нашелся здесь. Привожу полный текст:

public class HttpTransportBasicAuthSE extends HttpTransportSE {
    private String username;
    private String password;

    /**
     * Constructor with username and password
     *
     * @param url
     *            The url address of the webservice endpoint
     * @param username
     *            Username for the Basic Authentication challenge RFC 2617
     * @param password
     *            Password for the Basic Authentication challenge RFC 2617
     */
    public HttpTransportBasicAuthSE(String url, String username, String password) {
        super(url);
        this.username = username;
        this.password = password;
    }

    public ServiceConnection getServiceConnection() throws IOException {
        ServiceConnectionSE midpConnection = new ServiceConnectionSE(url);
        addBasicAuthentication(midpConnection);
        return midpConnection;
    }

    protected void addBasicAuthentication(ServiceConnection midpConnection) throws IOException {
        if (username != null && password != null) {
            StringBuffer buf = new StringBuffer(username);
            buf.append(':').append(password);
            byte[] raw = buf.toString().getBytes();
            buf.setLength(0);
            buf.append("Basic ");
            org.kobjects.base64.Base64.encode(raw, 0, raw.length, buf);
            midpConnection.setRequestProperty("Authorization", buf.toString());
        }
    }
}


Полагаю, комментарии к коду излишни. Уточню только, что для применения HttpTransportBasicAuthSE достаточно просто изменить строку в DataLoader

HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);


на

HttpTransportBasicAuthSE androidHttpTransport = new HttpTransportBasicAuthSE(URL, "basicLogin", "authPassword");


и все начинает работать с авторизацией очень четко!

P.S.: не забывайте добавлять в Manifest разрешение на Internet


© Habrahabr.ru