VS 2022 Добавить ссылку на службу, не генерирующую ClientCredentials для .Net Framework

Мне нужно добавить ссылку на службу, чтобы создать клиентский прокси для службы .Net Framework и службы .Net 8. Справочник по добавлению службы .Net 8 правильно генерирует клиентский прокси и выполняется без проблем. В отдельном проекте или службе на основе Framework созданный прокси-сервер не включает ClientCredentials.

Для следующей политики из wsdl:

  <wsp:Policy wsu:Id = "WSHttpBinding_IQuery_2012_04_policy">
    <wsp:ExactlyOne>
      <wsp:All>
        <sp:TransportBinding xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
          <wsp:Policy>
            <sp:TransportToken>
              <wsp:Policy>
                <sp:HttpsToken RequireClientCertificate = "false" />
              </wsp:Policy>
            </sp:TransportToken>
            <sp:AlgorithmSuite>
              <wsp:Policy>
                <sp:Basic256 />
              </wsp:Policy>
            </sp:AlgorithmSuite>
            <sp:Layout>
              <wsp:Policy>
                <sp:Strict />
              </wsp:Policy>
            </sp:Layout>
            <sp:IncludeTimestamp />
          </wsp:Policy>
        </sp:TransportBinding>
        <sp:SignedSupportingTokens xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
          <wsp:Policy>
            <sp:UsernameToken sp:IncludeToken = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
              <wsp:Policy>
                <sp:WssUsernameToken10 />
              </wsp:Policy>
            </sp:UsernameToken>
          </wsp:Policy>
        </sp:SignedSupportingTokens>
        <sp:Wss11 xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
          <wsp:Policy />
        </sp:Wss11>
        <sp:Trust10 xmlns:sp = "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
          <wsp:Policy>
            <sp:MustSupportIssuedTokens />
            <sp:RequireClientEntropy />
            <sp:RequireServerEntropy />
          </wsp:Policy>
        </sp:Trust10>
        <wsaw:UsingAddressing />
      </wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>

Он генерирует правильную информацию о привязке/клиенте:

<system.serviceModel>
    <bindings>
        <wsHttpBinding>
            <binding name = "WSHttpBinding_IQuery_2012_04">
                <security mode = "TransportWithMessageCredential">
                    <transport clientCredentialType = "None" />
                    <message clientCredentialType = "UserName" establishSecurityContext = "false" />
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>
    <client>
        <endpoint address = "https://somehost/ptsqamt/Maintain/Services/Data/2012/04/Query.svc"
            binding = "wsHttpBinding" bindingConfiguration = "WSHttpBinding_IQuery_2012_04"
            contract = "PTSQueryService.IQuery_2012_04" name = "WSHttpBinding_IQuery_2012_04" />
    </client>
</system.serviceModel>

Сгенерированный клиентский прокси не создает ClientCredentials, поэтому я не могу добавить пользователя/пароль.

Сгенерированный фрагмент клиента для справки по .Net Framework:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class Query_2012_04Client : System.ServiceModel.ClientBase<WCFTest.PTSQueryService.IQuery_2012_04>, WCFTest.PTSQueryService.IQuery_2012_04 {
    
    public Query_2012_04Client() {
    }
    
    public Query_2012_04Client(string endpointConfigurationName) : 
            base(endpointConfigurationName) {
    }
    
    public Query_2012_04Client(string endpointConfigurationName, string remoteAddress) : 
            base(endpointConfigurationName, remoteAddress) {
    }
    
    public Query_2012_04Client(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(endpointConfigurationName, remoteAddress) {
    }
    
    public Query_2012_04Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(binding, remoteAddress) {
    }
    
    public WCFTest.PTSQueryService.QueryParameter[] GetQueryParameters(int qtype, string qname) {
        return base.Channel.GetQueryParameters(qtype, qname);
    }
    
    public System.Threading.Tasks.Task<WCFTest.PTSQueryService.QueryParameter[]> GetQueryParametersAsync(int qtype, string qname) {
        return base.Channel.GetQueryParametersAsync(qtype, qname);
    }
    
    public string GetQueryData(int qtype, string qname, WCFTest.PTSQueryService.QueryArgument[] args, int page, int pageSize) {
        return base.Channel.GetQueryData(qtype, qname, args, page, pageSize);
    }
    
    public System.Threading.Tasks.Task<string> GetQueryDataAsync(int qtype, string qname, WCFTest.PTSQueryService.QueryArgument[] args, int page, int pageSize) {
        return base.Channel.GetQueryDataAsync(qtype, qname, args, page, pageSize);
    }
}

Для справки, вот что сгенерировал проект .Net 8:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
public partial class Query_2012_04Client : System.ServiceModel.ClientBase<PTSQueryService.IQuery_2012_04>, PTSQueryService.IQuery_2012_04
{
    
    /// <summary>
    /// Implement this partial method to configure the service endpoint.
    /// </summary>
    /// <param name = "serviceEndpoint">The endpoint to configure</param>
    /// <param name = "clientCredentials">The client credentials</param>
    static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
    
    public Query_2012_04Client() : 
            base(Query_2012_04Client.GetDefaultBinding(), Query_2012_04Client.GetDefaultEndpointAddress())
    {
        this.Endpoint.Name = EndpointConfiguration.WSHttpBinding_IQuery_2012_04.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }
    
    public Query_2012_04Client(EndpointConfiguration endpointConfiguration) : 
            base(Query_2012_04Client.GetBindingForEndpoint(endpointConfiguration), Query_2012_04Client.GetEndpointAddress(endpointConfiguration))
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }
    
    public Query_2012_04Client(EndpointConfiguration endpointConfiguration, string remoteAddress) : 
            base(Query_2012_04Client.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress))
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }
    
    public Query_2012_04Client(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(Query_2012_04Client.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }
    
    public Query_2012_04Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(binding, remoteAddress)
    {
    }
    
    public System.Threading.Tasks.Task<PTSQueryService.QueryParameter[]> GetQueryParametersAsync(int qtype, string qname)
    {
        return base.Channel.GetQueryParametersAsync(qtype, qname);
    }
    
    public System.Threading.Tasks.Task<string> GetQueryDataAsync(int qtype, string qname, PTSQueryService.QueryArgument[] args, int page, int pageSize)
    {
        return base.Channel.GetQueryDataAsync(qtype, qname, args, page, pageSize);
    }
    
    public virtual System.Threading.Tasks.Task OpenAsync()
    {
        return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
    }
    
    private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
    {
        if ((endpointConfiguration == EndpointConfiguration.WSHttpBinding_IQuery_2012_04))
        {
            System.ServiceModel.WSHttpBinding result = new System.ServiceModel.WSHttpBinding();
            result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
            result.MaxReceivedMessageSize = int.MaxValue;
            result.AllowCookies = true;
            result.Security.Mode = System.ServiceModel.SecurityMode.TransportWithMessageCredential;
            result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.None;
            result.Security.Message.ClientCredentialType = System.ServiceModel.MessageCredentialType.UserName;
            result.Security.Message.EstablishSecurityContext = false;
            return result;
        }
        throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
    }
    
    private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
    {
        if ((endpointConfiguration == EndpointConfiguration.WSHttpBinding_IQuery_2012_04))
        {
            return new System.ServiceModel.EndpointAddress("https://somehost/ptsqamt/Maintain/Services/Data/2012/04/Query.s" +
                    "vc");
        }
        throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
    }
    
    private static System.ServiceModel.Channels.Binding GetDefaultBinding()
    {
        return Query_2012_04Client.GetBindingForEndpoint(EndpointConfiguration.WSHttpBinding_IQuery_2012_04);
    }
    
    private static System.ServiceModel.EndpointAddress GetDefaultEndpointAddress()
    {
        return Query_2012_04Client.GetEndpointAddress(EndpointConfiguration.WSHttpBinding_IQuery_2012_04);
    }
    
    public enum EndpointConfiguration
    {
        
        WSHttpBinding_IQuery_2012_04,
    }
}

