我正在尝试使用第三方网络服务 https://staging.identitymanagement.lexisnexis.com/identity-proofing/services/identityProofingServiceWS/v2?wsdl
我已将其添加为服务参考,但我不确定如何传递标头的凭据。
如何使标头请求与此格式匹配?
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-49" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>12345/userID</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-username-token-profile-1.0#PasswordText">password123</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">d+VxCZX1cH/ieMkKEr/ofA==</wsse:Nonce>
<wsu:Created>2012-08-04T20:25:04.038Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
答案 0 :(得分:59)
上面的答案是错的!不要添加自定义标头。从您的示例xml判断,它是标准的WS-Security标头。 WCF绝对支持开箱即用。添加服务引用时,应在配置文件中为您创建basicHttpBinding绑定。您必须修改它以包含带有TransportWithMessageCredential模式的security元素和带有clientCredentialType = UserName的message元素:
<basicHttpBinding>
<binding name="usernameHttps">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
上面的配置告诉WCF通过HTTPS在SOAP头中期望用户ID /密码。然后,您可以在拨打电话之前在代码中设置ID /密码:
var service = new MyServiceClient();
service.ClientCredentials.UserName.UserName = "username";
service.ClientCredentials.UserName.Password = "password";
除非此特定服务提供商偏离标准,否则它应该有效。
答案 1 :(得分:33)
可能有一种更聪明的方法,但你可以像这样手动添加标题:
var client = new IdentityProofingService.IdentityProofingWSClient();
using (new OperationContextScope(client.InnerChannel))
{
OperationContext.Current.OutgoingMessageHeaders.Add(
new SecurityHeader("UsernameToken-49", "12345/userID", "password123"));
client.invokeIdentityService(new IdentityProofingRequest());
}
这里,SecurityHeader
是一个自定义实现的类,由于我选择使用属性来配置XML序列化,因此需要其他一些类:
public class SecurityHeader : MessageHeader
{
private readonly UsernameToken _usernameToken;
public SecurityHeader(string id, string username, string password)
{
_usernameToken = new UsernameToken(id, username, password);
}
public override string Name
{
get { return "Security"; }
}
public override string Namespace
{
get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
}
protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
XmlSerializer serializer = new XmlSerializer(typeof(UsernameToken));
serializer.Serialize(writer, _usernameToken);
}
}
[XmlRoot(Namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")]
public class UsernameToken
{
public UsernameToken()
{
}
public UsernameToken(string id, string username, string password)
{
Id = id;
Username = username;
Password = new Password() {Value = password};
}
[XmlAttribute(Namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd")]
public string Id { get; set; }
[XmlElement]
public string Username { get; set; }
[XmlElement]
public Password Password { get; set; }
}
public class Password
{
public Password()
{
Type = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
}
[XmlAttribute]
public string Type { get; set; }
[XmlText]
public string Value { get; set; }
}
我没有将Nonce
位添加到UsernameToken
XML,但它与Password
非常相似。还需要添加Created
元素,但这只是一个简单的[XmlElement]
。
答案 2 :(得分:2)
添加自定义硬编码标头可能会起作用(有时也可能会被拒绝),但这样做完全是错误的。 WSSE的目的是安全性。出于这个原因,Microsoft发布了Microsoft Web Services Enhancements 2.0以及随后的WSE 3.0。您需要安装此软件包(http://www.microsoft.com/en-us/download/details.aspx?id=14089)。
文档不易理解,特别是对于那些没有使用过SOAP和WS-Addressing的人。首先是&#34; BasicHttpBinding&#34;是Soap 1.1,它不会给你与WSHttpBinding相同的消息头。安装包并查看示例。您需要从WSE 3.0引用DLL,您还需要正确设置消息。 WS Addressing标头上有大量或多种变体。您要查找的是UsernameToken配置。
这是一个较长的解释,我应该为每个人写一些东西,因为我无法在任何地方找到正确的答案。至少你需要从WSE 3.0包开始。
答案 3 :(得分:0)
假设您在localhost
中有名称web.config
的服务参考,那么您可以按照以下方式进行操作
localhost.Service objWebService = newlocalhost.Service();
localhost.AuthSoapHd objAuthSoapHeader = newlocalhost.AuthSoapHd();
string strUsrName =ConfigurationManager.AppSettings["UserName"];
string strPassword =ConfigurationManager.AppSettings["Password"];
objAuthSoapHeader.strUserName = strUsrName;
objAuthSoapHeader.strPassword = strPassword;
objWebService.AuthSoapHdValue =objAuthSoapHeader;
string str = objWebService.HelloWorld();
Response.Write(str);
答案 4 :(得分:0)
假设您使用HttpWebRequest和HttpWebResponse调用Web服务,因为.Net客户端doest支持您尝试使用的WSLD结构。
在这种情况下,您可以在标题上添加安全凭证,如:
<soap:Envelpe>
<soap:Header>
<wsse:Security soap:mustUnderstand='true' xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'><wsse:UsernameToken wsu:Id='UsernameToken-3DAJDJSKJDHFJASDKJFKJ234JL2K3H2K3J42'><wsse:Username>YOU_USERNAME/wsse:Username><wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>YOU_PASSWORD</wsse:Password><wsse:Nonce EncodingType='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'>3WSOKcKKm0jdi3943ts1AQ==</wsse:Nonce><wsu:Created>2015-01-12T16:46:58.386Z</wsu:Created></wsse:UsernameToken></wsse:Security>
</soapHeather>
<soap:Body>
</soap:Body>
</soap:Envelope>
您可以使用SOAPUI来使用http日志获取wsse安全性。
要小心,因为这不是一种安全的方案。
答案 5 :(得分:0)
我从这里得到了一个更好的方法:WCF: Creating Custom Headers, How To Add and Consume Those Headers
客户识别自己
这里的目标是让客户提供某种信息 服务器可以使用哪个来确定谁正在发送消息。该 以下C#代码将添加名为ClientId的标头:
var cl = new ActiveDirectoryClient();
var eab = new EndpointAddressBuilder(cl.Endpoint.Address);
eab.Headers.Add( AddressHeader.CreateAddressHeader("ClientId", // Header Name
string.Empty, // Namespace
"OmegaClient")); // Header Value
cl.Endpoint.Address = eab.ToEndpointAddress();
// Now do an operation provided by the service.
cl.ProcessInfo("ABC");
该代码正在做的是添加名为ClientId的端点头 将值OmegaClient插入soap标头中 没有命名空间。
客户端配置文件中的自定义标题
还有另一种方式 做一个自定义标题。这可以在Xml配置文件中实现 通过将自定义标头指定为所有邮件发送的客户端 端点的一部分如此:
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IActiveDirectory" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:41863/ActiveDirectoryService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IActiveDirectory"
contract="ADService.IActiveDirectory" name="BasicHttpBinding_IActiveDirectory">
<headers>
<ClientId>Console_Client</ClientId>
</headers>
</endpoint>
</client>
</system.serviceModel>
</configuration>
答案 6 :(得分:0)
显然这篇文章已经存在多年了 - 但事实是我在寻找类似问题时确实找到了它。在我们的例子中,我们必须将用户名/密码信息添加到Security标头中。这与在Security标头之外添加标头信息不同。
执行此操作的正确方法(对于自定义绑定/ authenticationMode =“CertificateOverTransport”)(如.Net框架版本4.6.1),是照常添加客户端凭据:
client.ClientCredentials.UserName.UserName = "[username]";
client.ClientCredentials.UserName.Password = "[password]";
然后在安全绑定元素中添加“令牌” - 因为当身份验证模式设置为证书时,默认情况下不会包含用户名/密码凭证。
您可以像这样设置此令牌:
//Get the current binding
System.ServiceModel.Channels.Binding binding = client.Endpoint.Binding;
//Get the binding elements
BindingElementCollection elements = binding.CreateBindingElements();
//Locate the Security binding element
SecurityBindingElement security = elements.Find<SecurityBindingElement>();
//This should not be null - as we are using Certificate authentication anyway
if (security != null)
{
UserNameSecurityTokenParameters uTokenParams = new UserNameSecurityTokenParameters();
uTokenParams.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
security.EndpointSupportingTokenParameters.SignedEncrypted.Add(uTokenParams);
}
client.Endpoint.Binding = new CustomBinding(elements.ToArray());
应该这样做。如果没有上述代码(显式添加用户名令牌),即使在客户端凭据中设置用户名信息,也可能无法将这些凭据传递给服务。
答案 7 :(得分:0)
我在web.config中添加了customBinding。
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="CustomSoapBinding">
<security includeTimestamp="false"
authenticationMode="UserNameOverTransport"
defaultAlgorithmSuite="Basic256"
requireDerivedKeys="false"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
</security>
<textMessageEncoding messageVersion="Soap11"></textMessageEncoding>
<httpsTransport maxReceivedMessageSize="2000000000"/>
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="https://test.com:443/services/testService"
binding="customBinding"
bindingConfiguration="CustomSoapBinding"
contract="testService.test"
name="test" />
</client>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
添加customBinding之后,我可以将用户名和密码传递给客户端服务,如下所示:
service.ClientCridentials.UserName.UserName = "testUser";
service.ClientCridentials.UserName.Password = "testPass";
通过这种方式,您可以将标头中的用户名,密码传递给SOAP WCF服务。
答案 8 :(得分:0)
WCF开箱即用的建议表明问题中 提供的标题的答案不正确。问题的标题在UsernameToken中包含 Nonce 和 Created 时间戳,这是WCF不支持的WS-Security规范的正式部分。 WCF仅支持开箱即用的用户名和密码。
如果您需要做的就是添加用户名和密码,那么Sergey的答案就是最省力的方法。如果需要添加任何其他字段,则需要提供自定义类来支持它们。
我found的一种更优雅的方法是重写ClientCredentials,ClientCredentialsSecurityTokenManager和WSSecurityTokenizer类以支持其他属性。我提供了指向博客文章的链接,其中详细讨论了该方法,但这是替代代码的示例代码:
public class CustomCredentials : ClientCredentials
{
public CustomCredentials()
{ }
protected CustomCredentials(CustomCredentials cc)
: base(cc)
{ }
public override System.IdentityModel.Selectors.SecurityTokenManager CreateSecurityTokenManager()
{
return new CustomSecurityTokenManager(this);
}
protected override ClientCredentials CloneCore()
{
return new CustomCredentials(this);
}
}
public class CustomSecurityTokenManager : ClientCredentialsSecurityTokenManager
{
public CustomSecurityTokenManager(CustomCredentials cred)
: base(cred)
{ }
public override System.IdentityModel.Selectors.SecurityTokenSerializer CreateSecurityTokenSerializer(System.IdentityModel.Selectors.SecurityTokenVersion version)
{
return new CustomTokenSerializer(System.ServiceModel.Security.SecurityVersion.WSSecurity11);
}
}
public class CustomTokenSerializer : WSSecurityTokenSerializer
{
public CustomTokenSerializer(SecurityVersion sv)
: base(sv)
{ }
protected override void WriteTokenCore(System.Xml.XmlWriter writer,
System.IdentityModel.Tokens.SecurityToken token)
{
UserNameSecurityToken userToken = token as UserNameSecurityToken;
string tokennamespace = "o";
DateTime created = DateTime.Now;
string createdStr = created.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
// unique Nonce value - encode with SHA-1 for 'randomness'
// in theory the nonce could just be the GUID by itself
string phrase = Guid.NewGuid().ToString();
var nonce = GetSHA1String(phrase);
// in this case password is plain text
// for digest mode password needs to be encoded as:
// PasswordAsDigest = Base64(SHA-1(Nonce + Created + Password))
// and profile needs to change to
//string password = GetSHA1String(nonce + createdStr + userToken.Password);
string password = userToken.Password;
writer.WriteRaw(string.Format(
"<{0}:UsernameToken u:Id=\"" + token.Id +
"\" xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
"<{0}:Username>" + userToken.UserName + "</{0}:Username>" +
"<{0}:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" +
password + "</{0}:Password>" +
"<{0}:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" +
nonce + "</{0}:Nonce>" +
"<u:Created>" + createdStr + "</u:Created></{0}:UsernameToken>", tokennamespace));
}
protected string GetSHA1String(string phrase)
{
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
return Convert.ToBase64String(hashedDataBytes);
}
}
在创建客户端之前,您需要创建自定义绑定并向其中手动添加安全性,编码和传输元素。然后,使用您的自定义实现替换默认的ClientCredentials,并像通常一样设置用户名和密码:
var security = TransportSecurityBindingElement.CreateUserNameOverTransportBindingElement();
security.IncludeTimestamp = false;
security.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256;
security.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
var encoding = new TextMessageEncodingBindingElement();
encoding.MessageVersion = MessageVersion.Soap11;
var transport = new HttpsTransportBindingElement();
transport.MaxReceivedMessageSize = 20000000; // 20 megs
binding.Elements.Add(security);
binding.Elements.Add(encoding);
binding.Elements.Add(transport);
RealTimeOnlineClient client = new RealTimeOnlineClient(binding,
new EndpointAddress(url));
client.ChannelFactory.Endpoint.EndpointBehaviors.Remove(client.ClientCredentials);
client.ChannelFactory.Endpoint.EndpointBehaviors.Add(new CustomCredentials());
client.ClientCredentials.UserName.UserName = username;
client.ClientCredentials.UserName.Password = password;
答案 9 :(得分:0)
如果这与 Peoplesoft 问题有关:https://support.oracle.com/knowledge/PeopleSoft%20Enterprise/2370907_1.html
我需要在 Soap 密码上设置属性,而以前没有在该标签上设置该属性。
我只是在我的自定义绑定上设置 MessageSecurityVersion:
CustomBinding customBinding = new CustomBinding();
customBinding.Name = endpointName;
customBinding.CloseTimeout = TimeSpan.FromMinutes(1);
customBinding.OpenTimeout = TimeSpan.FromMinutes(1);
customBinding.SendTimeout = TimeSpan.FromMinutes(20);
customBinding.ReceiveTimeout = TimeSpan.FromMinutes(20);
TextMessageEncodingBindingElement textMessageElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
customBinding.Elements.Add(textMessageElement);
TransportSecurityBindingElement securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.IncludeTimestamp = false;
securityElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
customBinding.Elements.Add(securityElement);
// ORDER MATTERS: THIS HAS TO BE LAST!!! - HVT
HttpsTransportBindingElement transportElement = new HttpsTransportBindingElement();
transportElement.MaxBufferSize = int.MaxValue;
transportElement.MaxReceivedMessageSize = int.MaxValue;
customBinding.Elements.Add(transportElement);
return customBinding;