Базовая аутентификация HTTP с объектами HTTPService в Adobe Flex/AIR
Я пытаюсь запросить ресурс HTTP, для которого требуются базовые заголовки авторизации, из приложения Adobe AIR. Я попытался вручную добавить заголовки к запросу, а также с помощью метода setRemoteCredentials(), чтобы установить их, но безрезультатно.
Вот код:
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
private function authAndSend(service:HTTPService):void
{
service.setRemoteCredentials('someusername', 'somepassword');
service.send();
}
private function resultHandler(event:ResultEvent):void
{
apiResult.text = event.result.toString();
}
private function resultFailed(event:FaultEvent):void
{
apiResult.text = event.fault.toString();
}
]]>
</mx:Script>
<mx:HTTPService id="apiService"
url="https://mywebservice.com/someFileThatRequiresBasicAuth.xml"
resultFormat="text"
result="resultHandler(event)"
fault="resultFailed(event)" />
<mx:Button id="apiButton"
label="Test API Command"
click="authAndSend(apiService)" />
<mx:TextArea id="apiResult" />
Однако стандартное диалоговое окно базовой аутентификации все еще появляется, запрашивая у пользователя его имя пользователя и пароль. У меня такое чувство, что я делаю это неправильно, но вся информация, которую я смог найти (Flex docs, блоги, Google и т. Д.), Либо не сработала, либо была слишком расплывчатой, чтобы помочь.
Любая чёрная магия, о, флекс гуру? Благодарю.
РЕДАКТИРОВАТЬ: Изменение setRemoteCredentials() на setCredentials() приводит к следующей ошибке ActionScript:
[MessagingError message='Authentication not supported on DirectHTTPChannel (no proxy).']
РЕДАКТИРОВАТЬ: Проблема решена, после некоторого внимания со стороны Adobe. Смотрите сообщения ниже для полного объяснения. Этот код будет работать для заголовков HTTP-аутентификации произвольной длины.
import mx.utils.Base64Encoder;
private function authAndSend(service:HTTPService):void
{
var encoder:Base64Encoder = new Base64Encoder();
encoder.insertNewLines = false; // see below for why you need to do this
encoder.encode("someusername:somepassword");
service.headers = {Authorization:"Basic " + encoder.toString()};
service.send();
}
8 ответов
Наконец, получил некоторое внимание от Adobe и получил ответ на этот вопрос. Проблема с длинными заголовками HTTP-аутентификации заключается в том, что по умолчанию класс Base64Encoder будет вводить символы новой строки каждые 72 символа. Очевидно, что это приводит к тому, что фрагмент строки, закодированной в base-64, интерпретируется как новый атрибут заголовка, что вызывает ошибку.
Вы можете исправить это, установив (в приведенном выше примере) encoder.insertNewLines = false; По умолчанию установлено значение true.
Я исправил приведенный выше код для работы с произвольно длинными строками аутентификации.
Ах. Боль, страдание. Явное страдание.
Несмотря на то, что вы выяснили, как добавить заголовок перед выполнением вызова, неприятная правда заключается в том, что где-то глубоко в пространстве интеграции с Flash и браузером ваши заголовки снова удаляются.
Из моего блога в прошлом году на http://verveguy.blogspot.com/
Итак, я раскрыл Истину. (Я думаю) Это более мучительно, чем можно себе представить
1/ Все запросы HTTP GET удаляются из заголовков. Его нет в стеке Flex, поэтому это, вероятно, базовая среда выполнения проигрывателя Flash
2/ Все запросы HTTP GET с типом содержимого, отличным от application/x-www-form-urlencoded
превращаются в POST-запросы
3/ Все запросы HTTP POST, которые не имеют фактических опубликованных данных, преобразуются в запросы GET. Смотри 1/ и 2/
4/ Все запросы HTTP PUT и HTTP DELETE превращаются в запросы POST. Похоже, это ограничение браузера, с которым застрял Flash Player. (?)
На практике это сводится к тому, что если вы хотите передавать заголовки во всех запросах, вы всегда должны использовать POST, и вы должны найти другой способ передать семантику операции, которую вы "действительно хотели". Сообщество Rails остановилось на прохождении ?_method=PUT/DELETE
как обходной путь для проблем браузера, лежащих в основе 4/
Так как Flash добавляет замечательную боль при удалении заголовка на GET, я также использую ?_method=GET
в качестве обходного пути для этого. Однако, поскольку это срабатывает на 3/, я передаю фиктивный объект в качестве закодированных данных POST. Это означает, что мой сервис должен игнорировать фиктивные данные на ?_method=GET
запрос.
Крайне важно на данный момент знать о 2/. Это потеряло кучу моего времени.
Я встроил всю эту обработку в новый класс RESTService с поддержкой разметки MXML, поэтому можно делать вид, что его нет на стороне клиента.
Надеюсь, это кому-нибудь поможет.
Это действительно помогло мне! Спасибо! Я использую Flex Builder 3
Одно замечание: заголовки свойств WebService доступны только для чтения. Поэтому я попытался использовать httpHeaders. Оно работает!
var encoder:Base64Encoder = new Base64Encoder();
encoder.insertNewLines = false;
encoder.encode("test:test");
sfWS.httpHeaders = {Authorization:"Basic " + encoder.toString()};
У меня была такая же проблема при использовании HTTP Basic Authenticated Webservice. Это мое решение; работает нормально
private function authAndSend(service:WebService):void
{
var encoder:Base64Encoder = new Base64Encoder();
encoder.insertNewLines = false;
encoder.encode("user:password");
service.httpHeaders = { Authorization:"Basic " + encoder.ToString() };
service.initialize();
}
использование
authAndSend(WebService( aWebServiceWrapper.serviceControl));
Методы setCredentials() и setRemoteCredentials() предназначены для использования со службами данных Flex/LiveCycle, поэтому они, вероятно, не применимы в вашем случае.
Это должно работать для вас. Мне удалось воспроизвести это поведение на моем сервере, и это исправление, похоже, сработало; все еще кажется немного странным, что это не более удобно для API, учитывая, насколько часто вы думаете, что это был вариант использования, но, тем не менее, я проверил и подтвердил, что это работает, учитывая действующий сертификат SSL:
private function authAndSend(service:HTTPService):void
{
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode("someusername:somepassword");
service.headers = {Authorization:"Basic " + encoder.toString()};
service.send();
}
Надеюсь, поможет! И спасибо за публикацию - я уверен, что рано или поздно столкнулся бы с этим.;)
Кроме того, чтобы другие люди не потратили 10 минут на то, чтобы понять, почему правильный пример не совсем работает, вам нужно импортировать пакет mx.utils.Base64Encoder, например:
import mx.utils.Base64Encoder;
В начале или где-то в области CDATA. Я новичок в гибкости, так что поначалу это было не совсем очевидно.
Вот как это делается.
import mx.utils.Base64Encoder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
var _oHttp:HTTPService = new HTTPService;
var sUsername:String = "theusername"
var sPassword:String = "thepassword";
var oEncoder:Base64Encoder = new Base64Encoder();
oEncoder.insertNewLines = false;
oEncoder.encode(sUsername + ":" + sPassword);
_oHttp.method = "POST";
_oHttp.headers = {Authorization:"Basic " + oEncoder.toString()};
Попробуйте использовать setCredentials, а не setRemoteCredentials и потерпите неудачу, используя Fiddler/Charles, чтобы выяснить, какие заголовки отправляются с запросом.