ServiceModel
От: binks Россия  
Дата: 26.01.24 05:04
Оценка:
Привет.

Есть сервис, предоставляющий wsdl, генерирую по нему ServiceModel.

Обмен по https, для аутентификации используется Bearer токен.

var timeout = new TimeSpan(0, 20, 0);
var address = new EndpointAddress(URL);

BasicHttpsBinding binding = new()
{
    ReaderQuotas = XmlDictionaryReaderQuotas.Max,
    MaxReceivedMessageSize = int.MaxValue,
    SendTimeout = timeout,
    ReceiveTimeout = timeout,
    UseDefaultWebProxy = UseSystemProxy
};
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; // можно поставить Basic и заполнить UserName и Password, но это ничего не меняет

_client = new Client(binding, address);

(IContextChannel)_client.InnerChannel).OperationTimeout = timeout;
_client.Endpoint.AddCustomHeader("Bearer " + Token); // до IClientMessageInspector.BeforeSendRequest в данном случае не доходит

// где-то дальше
_client.Request();

// System.ServiceModel.Security.MessageSecurityException: 'Запрос HTTP не разрешен для схемы аутентификации клиента "Anonymous". От сервера получен заголовок аутентификации "Basic realm="Realm", Basic realm="Realm"".'


Если закомментировать
(IContextChannel)_client.InnerChannel).OperationTimeout = timeout;

то попадаем в IClientMessageInspector.BeforeSendRequest и я устанавливаю в заголовок токен, но дальше сервер не отвечает и спустя минуту вылетает исключение
System.TimeoutException: 'Истекло время ожидания канала запроса при ожидании ответа по истечении 00:18:59.1452021. Увеличьте значение времени ожидания, передаваемое при вызове Request, или значение SendTimeout в Binding. Время, выделенное для выполнения этой операции, может быть составной частью более длительного времени ожидания.'


Непонятно откуда взялось "00:18:59.1452021"

В итоге не понятно что вообще происходит:
1. Почему, если я трогаю (IContextChannel)_client.InnerChannel).OperationTimeout, не обязательно сюда что-то писать (значение по умолчанию 10 минут), можно просто прочитать, то падает с System.ServiceModel.Security.MessageSecurityException: 'Запрос HTTP не разрешен для схемы аутентификации клиента "Anonymous". От сервера получен заголовок аутентификации "Basic realm="Realm", Basic realm="Realm"".'
2. Если не трогать OperationTimeout, то сервис не отвечет. Но можно сделать запрос из SoapUI и всё быстро отрабатывает. Что интересно, мой сервис тоже иногда может нормально отработать.
Re: ServiceModel
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.01.24 05:47
Оценка:
Здравствуйте, binks, Вы писали:

А что видно в трафике между клиентом и сервером? Я бы начал с запуска fiddlertool и посмотрел на сами запросы.
Уже потом, наверное, включил бы трассировку в дотнете и посмотрел, что там происходит.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: ServiceModel
От: binks Россия  
Дата: 26.01.24 06:29
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А что видно в трафике между клиентом и сервером? Я бы начал с запуска fiddlertool и посмотрел на сами запросы.

S>Уже потом, наверное, включил бы трассировку в дотнете и посмотрел, что там происходит.

Смотрел через WireShark, но не разобрался как декодировать Data, там же https
Отредактировано 26.01.2024 6:33 binks . Предыдущая версия .
Re[3]: ServiceModel
От: Qulac Россия  
Дата: 26.01.24 07:36
Оценка:
Здравствуйте, binks, Вы писали:

B>Здравствуйте, Sinclair, Вы писали:


S>>А что видно в трафике между клиентом и сервером? Я бы начал с запуска fiddlertool и посмотрел на сами запросы.

S>>Уже потом, наверное, включил бы трассировку в дотнете и посмотрел, что там происходит.

B>Смотрел через WireShark, но не разобрался как декодировать Data, там же https

B>Image: sNyr94CidEb

