Wcf 在WinRT中创建服务总线SAS令牌和使用中继
我有一个服务总线中继(WCF SOAP),我想在我的Windows应用商店应用中使用。我已经编写了代码来创建令牌以及下面的客户端 问题是,我得到了一个AuthorizationFailedFault,它返回了一个faultstring“InvalidSignature:The token has a invalid signature”。我无法理解它 我的创建令牌方法:Wcf 在WinRT中创建服务总线SAS令牌和使用中继,wcf,azureservicebus,azure-servicebusrelay,winrt-httpclient,Wcf,Azureservicebus,Azure Servicebusrelay,Winrt Httpclient,我有一个服务总线中继(WCF SOAP),我想在我的Windows应用商店应用中使用。我已经编写了代码来创建令牌以及下面的客户端 问题是,我得到了一个AuthorizationFailedFault,它返回了一个faultstring“InvalidSignature:The token has a invalid signature”。我无法理解它 我的创建令牌方法: private static string CreateSasToken() { TimeSpan sinceEpoc
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;
}
}
公共部分类ServiceClient
{
公共异步任务GetDataUsingDataContract(字符串项,字符串标记)
{
HttpClient=新的HttpClient();
client.DefaultRequestHeaders.Add(“ServiceBusAuthorization”,sasToken);
client.DefaultRequestHeaders.Add(“SOAPAction”,“../GetDataUsingDataContract”);
client.DefaultRequestHeaders.Add(“主机”,“xxxxxxxxxx.servicebus.windows.net”);
HttpRequestMessage请求=新的HttpRequestMessage(HttpMethod.Post,ServiceUri);
var content=新的StringContent(@“
“+项目+@”
,System.Text.Encoding.UTF8,“application/xml”);
request.Content=内容;
HttpResponseMessage wcfResponse=client.SendAsync(请求).Result;
HttpContent流=wcfResponse.Content;
var response=stream.ReadAsStringAsync();
var returnPacket=response.Result;
返回包;
}
}
通过在控制台应用程序中复制由Micorosft.ServiceBus创建的未过期令牌,我已成功使用Http(通过Fiddler)使用中继。我找到了一个解决方案,该解决方案涉及两种方法都是错误的 CreateSasToken方法: 一个小的更改涉及将hashKey变量设置为byte[]而不是string。这一行:
string hashKey=Encoding.UTF8.GetBytes(Secret.ToString()代码>
改为:
var hashKey=Encoding.UTF8.GetBytes(Secret)代码>
这个改变意味着我需要使用不同的方法来设置keyBuffer。
这一行:
IBuffer-keyBuffer=cryptographicsbuffer.ConvertStringToBinary(hashKey,编码)代码>
更改为:
IBuffer keyBuffer=cryptographicsbuffer.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;
}
服务客户端类
这里有几件事需要注意
为了使请求工作,必须将SAS令牌作为AuthenticationValueHeader对象的参数添加到标头中。因此,我向助手类(ServiceBusHelper)添加了以下方法,该类将Key、KeyName和SasToken作为属性,将CreateSasToken作为方法
public static AuthenticationHeaderValue CreateBasicHeader()
{
return new AuthenticationHeaderValue("Basic", SasToken);
}
HttpRequestMessage内容属性必须以特殊方式创建。接受传入的item参数,这是一个序列化的WCF数据契约类型,我需要做一些事情来制作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("<", "<").Replace(">", ">");
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;
}
}
公共部分类SalesNotifyServiceClient
{
公共异步任务GetDataUsingDataContract(字符串项)
{
字符串returnPacket=“”;
字符串元素=”;
尝试
{
HttpClient=新的HttpClient();
client.DefaultRequestHeaders.Add(“ServiceBusAuthorization”,
ServiceBusHelper.CreateBasicHeader().Parameter);
client.DefaultRequestHeaders.Add(“SOAPAction”,
“../GetDataUsingDataContract”);
client.DefaultRequestHeaders.Add(“主机”,
“xxxxxxxxx.servicebus.windows.net”);
HttpRequestMessage请求=新的HttpRequestMessage(HttpMethod.Post,
ServiceBusHelper.ServiceUri);
//创建request.Content
var encodedItem=item.Replace(“,”);
var Strequest=
@"
" +
encodedItem+
@"";
var内容=新的StringContent(strRequest,
System.Text.Encoding.UTF8,“应用程序/xml”);
request.Content=内容;
HttpResponseMessage wcfResponse=client.SendAsync(请求).Result;
HttpContent流=wcfResponse.Content;
var response=wait stream.ReadAsStringAsync();
//处理回应
XDocument文件;
使用(StringReader s=新StringReader(响应))
{
doc=XDocument.Load(s);
}
如果(doc.Root!=null)
{
元素=doc.Root.Value;
}
returnPacket=元素;
}
捕获(例外e)
{
var消息=e.消息;
}
返回包;
}
}
为了得到DataContract对象,我必须对响应字符串做一些事情。正如您在上面处理响应的//注释中所看到的,我使用StringReader将返回的SOAP信封作为字符串加载到XDocument中,根值是我的序列化DataContract对象。然后,我反序列化了从方法had my response对象返回的returnPacket变量