遗憾的是,Microsoft 决定取消 WCF 中对 WSSecurity / UsernameTokens 的支持。当我几周前遇到同样的问题时,我深入研究了这个问题,但找不到他们为什么这样做。但只要付出一点努力,就有希望。
首先,如果您不想使用 HTTPS,则需要在绑定中指定(您指定了 https)。例如,我的绑定(也仅使用 HTTP)看起来非常简单:
<customBinding>
<binding name="dummySoapBinding">
<textMessageEncoding writeEncoding="UTF-8" messageVersion="Soap11" />
<httpTransport />
</binding>
</customBinding>
之后,为了在请求中创建必要的 WSS 安全标头,我定义了一个应用于端点的自定义行为。此行为将 MessageInspector 附加到发送请求的 ClientRuntime。该消息检查器在发送之前更改消息,并添加安全标头。您可以找到一些有关自定义行为的信息here https://blogs.msdn.microsoft.com/carlosfigueira/2011/06/28/wcf-extensibility-behavior-configuration-extensions/。有关消息检查器的信息可用here https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/18/wcf-extensibility-message-inspectors/.
我的 MessageInspector 目前看起来像这样:
public string Username { get; set; }
public string Password { get; set; }
public PasswordDigestMessageInspector(string username, string password)
{
Username = username;
Password = password;
}
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
// do nothing
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
// generate token
var usernameToken = new UsernameToken(this.Username, this.Password, PasswordOption.SendHashed);
// save token as xml
var securityToken = usernameToken.GetXml(new XmlDocument());
var securityTokenText = securityToken.OuterXml;
// remove vs data
var limit = request.Headers.Count;
for (var i = 0; i < limit; ++i)
{
if (!request.Headers[i].Name.Equals("VsDebuggerCausalityData")) continue;
request.Headers.RemoveAt(i);
break;
}
// set encoding type for nonce
var nonceRegex = new Regex(@"<wsse:Nonce");
securityTokenText = nonceRegex.Replace(securityTokenText,
"<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\"");
var newDoc = new XmlDocument();
newDoc.LoadXml(securityTokenText);
// create security header from message
var securityHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", newDoc.DocumentElement, false);
// add header to request message
request.Headers.Add(securityHeader);
// complete
return Convert.DBNull;
}
我在这里使用的 UsernameToken 类包含您需要的一切。它曾在 WCF 之前使用过,但遗憾的是已被删除。该类定义于:Microsoft.Web.Services3.Security.Tokens。应该有 nuget 包。或者,您可以手动安装 Microsoft 的 WebService Enhancements (WSE) 3.0 (here https://www.microsoft.com/en-us/download/details.aspx?id=14089).
有一些额外的逻辑可以修改一些参数,以便我调用的 WebService 接受我的请求。它非常严格,我没有办法修改它。你可能可以忽略这一点。
继续,我添加到端点的行为如下所示:
public class PasswordDigestBehaviour : IEndpointBehavior
{
public string Username { get; set; }
public string Password { get; set; }
public PasswordDigestBehaviour(string username, string password)
{
Username = username;
Password = password;
}
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
// do nothing
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new PasswordDigestMessageInspector(this.Username, this.Password));
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
// do nothing
}
public void Validate(ServiceEndpoint endpoint)
{
// do nothing
}
}
很简单。
最后,您可以将行为附加到 WCF 客户端类:
client.Endpoint.Behaviors.Add(new PasswordDigestBehaviour("Testuser", "Testpassword"));
我希望它能为您节省一些时间,因为我最近在这个话题上遇到了很多麻烦。
编辑:我刚刚看到您没有发送散列密码。您必须在“UsernameToken”的构造函数中考虑这一点,因为我正在对其进行哈希处理。