fiddler лучше использовать если https.
Программа – это мысли спрессованные в код
Re[3]: ServiceModel
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.01.24 08:21
Оценка:
Здравствуйте, binks, Вы писали:
B>Смотрел через WireShark, но не разобрался как декодировать Data, там же https
B>Image: sNyr94CidEb
Смотрите через fiddlertool, в него декодирование https встроено из коробки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: ServiceModel
От: binks Россия  
Дата: 26.01.24 08:53
Оценка:
Здравствуйте, Sinclair, Вы писали:

Если указать так
binding.ProxyAddress = new Uri("localhost:8888");


то получаю

System.NotSupportedException: Only the 'http', 'socks4', 'socks4a' and 'socks5' schemes are allowed for proxies.


Ещё пробовал в консоли сделать так
netsh winhttp set proxy 127.0.0.1:8888
но тоже безрезультатно.
Re[5]: ServiceModel
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.01.24 09:39
Оценка:
Здравствуйте, binks, Вы писали:
B>но тоже безрезультатно.
Посмотрите советы из
https://stackoverflow.com/questions/4902281/can-fiddler2-be-used-to-monitor-soap-requests-made-in-visual-studio-debugging-se
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: ServiceModel
От: binks Россия  
Дата: 26.01.24 10:02
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Посмотрите советы из

S>https://stackoverflow.com/questions/4902281/can-fiddler2-be-used-to-monitor-soap-requests-made-in-visual-studio-debugging-se

Не подходит. Я нашёл только плохое

ЗЫ: У меня .net7
Re: ServiceModel
От: Sharov Россия  
Дата: 26.01.24 11:58
Оценка:
Здравствуйте, binks, Вы писали:

B>Привет.


B>Есть сервис, предоставляющий wsdl, генерирую по нему ServiceModel.


B>Обмен по https, для аутентификации используется Bearer токен.


B>
B>var timeout = new TimeSpan(0, 20, 0);
B>var address = new EndpointAddress(URL);

B>BasicHttpsBinding binding = new()
B>{
B>    ReaderQuotas = XmlDictionaryReaderQuotas.Max,
B>    MaxReceivedMessageSize = int.MaxValue,
B>    SendTimeout = timeout,
B>    ReceiveTimeout = timeout,
B>    UseDefaultWebProxy = UseSystemProxy
B>};
B>binding.Security.Mode = BasicHttpsSecurityMode.Transport;
B>binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; // можно поставить Basic и заполнить UserName и Password, но это ничего не меняет

B>_client = new Client(binding, address);

B>(IContextChannel)_client.InnerChannel).OperationTimeout = timeout;
B>_client.Endpoint.AddCustomHeader("Bearer " + Token); // до IClientMessageInspector.BeforeSendRequest в данном случае не доходит

B>// где-то дальше
B>_client.Request();

B>// System.ServiceModel.Security.MessageSecurityException: 'Запрос HTTP не разрешен для схемы аутентификации клиента "Anonymous". От сервера получен заголовок аутентификации "Basic realm="Realm", Basic realm="Realm"".'
B>


B>Если закомментировать

B>
B>(IContextChannel)_client.InnerChannel).OperationTimeout = timeout;
B>

B>то попадаем в IClientMessageInspector.BeforeSendRequest и я устанавливаю в заголовок токен, но дальше сервер не отвечает и спустя минуту вылетает исключение
B>
B>System.TimeoutException: 'Истекло время ожидания канала запроса при ожидании ответа по истечении 00:18:59.1452021. Увеличьте значение времени ожидания, передаваемое при вызове Request, или значение SendTimeout в Binding. Время, выделенное для выполнения этой операции, может быть составной частью более длительного времени ожидания.'
B>


Это же кажется WCF? Если да, тогда советую выкрутить все логи по максимуму.
Кодом людям нужно помогать!
Re: ServiceModel
От: · Великобритания  
Дата: 26.01.24 12:04
Оценка: 1 (1) +1
Здравствуйте, binks, Вы писали:

B>_client.Endpoint.AddCustomHeader("Bearer " + Token); // до IClientMessageInspector.BeforeSendRequest в данном случае не доходит

Я не очень понял что это за API, но я нигде не вижу имени хедера. Bearer должен выглядеть так:
Authorization: Bearer <token>
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 26.01.2024 12:05 · . Предыдущая версия .
Re[7]: ServiceModel
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.01.24 12:12
Оценка:
Здравствуйте, binks, Вы писали:
B>Не подходит. Я нашёл только плохое
B>ЗЫ: У меня .net7

