如何将用户凭据从 WebClient 传递到 WCF REST 服务?

发布于 2024-07-19 06:08:31 字数 3109 浏览 5 评论 0原文

我正在尝试公开 WCT REST 服务,只有具有有效用户名和密码的用户才能访问它。 用户名和密码存储在 SQL 数据库中。

这是服务契约:

public interface IDataService
{
    [OperationContract]
    [WebGet(ResponseFormat = WebMessageFormat.Json)]
    byte[] GetData(double startTime, double endTime);
}

这是 WCF 配置:

<bindings>
  <webHttpBinding>
    <binding name="SecureBinding">
      <security mode="Transport">
        <transport clientCredentialType="Basic"/>
      </security>
    </binding>
  </webHttpBinding>
</bindings>
<behaviors>
  <serviceBehaviors>
    <behavior name="DataServiceBehavior">
      <serviceMetadata httpGetEnabled="true"/>
      <serviceCredentials>
        <userNameAuthentication
           userNamePasswordValidationMode="Custom"
           customUserNamePasswordValidatorType=
                 "CustomValidator, WCFHost" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>
<services>
  <service behaviorConfiguration="DataServiceBehavior" name="DataService">
    <endpoint address="" binding="webHttpBinding"
              bindingConfiguration="SecureBinding" contract="IDataService" />
  </service>
</services>

我通过 Silverlight 应用程序中的 WebClient 类访问服务。 但是,我无法弄清楚如何将用户凭据传递给服务。 我尝试了 client.Credentials 的各种值,但它们似乎都没有触发我的自定义验证器中的代码。 我收到以下错误:基础连接已关闭:发送时发生意外错误。

这是我尝试过的一些示例代码:

   WebClient client = new WebClient();
   client.Credentials = new NetworkCredential("name", "password", "domain");
   client.OpenReadCompleted += new OpenReadCompletedEventHandler(GetData);
   client.OpenReadAsync(new Uri(uriString));

如果我将安全模式设置为“无”,则整个过程正常。 我还尝试了其他 clientCredentialType 值,但没有一个起作用。 我还自行托管了 WCF 服务,以消除与 IIS 在服务获得机会之前尝试对用户进行身份验证相关的问题。

任何有关潜在问题的评论将不胜感激。 谢谢。

更新:感谢Mehmet的出色建议。 这是我的跟踪配置:

 <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
      <source name="System.IdentityModel" switchValue="Information, 
               ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="xml"
           type="System.Diagnostics.XmlWriterTraceListener"
           initializeData="c:\Traces.svclog" />
    </sharedListeners>
  </system.diagnostics>

但我没有看到来自我的 Silverlight 客户端的任何消息。 至于https vs http,我使用https如下:

string baseAddress = "https://localhost:6600/";
_webServiceHost = new WebServiceHost(typeof(DataServices), 
                new Uri(baseAddress));
_webServiceHost.Open();

但是,我没有配置任何SSL证书。 这是问题所在吗?

I am trying to expose a WCT REST service and only users with valid username and password would be able to access it. The username and password are stored in a SQL database.

Here is the service contract:

public interface IDataService
{
    [OperationContract]
    [WebGet(ResponseFormat = WebMessageFormat.Json)]
    byte[] GetData(double startTime, double endTime);
}

Here is the WCF configuration:

<bindings>
  <webHttpBinding>
    <binding name="SecureBinding">
      <security mode="Transport">
        <transport clientCredentialType="Basic"/>
      </security>
    </binding>
  </webHttpBinding>
</bindings>
<behaviors>
  <serviceBehaviors>
    <behavior name="DataServiceBehavior">
      <serviceMetadata httpGetEnabled="true"/>
      <serviceCredentials>
        <userNameAuthentication
           userNamePasswordValidationMode="Custom"
           customUserNamePasswordValidatorType=
                 "CustomValidator, WCFHost" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>
<services>
  <service behaviorConfiguration="DataServiceBehavior" name="DataService">
    <endpoint address="" binding="webHttpBinding"
              bindingConfiguration="SecureBinding" contract="IDataService" />
  </service>
</services>

I am accessing the service via the WebClient class within a Silverlight application. However, I have not been able to figure out how to pass the user credentials to the service. I tried various values for client.Credentials but none of them seems to trigger the code in my custom validator. I am getting the following error: The underlying connection was closed: An unexpected error occurred on a send.

Here is some sample code I have tried:

   WebClient client = new WebClient();
   client.Credentials = new NetworkCredential("name", "password", "domain");
   client.OpenReadCompleted += new OpenReadCompletedEventHandler(GetData);
   client.OpenReadAsync(new Uri(uriString));

If I set the security mode to None, the whole thing works. I also tried other clientCredentialType values and none of them worked. I also self-hosted the WCF service to eliminate the issues related to IIS trying to authenticate a user before the service gets a chance.