Спасибо за любую помощь, которую вы можете предложить.

Обычно вы передаете учетные данные пользователя, который вошел в систему клиента, на сервер через прокси. Вы не используете имя пользователя и пароль, поскольку пароль не зашифрован. Когда вы используете имя пользователя и пароль, вы создаете учетные данные (или используете учетные данные пользователя по умолчанию). См.: Learn.microsoft.com/en-us/dotnet/framework/wcf/feature-detai‌​ls/…

jdweng 02.07.2024 21:20

@jdweng Проблема заключается в том, что при создании прокси-сервера не были добавлены ClientCredentials. Для клиента .Net Core это работало нормально.

Mike 02.07.2024 22:15
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

ClientCredentials существует в System.ServiceModel.ClientBase <TChannel>.

Вы можете использовать его следующим образом:

Добавьте класс проверки на сервер: общедоступный класс CustomUserNameValidator: UserNamePasswordValidator {

   public override void Validate(string userName, string password)
    {
        if (null == userName || null == password)
        {
            throw new ArgumentNullException();
        }
        if (userName != "admin" && password != "wcf.admin") 
        {
            throw new System.IdentityModel.Tokens.SecurityTokenException("Unknown Username or Password");
        }

    }
}

Используйте этот код в клиенте, чтобы добавить имя пользователя и пароль:

using (var proxy = new ServiceReference1.Service1Client())
{
    proxy.ClientCredentials.UserName.UserName = "admin";
    proxy.ClientCredentials.UserName.Password = "wcf.admin";
    string result = proxy.GetData(1);
    Console.WriteLine(result);
    var compositeObj = proxy.GetDataUsingDataContract(new CompositeType() { BoolValue = true, StringValue = "test" });
    Console.WriteLine(SerializerToJson(compositeObj));
}
Console.ReadKey();

Если есть несоответствие, возникнет ошибка.

Я не использовал этот ответ, но он меня разбудил, так что я отдам вам должное за это. В отличие от версии Core, которая давала вам прямой доступ к ClientCredentials, мне пришлось привести ссылку на службу с помощью ClientBase. Это одна из тех деталей, которые забываешь, когда долго что-то не делал. Собираюсь добавить ответ

Mike 03.07.2024 15:19

В конце концов, это было простое преобразование ссылки, которая предоставила ClientCredentials. Я как бы корю себя за то, что потратил на это так много времени.

Поэтому я на самом деле вызываю эту службу из службы. Я создаю ссылку в главном конструкторе сервисов:

public class MainService : IMainService
{
    private readonly IQuery_2012_04 _ptsService;

    public MainService()
    {
        _ptsService = new Query_2012_04Client();
        ((ClientBase<IQuery_2012_04>)_ptsService).ClientCredentials.UserName.UserName = ConfigurationManager.AppSettings["PTSServiceUser"];
        ((ClientBase<IQuery_2012_04>)_ptsService).ClientCredentials.UserName.Password = ConfigurationManager.AppSettings["PTSServicePwd"];
        ((ClientBase<IQuery_2012_04>)_ptsService).Endpoint.Address = new EndpointAddress(ConfigurationManager.AppSettings["PTSQueryServiceUrl"]);
    }

    *** Snip ***
 }

Другие вопросы по теме