Печаль. Похоже, всё устарело с обеих сторон. Посмотрим, может более опытные коллеги чего посоветуют.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: ServiceModel
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.01.24 12:14
Оценка:
S>Печаль. Похоже, всё устарело с обеих сторон. Посмотрим, может более опытные коллеги чего посоветуют.
P.S.
https://learn.microsoft.com/en-us/dotnet/framework/network-programming/how-to-configure-network-tracing
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: ServiceModel
От: binks Россия  
Дата: 26.01.24 13:48
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, binks, Вы писали:


B>>_client.Endpoint.AddCustomHeader("Bearer " + Token); // до IClientMessageInspector.BeforeSendRequest в данном случае не доходит

·>Я не очень понял что это за API, но я нигде не вижу имени хедера. Bearer должен выглядеть так:
·>
·>Authorization: Bearer <token>
·>

Я не обратил внимание, что тут криво сделано. Там внутри как раз заполняется заголовок Authorization.

Это SOAP API

Очень странно, но я не нашёл другого способа добавить авторизацию с bearer токеном, кроме как через интерфейс интерцептера.
Re: ServiceModel
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 28.01.24 08:38
Оценка:
Здравствуйте, binks, Вы писали:

За давностью лет мог уже многое позабыть, так что отвечу как и что вспомню....
B>Непонятно откуда взялось "00:18:59.1452021"
Ну т.к. это ~19 минут, а вы сказали, что ошибку получаете через примерно минуту, то логично что это выставленный вами timout == 20 минут, минус минута ожидания.
Тут, конечно сообщения с такой формулировкой, что кого угодно собьет с толку... Но в целом получается так:
— вы в биндинге указываете SendTimeout и ReceiveTimeout по 20 минут. Это время на выполнение целиком операции — с того момента, как вы начали запрос, до получения ответа.
— однако у вас всё обрывается уже через минуту. Это, как я понимаю, таймаут транспорта. Т.е. ваш нижележащий уровень не смог за минуту установить соединение и вернул ошибку.
Ну и в результате у вас осталось еще 19 минут на операцию, но вы получили таймаут от канального уровня.

B>1. Почему, если я трогаю (IContextChannel)_client.InnerChannel).OperationTimeout, не обязательно сюда что-то писать (значение по умолчанию 10 минут), можно просто прочитать, то падает с System.ServiceModel.Security.MessageSecurityException: 'Запрос HTTP не разрешен для схемы аутентификации клиента "Anonymous". От сервера получен заголовок аутентификации "Basic realm="Realm", Basic realm="Realm"".'


Потому что у WCF конструирование Pipeline, т.е. всего — от канала до прикладного уровня происходит, обычно, когда вы только делаете первый запрос (и после этого он становится read-only и там почти ничего менять уже нельзя)
Причем, и все проверки на то, на сколько вы указали совместимые между собой параметры происходят только в этот момент (ну не совсем так — что-то и прямо в момент создания, например, биндинга проверится — но там самый минимум... типа не передали ли вы пустой адрес или что-то в этом роде).

Но в некоторых случаях конструирование, а значит и проверки, можно вызвать, если обратиться к некоторым внутренним структурам. Ну примерно как у вас. Вы обращаетесь к внутреннему каналу, а чтобы его создать, нужно вызвать Binder, который уже начнет всё проверять... Ну вывалит вам ошибку.

Теперь о самой ошибке.
Там вроде тоже всё прозрачно. Вы говорите, что у вас безопасность на уровне транспорта (binding.Security.Mode = BasicHttpsSecurityMode.Transport), но аутентификации нет (binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None).
Это недопустимая комбинация.
Более того, вы писали в комментарии, что установка HttpClientCredentialType.Basic ничего не меняет. Логично, если вы хотите безопасность на уровне транспорта, нужно работать по HTTPS, других вариантов WCF знать не хочет. Проверьте, что вы в качестве Endpoint указываете схему с https. Если явно не указываете — проверьте базовый адрес.

