我最后更新了使用wcf的代码,因为这就是我一直在研究的当前dev版本中的代码。然后我使用了一些发布在亚马逊论坛上的代码,但使其更易于使用。
更新:
新的易于使用的代码,使您仍然可以使用所有配置设置
在我之前发布的代码中,以及我在其他地方看到的,当创建服务对象时,其中一个构造函数重写用于告诉它使用HTTPS,为其提供HTTPS URL,并手动附加将执行签名的消息检查器。不使用默认构造函数的缺点是您无法通过配置文件配置服务。
我已经重做了这段代码,这样您就可以继续使用默认的、无参数的、构造函数,并通过配置文件配置服务。这样做的好处是,您不必重新编译代码来使用它,也不必在部署后进行更改,例如对maxStringContentLength进行更改(这是导致此更改发生的原因,同时也发现了在代码中进行所有操作的缺点)。我还稍微更新了签名部分,这样您就可以告诉它要使用什么散列算法以及提取操作的regex。
这两个变化是因为并非所有来自亚马逊的Web服务都使用相同的哈希算法,并且可能需要以不同的方式提取操作。这意味着您可以通过更改配置文件中的内容,为每种服务类型重用相同的代码。
public class SigningExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(SigningBehavior); }
}
[ConfigurationProperty("actionPattern", IsRequired = true)]
public string ActionPattern
{
get { return this["actionPattern"] as string; }
set { this["actionPattern"] = value; }
}
[ConfigurationProperty("algorithm", IsRequired = true)]
public string Algorithm
{
get { return this["algorithm"] as string; }
set { this["algorithm"] = value; }
}
[ConfigurationProperty("algorithmKey", IsRequired = true)]
public string AlgorithmKey
{
get { return this["algorithmKey"] as string; }
set { this["algorithmKey"] = value; }
}
protected override object CreateBehavior()
{
var hmac = HMAC.Create(Algorithm);
if (hmac == null)
{
throw new ArgumentException(string.Format("Algorithm of type ({0}) is not supported.", Algorithm));
}
if (string.IsNullOrEmpty(AlgorithmKey))
{
throw new ArgumentException("AlgorithmKey cannot be null or empty.");
}
hmac.Key = Encoding.UTF8.GetBytes(AlgorithmKey);
return new SigningBehavior(hmac, ActionPattern);
}
}
public class SigningBehavior : IEndpointBehavior
{
private HMAC algorithm;
private string actionPattern;
public SigningBehavior(HMAC algorithm, string actionPattern)
{
this.algorithm = algorithm;
this.actionPattern = actionPattern;
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new SigningMessageInspector(algorithm, actionPattern));
}
}
public class SigningMessageInspector : IClientMessageInspector
{
private readonly HMAC Signer;
private readonly Regex ActionRegex;
public SigningMessageInspector(HMAC algorithm, string actionPattern)
{
Signer = algorithm;
ActionRegex = new Regex(actionPattern);
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var operation = GetOperation(request.Headers.Action);
var timeStamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
var toSignBytes = Encoding.UTF8.GetBytes(operation + timeStamp);
var sigBytes = Signer.ComputeHash(toSignBytes);
var signature = Convert.ToBase64String(sigBytes);
request.Headers.Add(MessageHeader.CreateHeader("AWSAccessKeyId", Helpers.NameSpace, Helpers.AWSAccessKeyId));
request.Headers.Add(MessageHeader.CreateHeader("Timestamp", Helpers.NameSpace, timeStamp));
request.Headers.Add(MessageHeader.CreateHeader("Signature", Helpers.NameSpace, signature));
return null;
}
private string GetOperation(string request)
{
var match = ActionRegex.Match(request);
var val = match.Groups["action"];
return val.Value;
}
}
要使用它,您不需要对现有代码进行任何更改,甚至可以根据需要将签名代码放入整个其他程序集中。您只需要这样设置配置部分(注意:版本号很重要,如果没有它匹配代码,将无法加载或运行)
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="signer" type="WebServices.Amazon.SigningExtension, AmazonExtensions, Version=1.3.11.7, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="AWSECommerceBehaviors">
<signer algorithm="HMACSHA256" algorithmKey="..." actionPattern="\w:\/\/.+/(?<action>.+)" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="AWSECommerceServiceBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536">
<readerQuotas maxDepth="32" maxStringContentLength="16384" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://ecs.amazonaws.com/onca/soap?Service=AWSECommerceService" behaviorConfiguration="AWSECommerceBehaviors" binding="basicHttpBinding" bindingConfiguration="AWSECommerceServiceBinding" contract="WebServices.Amazon.AWSECommerceServicePortType" name="AWSECommerceServicePort" />
</client>
</system.serviceModel>