我在我的WCF应用中实现了自定义userNamePasswordValidationMode
,如下所示:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding>
<security mode ="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyProject.Validator.MyValidator, MyProject" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
</system.serviceModel>
这不会引发任何错误,但是当我在客户端中引用我的服务并设置用户名/密码凭据时,即使输入了错误的密码,我的方法仍会被调用:
Testing.myAPIClient.client = new Testing.myAPIClient();
client.ClientCredentials.UserName.UserName = "test";
client.ClientCredentials.UserName.Password = "wrongpassword";
Console.WriteLine(client.StockQuery("123"));
Console.ReadLine();
方法StockQuery
仍然被调用,MyValidator
中的代码甚至无法被调用:
public class MyValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
using (var ax = new AXConnector())
{
if (!(bool)ax.CallStaticClassMethod("OnlineUsers", "validateLogon", userName, password))
{
throw new UnauthorizedAccessException("Not Authorised");
}
}
}
}
这是我的app.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IMyAPI" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://myServer:89/MyAPI.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IMyAPI" contract="Testing.IMyAPI"
name="BasicHttpBinding_IMyAPI" />
</client>
</system.serviceModel>
服务接口:
[ServiceContract]
public interface IMyAPI
{
string UserName { [OperationContract] get; [OperationContract] set; }
string Password { [OperationContract] get; [OperationContract] set; }
[OperationContract]
bool StockQuery(string partNo);
[OperationContract]
decimal PriceQuery(string partNo, string accNo);
}
服务类:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required),
ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
public class MyAPI : IMyAPI
{
public string UserName { get; set; }
public string Password { get; set; }
public MyAPI()
{
this.CheckSecurity();
}
private void CheckSecurity()
{
if (this.UserName != "test" && this.Password != "123")
{
throw new UnauthorizedAccessException("Not Authorised");
}
}
// StockQuery and PriceQuery methods...
}
答案 0 :(得分:1)
您的客户Security.Mode
设置为"None"
,应该是"Message"
。
<security mode ="Message">
<message clientCredentialType="UserName"/>
</security>
修改:您可能需要certificate才能使用它。您可以关注this walkthrough,但不建议在生产中使用它。
[1] 另一个选择是实现自己的安全性。这是一个基本的例子。
在您的服务中,将其ServiceBehavior
InstanceContextMode
更改为PerSession
,将ConcurrencyMode
更改为Single
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
public class SomeService : ISomeService
{
// ...
}
在您的服务中添加Username
和Password
媒体资源。
public string UserName { [OperationContract] get; [OperationContract] set; }
public string Password { [OperationContract] get; [OperationContract] set; }
添加用于检查安全性的私有方法。
public void CheckSecurity()
{
if ((this.UserName == null || this.Password == null) ||
this.UserName == "username" && this.Password == "password"))
{
throw new FaultException("Unknown username or incorrect password.");
}
}
然后在每个服务 [2] 类构造函数方法中调用CheckSecurity
方法。
public void SomeServiceMethod()
{
this.CheckSecurity();
// some method code
}
在您的客户端应用程序代码中,为每个实例设置服务用户名和密码,或者创建一个静态类来为您执行此操作。
您也可以尝试在用户名和密码中使用加密来增加安全性。
[1]取自How to put a password on a WCF Service? 中的答案
[2]您需要在每个服务方法的开头调用CheckSecuirty
方法。
答案 1 :(得分:0)
我们遇到了一个问题,即多年来一切正常,但是现在我们正在迁移到一个在负载平衡器后面将SSL卸载了所有东西的环境,因此我们不再能够依靠传输了。我遇到了OP描述的所有相同问题。
我们仍在使用正则十进制basicHttpBinding。以这种方式工作:
创建了一个可解析标题XML的类:
[DataContract(Namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", Name = "Security")]
public partial class SecurityHeaderType
{
[XmlElement(Namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")]
[DataMember]
public UsernameToken UsernameToken { get; set; }
}
public class UsernameToken : IXmlSerializable
{
public string Username { get; set; }
public string Password { get; set; }
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
Dictionary<string, string> secDictionary;
string xml = reader.ReadOuterXml();
using (var s = GenerateStreamFromString(xml))
{
secDictionary =
XElement.Load(s).Elements()
.ToDictionary(e => e.Name.LocalName, e => e.Value);
}
Username = secDictionary["Username"];
Password = secDictionary["Password"];
}
public Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}
我按照@ john-isaiah-carmona的用户名/密码字段示例,然后像这样写给他们:
public void CheckSecurity()
{
if (OperationContext.Current.IncomingMessageHeaders.FindHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd") != -1)
{
var securityHeader = OperationContext.Current.IncomingMessageHeaders.GetHeader<SecurityHeaderType>("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
Username = securityHeader.UsernameToken.Username;
Password = securityHeader.UsernameToken.Password;
}
else
throw new FaultException("Unknown username or incorrect password.");
}