Вообще вы хотите не очень подходящий для WCF сценарий — свою кастомную аутентификацию на уровне транспорта.
Посмотрите, вот тут https://learn.microsoft.com/en-us/archive/blogs/carlosfigueira/wcf-extensibility может быть что-то вам подойдет.

Наверное, самое простое, хоть и костыльное, решение, будет:
— выключить безопасность вообще (None) или оставить TransportWithMessageCredential (но тогда проверить, что работает по HTTPS).
— прописать свой Authorization заголовок. Например, вот так https://stackoverflow.com/questions/964433/how-to-add-a-custom-http-header-to-every-wcf-call (тот пример, что у вас — это добавление заголовка к SOAP сообщению, это не то.
Re[9]: ServiceModel
От: binks Россия  
Дата: 29.01.24 05:52
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>Печаль. Похоже, всё устарело с обеих сторон. Посмотрим, может более опытные коллеги чего посоветуют.

S>P.S.
S>https://learn.microsoft.com/en-us/dotnet/framework/network-programming/how-to-configure-network-tracing

С трейсингом тоже проблемы возникли, не получается его включить.
Был бы рад готовым примерам.
Re[2]: ServiceModel
От: binks Россия  
Дата: 29.01.24 06:17
Оценка:
Здравствуйте, Михаил Романов, Вы писали:

МР>Ну и в результате у вас осталось еще 19 минут на операцию, но вы получили таймаут от канального уровня.

Похоже на правду.

МР>Но в некоторых случаях конструирование, а значит и проверки, можно вызвать, если обратиться к некоторым внутренним структурам. Ну примерно как у вас. Вы обращаетесь к внутреннему каналу, а чтобы его создать, нужно вызвать Binder, который уже начнет всё проверять... Ну вывалит вам ошибку.

Похоже на правду.

МР>Теперь о самой ошибке.

МР>Там вроде тоже всё прозрачно. Вы говорите, что у вас безопасность на уровне транспорта (bind

ing.Security.Mode = BasicHttpsSecurityMode.Transport), но аутентификации нет (binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None).
МР>Это недопустимая комбинация.
Почему недопустимая?
Я так понимаю, что безопасность на уровне транспорта это ssl. А будет ли доступ к ресурсам регулироваться паролем/сертификатом или ещё чем-то — не важно.

МР>Более того, вы писали в комментарии, что установка HttpClientCredentialType.Basic ничего не меняет. Логично, если вы хотите безопасность на уровне транспорта, нужно работать по HTTPS, других вариантов WCF знать не хочет. Проверьте, что вы в качестве Endpoint указываете схему с https. Если явно не указываете — проверьте базовый адрес.

Не меняется, если я трогаю InnerChannel.

МР>Вообще вы хотите не очень подходящий для WCF сценарий — свою кастомную аутентификацию на уровне транспорта.

МР>Посмотрите, вот тут https://learn.microsoft.com/en-us/archive/blogs/carlosfigueira/wcf-extensibility может быть что-то вам подойдет.
МР>Наверное, самое простое, хоть и костыльное, решение, будет:
МР>- выключить безопасность вообще (None) или оставить TransportWithMessageCredential (но тогда проверить, что работает по HTTPS).
МР>- прописать свой Authorization заголовок. Например, вот так https://stackoverflow.com/questions/964433/how-to-add-a-custom-http-header-to-every-wcf-call (тот пример, что у вас — это добавление заголовка к SOAP сообщению, это не то.

Добавление заголовка в soap происходит именно при TransportWithMessageCredential, потому-что получаю ошибку
System.ServiceModel.Security.MessageSecurityException: 'Незащищенное или неправильно защищенное сообщение об ошибке было получено от другой стороны. Код ошибки и описание см. внутреннее исключение.'
Inner Exception: One or more mandatory SOAP header blocks not understood


Всё-таки BasicHttpsSecurityMode.Transport + мой_client.Endpoint.AddCustomHeader (аналогично через интерсептер как по ссылке https://stackoverflow.com/questions/964433/how-to-add-a-custom-http-header-to-every-wcf-call) — правильная комбинация.

Склоняюсь к тому, что это либо что-то внтурях WCF, либо ошибка сервера. Обмен какой-то идёт, но как его расшифровать не ясно. Да трассировку WCF включить не получается.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.