Any comment on what the underlying issues may be would be much appreciated. Thanks.

Update: Thanks to Mehmet's excellent suggestions. Here is the tracing configuration I had:

 <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
      <source name="System.IdentityModel" switchValue="Information, 
               ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="xml" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="xml"
           type="System.Diagnostics.XmlWriterTraceListener"
           initializeData="c:\Traces.svclog" />
    </sharedListeners>
  </system.diagnostics>

But I did not see any message coming from my Silverlight client. As for https vs http, I used https as follows:

string baseAddress = "https://localhost:6600/";
_webServiceHost = new WebServiceHost(typeof(DataServices), 
                new Uri(baseAddress));
_webServiceHost.Open();

However, I did not configure any SSL certificate. Is this the problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

木槿暧夏七纪年 2024-07-26 06:08:31

由于您使用的是“基本”身份验证,因此您需要在请求标头中发送用户名和密码。 下面显示了手动将凭据添加到标头的示例:

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(@"https://localhost:6600/MyWCFService/GetData");
//Add a header to the request that contains the credentials
//DO NOT HARDCODE IN PRODUCTION!! Pull credentials real-time from database or other store.
string svcCredentials = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes("userName" + ":" + "password"));
req.Headers.Add("Authorization", "Basic " + svcCredentials);
//Parse the response and do something with it...
using (WebResponse svcResponse = (HttpWebResponse)req.GetResponse())
{
  using (StreamReader sr = new StreamReader(svcResponse.GetResponseStream()))
  {
    //Sample parses json response; your code here may be different
    JavaScriptSerializer js = new JavaScriptSerializer();
    string jsonTxt = sr.ReadToEnd();
  }
}

Since you are using 'Basic' authentication you need to send the username and password in the request header. The example of manually adding the credentials to the header is displayed below:

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(@"https://localhost:6600/MyWCFService/GetData");
//Add a header to the request that contains the credentials
//DO NOT HARDCODE IN PRODUCTION!! Pull credentials real-time from database or other store.
string svcCredentials = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes("userName" + ":" + "password"));
req.Headers.Add("Authorization", "Basic " + svcCredentials);
//Parse the response and do something with it...
using (WebResponse svcResponse = (HttpWebResponse)req.GetResponse())
{
  using (StreamReader sr = new StreamReader(svcResponse.GetResponseStream()))
  {
    //Sample parses json response; your code here may be different
    JavaScriptSerializer js = new JavaScriptSerializer();
    string jsonTxt = sr.ReadToEnd();
  }
}
木槿暧夏七纪年 2024-07-26 06:08:31

首先,您可能希望将 WCF 跟踪添加到您的服务中以获取更多详细信息。 其次,我认为问题可能是您以明文形式发送用户凭据,而 WCF 不允许通过不安全的传输通道以明文形式传输用户凭据。 因此,请尝试使用 https 或指定加密算法来通过 http 保护用户凭据。

First you may want to add WCF tracing to your service to get more details. Second, I believe the problem could be that you're sending user credentials in clear text and WCF does not allow the transmission of user credentials in clear text over unsecured transport channel. So either try using https or specify an encryption algorithm to secure user credentials over http.

完美的未来在梦里 2024-07-26 06:08:31

上面的 atconway 建议似乎是正确的方法,但您的服务必须从标头读取 Base 64 字符串数据并转换回字符串,然后进行身份验证。 但它需要在每次调用时进行身份验证。

另一种方法是使用安全密钥。 和令牌

  1. 每个客户端都有他在第一次请求时发送的安全密钥
    可能在标题中。
  2. 您可以生成密钥,如 key = MD5Hash(username+password);
  3. 作为响应,他获得令牌,然后将令牌发送到每个
    要求。 令牌可以是Guid。 每个令牌都会在 x 分钟后过期。
  4. 在服务器端,您可以维护像 Dictionay 这样的单例字典,以在当前时间 > 时检查令牌过期情况。 过期时间将其从字典中删除。
  5. 要更新会话,请将更新会话方法放入代码中。

为了极端安全

拥有私钥/公钥对,客户端将使用公钥加密整个发布数据,您将使用私钥解密。

What atconway suggested above seems correct way but your service has to read base 64 string data from header and convert back to string and then authenticate. But it needs to authenticate on every call.

One more approach is to use secure key. and token

  1. Every client has secure key which he sends on first request ,
    probably in header.
  2. You can generate key like key = MD5Hash(username+password);
  3. In response he gets token the , the token is then sent in each
    request. Token can be Guid. Every token expired after x mins.
  4. On server side you can maintain a singleton dictionary like Dictionay , to check token expiration when current time > expiration time remove it from dictionary.
  5. To renew sessions put renew session method in your code.

For extream security

Have private/public key pair, client will encrypt entire post data using public key and you will decrypt using private key.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文