Загрузка XML-файла с авторизацией в заголовке, ошибка 401

#delphi #post #http-status-code-401 #idhttp

#delphi #Публикация #http-status-code-401 #idhttp

Вопрос:

Я пытаюсь отправить XML-файл с помощью TIdHTTP.Post() метода. Я видел много примеров в вопросах здесь, на сайте, но я не нашел ни одного, который соответствовал бы тому, что я делаю.

Мне нужно сначала войти в веб-сервис, и он возвращает мне SessionID и UserAuth , а затем мне нужно отправить XML-файл, используя эту информацию.

Вот как я это делаю:

 var
  IdHTTP : TIdHTTP;
  params : TMemoryStream;
  vResponse : TStringStream;
  UserName, PassUser, URLogon, URSend, AuthUser: string;
  jsonObj, jSubObj: TJSONObject;
begin
  IdHTTP := TIdHTTP.Create;
  vResponse := TStringStream.Create('');
  params := TMemoryStream.Create;
  UserName := '123456';
  PassUser := '12345678';
  AuthUser := string(EncodeBase64(AnsiString(UserName ':' PassUser)));
  URLogon := 'http://svchomologacao.sigen.cidasc.sc.gov.br/Acesso/Login?authToken='   AuthUser;
  URSend := 'http://svchomologacao.sigen.cidasc.sc.gov.br/Receituario/Incluir';

  IdHTTP.Post(URLogon, params, vResponse);
  Memo1.Lines.Add(vResponse.DataString);

  //this is the return I get from WS, the SessionID and UserAuth in JSon:
  jsonObj := TJsonObject.ParseJSONValue(vResponse.DataString) as TJsonObject; //Json conversion

  params.LoadFromFile('D:Exemple.xml');

  IdHTTP.Request.CustomHeaders.Clear;
  IdHTTP.Request.Clear;
  IdHTTP.ConnectTimeout := 30000;
  IdHTTP.Request.BasicAuthentication := True;
  IdHTTP.Request.ContentType := 'application/xml';
  IdHTTP.Request.ContentEncoding := 'raw';
  IdHTTP.Request.Accept := 'application/xml';
  IdHTTP.Response.CharSet := 'UTF-8';
  IdHTTP.Request.UserAgent := 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; GTB5; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; Maxthon; InfoPath.1; .NET CLR 3.5.30729; .NET CLR 3.0.30618)';
  IdHTTP.Request.CustomHeaders.AddValue(jsonObj.Get(0).JsonString.Value, jsonObj.Get(0).JsonValue.Value);
  IdHTTP.Request.CustomHeaders.AddValue(jsonObj.Get(1).JsonString.Value, jsonObj.Get(1).JsonValue.Value);

  IdHTTP.Post(URSend, params, vResponse);
  ShowMessage(vResponse.DataString);
end;
  

Во втором сообщении я получаю ответ:

HTTP / 1.1 401 Неавторизованный


Ниже приводится выдержка из документации:

URL-адрес, где можно увидеть описание сервисов, является следующим: https://svchomologacao.sigen.cidasc.sc.gov.br

которая указывает на тестовую базу данных CIDASC, так что компании, предоставляющие ИТ-услуги для пестицидов, далее именуемые EIS, могут интегрировать свои программы через ВЕБ-службы.

После того, как интеграция HIA будет одобрена CIDASC, URL, который будет использоваться для отправки официальных данных, будет следующим:http://sigensvc.cidasc.sc.gov.br

где рецепты будут храниться в производственной базе данных CIDASC.

authToken — это комбинация (кодирование) в BASE64 (www.base64encode.org ) и в UTF-8 логин и пароль специалиста, выдавшего рецепт, разделенные двоеточием (:), как показано ниже:

пример: профессиональный пользователь: professionalpass

Из приведенного выше примера сгенерированный authToken будет выглядеть следующим образом: dXN1YXJpb2RvcHJvZmlzc2lvbmFsOnNlbmhhZG9wcm9maXNzaW9uYWw =

Чтобы установить соединение с сервером cidasc, для передачи предписаний конкретной EIS следует вызвать метод входа в систему службы доступа, как показано ниже:

svchomologacao.sigen.cidosc.sc.gov.br/Acesso/Login?authToken=xyz

where xyz will be the token obtained in step 2.

We remember that this method is POST, so that it will only operate via a call made by the software being developed by the EIS. For testing, existing programs may be used to
such as POSTMAN.

SEND THE RECIPIENTS TO CIDASC
After establishing the connection to the server, the EIS should call the Include method of the Receipt service, to send the recipients, as shown below:

svchomologacao.sigen.cidasc.sc.gov.br/Receituario/Incluir

From this point on, the system developed by the EIS individually, that is, it transmits the first one and waits for the response code of the method.

After changing HTTPS it was like this:

 var
  IdHTTP : TIdHTTP;
  sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
  vResponse : TStringStream;
  params : TMemoryStream;
begin
  IdHTTP := TidHTTP.Create;
  sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
  sshSocketHandler.SSLOptions.Method := sslvSSLv2;
  IdHTTP.IOHandler := sshSocketHandler;
  params := TMemoryStream.Create;
  vResponse := TStringStream.Create('');

  IdHTTP.Post('https://svchomologacao.sigen.cidasc.sc.gov.br/Acesso/Login?authToken=MzQ3MjA1Mzk2OjM4MjY1OTk5', params, vResponse);
  Memo1.Lines.Add(vResponse.DataString);
  

Теперь я получаю ошибку:

10054 Сброс соединения одноранговым узлом


