【问题标题】:Creating A Service Bus SAS Token and Consuming Relay in WinRT在 WinRT 中创建服务总线 SAS 令牌并使用中继
【发布时间】:2015-05-01 22:14:06
【问题描述】:

我有一个服务总线中继 (WCF SOAP) 我想在我的 Windows 应用商店应用程序中使用。我已经编写了创建令牌的代码以及下面的客户端。

问题是我得到一个 AuthorizationFailedFault 返回一个错误字符串“InvalidSignature:令牌的签名无效。”而且我想不通。

我的创建令牌方法:

private static string CreateSasToken()
{
    TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970,1, 1);
    var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + 3600);
    string stringToSign = webUtility.UrlEncode(ServiceUri.AbsoluteUri) + "\n" + expiry;

    string hashKey = Encoding.UTF8.GetBytes(Secret).ToString();

    MacAlgorithmProvider macAlgorithmProvider = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
    BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;

    var messageBuffer = CryptographicBuffer.ConvertStringToBinary(stringToSign,encoding);
    IBuffer keyBuffer = CryptographicBuffer.ConvertStringToBinary(hashKey,encoding);

    CryptographicKey hmacKey = macAlgorithmProvider.CreateKey(keyBuffer);
    IBuffer signedMessage = CryptographicEngine.Sign(hmacKey, messageBuffer);

    string signature = CryptographicBuffer.EncodeToBase64String(signedMessage);

    var sasToken = String.Format(CultureInfo.InvariantCulture,
        "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
        WebUtility.UrlEncode(ServiceUri.AbsoluteUri),
        WebUtility.UrlEncode(signature), expiry, Issuer);

    return sasToken;
}

我的客户类:

    public partial class ServiceClient
    {
        public async Task<string> GetDataUsingDataContract(string item, string sasToken)
        {

            HttpClient client = new HttpClient();

            client.DefaultRequestHeaders.Add("ServiceBusAuthorization",sasToken);
            client.DefaultRequestHeaders.Add("SOAPAction",".../GetDataUsingDataContract");
            client.DefaultRequestHeaders.Add("Host", "xxxxxxxxxxx.servicebus.windows.net");

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,ServiceUri);

            var content =new StringContent(@"<s:Envelope
                xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
                <s:Header></s:Header><s:Body>"+ item +@"</s:Body>
                </s:Envelope>",System.Text.Encoding.UTF8,"application/xml");
            request.Content = content;

            HttpResponseMessage wcfResponse = client.SendAsync(request).Result;
            HttpContent stream = wcfResponse.Content;

            var response = stream.ReadAsStringAsync();
            var returnPacket = response.Result;

            return returnPacket;
        }
    }

我已经成功地使用 Http(通过 Fiddler)通过复制由 Micorosft.ServiceBus 在控制台应用程序中创建的未过期令牌来使用中继。