Я связался со службой поддержки по электронной почте, и они сказали мне, что данные UserAuth и SessionID должны быть отправлены в файле cookie заголовка вызова include, и отправили пример на C #:

 public void InvokeService(string xml){


    HttpWebRequest requestReceituarioCancelar = (
        HttpWebRequest)WebRequest.CreateHttp(
        @"https://svchomologacao.sigen.cidasc.sc.gov.br/Receituario/Cancelar"
    );

    requestReceituarioCancelar.Headers.Clear();
    requestReceituarioCancelar.ContentType = "application/xml;charset="utf-8"";
    requestReceituarioCancelar.Accept = "application/xml";
    requestReceituarioCancelar.Method = "POST";

    XmlDocument SOAPReqBody = new XmlDocument();

    //ServiceResult: contains the login return (SessionID and UserAuth)

    var ServiceResult = StreamReceituarioLogin(requestLogin);

    var uri = new Uri("https://svchomologacao.sigen.cidasc.sc.gov.br");

    //Converts login return to json
    var retorno = JsonConvert.DeserializeObject<StructCookie>(ServiceResult); 

    //In the CookieContainer add the Uri containing the URL, SessionID and UserAuth 

    requestReceituarioCancelar.CookieContainer = new CookieContainer();
    requestReceituarioCancelar.CookieContainer.Add(uri, new Cookie("SessionID", retorno.SessionID));
    requestReceituarioCancelar.CookieContainer.Add(uri, new Cookie("UserAuth", retorno.UserAuth)); 

    SOAPReqBody.LoadXml(m);

    //Now it sends the requestReceituarioCancelar and the SOAPReqBody containing the XML
    Stream stream = null;
    using (stream = requestReceituarioCancelar.GetRequestStream())
    {
        SOAPReqBody.Save(stream);
        stream.Close();
    }
    using (WebResponse Serviceres = requestReceituarioCancelar.GetResponse())
    {
        using (StreamReader rd = new StreamReader(Serviceres.GetResponseStream()))
        {
            var ServiceResult = rd.ReadToEnd();
            rd.Close();
        }
    }
}
  

Но я не смог сделать то же самое в Delphi.

Комментарии:

1. Вы УВЕРЕНЫ, что должны отправлять SessionID и UserAuth в их собственных HTTP-заголовках, а не в HTTP Authentication заголовке или в строке запроса URL? У вас есть документация для REST API этого сервера, которой вы можете поделиться? Я также нахожу подозрительным, что сервер разрешил небезопасный AuthToken , не требуя HTTPS для его защиты. Любой злоумышленник MITM может таким образом украсть ваши учетные данные имени пользователя / пароля.

2. Предоставленная вами документация вообще не содержала никакой полезной информации относительно использования SessionID / UserAuth во втором СООБЩЕНИИ. И вы не должны использовать sslvSSLv2 . Это для SSL 2.0, который больше не является безопасным, и его больше никто не поддерживает. Вместо этого используйте sslvTLSv1 или выше.

Ответ №1:

Основываясь на электронном письме, которое вы получили от службы поддержки сервера, вам необходимо создать HTTP cookies для значений SessionID и UserAuth , а не создавать отдельные HTTP заголовки, как вы пытались изначально. Две совершенно разные вещи.

Для этой задачи можно использовать TIdCookieMananger компонент Indy, например:

 var
  IdHTTP : TIdHTTP;
  sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
  params : TMemoryStream;
  Response : string;
  UserName, PassUser, URLogon, URSend, URCookie, AuthUser: string;
  jsonObj: TJSONObject;
  Uri: TIdUri;
begin
  IdHTTP := TIdHTTP.Create;
  try
    sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
    sshSocketHandler.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
    IdHTTP.IOHandler := sshSocketHandler;

    IdHTTP.CookieManager := TIdCookieManager.Create(IdHTTP);
    IdHTTP.AllowCookies := True;

    UserName := '123456';
    PassUser := '12345678';
    AuthUser := string(EncodeBase64(AnsiString(UserName ':' PassUser)));

    URLogon := 'https://svchomologacao.sigen.cidasc.sc.gov.br/Acesso/Login?authToken='   AuthUser';
    URSend := 'https://svchomologacao.sigen.cidasc.sc.gov.br/Receituario/Incluir';
    URCookie := 'https://svchomologacao.sigen.cidasc.sc.gov.br';

    params := TMemoryStream.Create;
    try
      Response := IdHTTP.Post(URLogon, params);
      Memo1.Lines.Add(Response);

      //this is the return I get from WS, the SessionID and UserAuth in JSon:
      jsonObj := TJsonObject.ParseJSONValue(Response) as TJsonObject; //Json conversion
      try
        Uri := TIdUri.Create(URCookie);
        try
          IdHTTP.CookieManager.AddServerCookie('SessionID='   jsonObj.Values['SessionID'].Value, Uri);
          IdHTTP.CookieManager.AddServerCookie('UserAuth='   jsonObj.Values['UserAuth'].Value, Uri);
        finally
          Uri.Free;
        end;
      finally
        jsonObj.Free;
      end;

      params.LoadFromFile('D:Exemple.xml');

      IdHTTP.Request.Clear;
      IdHTTP.ConnectTimeout := 30000;
      IdHTTP.Request.Accept := 'application/xml';
      IdHTTP.Request.ContentType := 'application/xml';
      IdHTTP.Request.CharSet := 'utf-8';
      IdHTTP.Request.UserAgent := 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; GTB5; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; Maxthon; InfoPath.1; .NET CLR 3.5.30729; .NET CLR 3.0.30618)';

      Response := IdHTTP.Post(URSend, params);
      ShowMessage(Response);
    finally
      params.Free;
    end;
  finally
    IdHTTP.Free;
  end;
end;