【问题讨论】:

    标签: wcf azureservicebus azure-servicebusrelay winrt-httpclient


    【解决方案1】:

    我想出了一个解决方案,这两种方法都错了。

    CreateSasToken 方法:

    一个小改动涉及将 hashKey 变量设置为 byte[] 而不是字符串。这一行: string hashKey = Encoding.UTF8.GetBytes(Secret).ToString(); 改为: var hashKey = Encoding.UTF8.GetBytes(Secret);

    此更改意味着我需要使用不同的方法来设置 keyBuffer。 这一行: IBuffer keyBuffer = CryptographicBuffer.ConvertStringToBinary(hashKey,encoding); 改成这样: IBuffer keyBuffer = CryptographicBuffer.CreateFromByteArray(hashKey);

    所以新的 CreateSasToken 方法是:

        private static string GetSasToken()
        {
            TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
            var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + 3600);
            string stringToSign = WebUtility.UrlEncode(ServiceUri.AbsoluteUri) + "\n" + expiry;
    
            var hashKey = Encoding.UTF8.GetBytes(Secret);
    
            MacAlgorithmProvider macAlgorithmProvider =
                MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
            const BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
            var messageBuffer = CryptographicBuffer.ConvertStringToBinary(stringToSign,
                encoding);
    
            IBuffer keyBuffer = CryptographicBuffer.CreateFromByteArray(hashKey);
            CryptographicKey hmacKey = macAlgorithmProvider.CreateKey(keyBuffer);
            IBuffer signedMessage = CryptographicEngine.Sign(hmacKey, messageBuffer);
    
            string signature = CryptographicBuffer.EncodeToBase64String(signedMessage);
    
            var sasToken = String.Format(CultureInfo.InvariantCulture,
                "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
                WebUtility.UrlEncode(ServiceUri.AbsoluteUri),
                WebUtility.UrlEncode(signature),
                expiry, Issuer);
    
            return sasToken;
        }
    

    服务客户端类

    这里有几点需要注意。

    1. 为了使请求生效,必须将 SAS 令牌作为 AuthenticationValueHeader 对象的参数添加到标头中。因此,我将以下方法添加到我的助手类 (ServiceBusHelper) 中,该类将 Key、KeyName 和 SasToken 作为属性,并将 CreateSasToken 作为方法。

      public static AuthenticationHeaderValue CreateBasicHeader()
      {
          return new AuthenticationHeaderValue("Basic", SasToken);
      }
      
    2. 必须以特殊方式创建 HttpRequestMessage 内容属性。获取传入的 item 参数,这是一个序列化的 WCF DataContract 类型,我需要做一些事情来制作 SOAP 信封。这里没有详细介绍它们,而是整个类(仅一种方法)。我将评论处理响应的代码。

      public partial class SalesNotifyServiceClient
      {
          public async Task<string> GetDataUsingDataContract(string item)
          {
              string returnPacket = "";
              string element = "";
              try
              {
                  HttpClient client = new HttpClient();
      
                  client.DefaultRequestHeaders.Add("ServiceBusAuthorization",
                      ServiceBusHelper.CreateBasicHeader().Parameter);
                  client.DefaultRequestHeaders.Add("SOAPAction",
                      ".../GetDataUsingDataContract");
                  client.DefaultRequestHeaders.Add("Host",
                      "xxxxxxxxxx.servicebus.windows.net");
      
                  HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,
                      ServiceBusHelper.ServiceUri);
      
                  //Creating the request.Content
                  var encodedItem = item.Replace("<", "&lt;").Replace(">", "&gt;");
      
                  var strRequest =
                      @"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
                      <s:Header></s:Header><s:Body><GetDataUsingDataContract xmlns=
                      ""http://www.xxxxxxxxxx.com/servicemodel/relay""><item>" +
                      encodedItem + 
                      @"</item></GetDataUsingDataContract></s:Body></s:Envelope>";
      
                  var content = new StringContent(strRequest,
                      System.Text.Encoding.UTF8, "application/xml");
      
                  request.Content = content;
      
                  HttpResponseMessage wcfResponse = client.SendAsync(request).Result;
                  HttpContent stream = wcfResponse.Content;
      
                  var response = await stream.ReadAsStringAsync();
      
                  //Handling the response
                  XDocument doc;
                  using (StringReader s = new StringReader(response))
                  {
                      doc = XDocument.Load(s);
                  }
      
                  if (doc.Root != null)
                  {
                      element = doc.Root.Value;
                  }
      
                  returnPacket = element;
              }
              catch (Exception e)
              {
                  var message = e.Message;
              }
      
              return returnPacket;
          }
      }
      
    3. 为了获取 DataContract 对象,我必须对响应字符串做一些事情。正如您在上面的 //Handling the response 评论中看到的那样,使用 StringReader 我将返回的 SOAP 信封作为字符串加载到 XDocument 中,根值是我的序列化 DataContract 对象。然后我反序列化了从该方法返回的 returnPacket 变量有我的响应对象。

    【讨论】:

      猜你喜欢
      • 2022-01-15
      • 2022-01-17
      • 2017-09-25
      • 2018-12-17
      • 1970-01-01
      • 2014-03-29
      • 1970-01-01
      • 1970-01-01
      • 2012-09-01
      相关资源
      最近更新 更多