【发布时间】:2014-11-10 10:30:13
【问题描述】:
我正在为我的 WCF Web 服务使用 CustomUserNamePasswordValidator。但是,我正在尝试添加一个 IsAlive 操作,即使未经过身份验证,它也应该能够从客户端调用。
例如,我希望能够检查服务是否在线并且在启动时可访问,因此我可以在缺少 inet 连接或服务不可用(由于维护)时通知用户。
我已经有了所有这些的代码。我缺少的是如何在不传递用户名和密码的情况下访问操作。
我可能只添加第二个允许匿名访问的服务,但我真的更喜欢使用现有服务。
Validator 是这样实现的(我省略了实际的检查代码):
public sealed class MyCredentialValidator : UserNamePasswordValidator
{
public MyCredentialValidator ()
{
}
public override void Validate(string userName, string password)
{
Debug.WriteLine("MyCredentialValidator : Validate called.");
// do some checks
var isValid = CheckCredentials(userName, password)
if(!isValid)
{
throw new FaultException(...);
}
}
}
它是这样在 web.config 中注册的:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="SecureBehavior">
<serviceMetadata httpsGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyCredentialValidator,..."/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<bindings>
<wsHttpBinding>
<binding name="SecureBinding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="2147483647">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
<readerQuotas maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxStringContentLength="2147483647"/>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="my service" behaviorConfiguration="SecureBehavior">
<endpoint address="" binding="wsHttpBinding" contract="my contract" bindingConfiguration="SecureBinding">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
客户端配置:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="SecureBinding"
closeTimeout="00:10:00"
openTimeout="00:10:00"
receiveTimeout="00:10:00"
sendTimeout="00:10:00"
maxReceivedMessageSize="2147483647">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
<readerQuotas maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxStringContentLength="2147483647"/>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://my service url"
contract="my contract"
binding="wsHttpBinding"
bindingConfiguration="SecureBinding"
name="secure" />
</client>
</system.serviceModel>
客户端 wcf 调用代码:
var cf = new ChannelFactory<my contract>("secure");
using (IClientChannel channel = (IClientChannel)cf.CreateChannel())
{
channel.OperationTimeout = TimeSpan.FromSeconds(3);
bool success = false;
try
{
channel.Open();
result = ((my contract)channel).IsAlive();
channel.Close();
success = true;
}
finally
{
if (!success)
{
channel.Abort();
}
}
}
【问题讨论】:
-
好东西,虽然我不明白你为什么要实现身份验证客户端,在那里你有 debug.writeline,你应该在那里检查请求的去向,例如包含 IsAlive 然后返回,因此例如将其包装在 if else 语句中。
-
身份验证是服务器端!需要客户端中的绑定配置,以便 wcf 知道如何构建正确且有效的请求。身份验证当然是服务器端! :) 但是,请注意异常发生在客户端,当我解析通道工厂时,建立一个新通道并调用“打开”。我在问题中添加了客户端 wcf 代码。
-
哈哈,完美,你让我在那里呆了一秒钟,我只看到了 CheckCredentials。因此,在您开始调用 CheckCredentials 之前,您要检查您的传出 url,而不是我之前建议的传入 url,然后如果传出 url 例如包含“IsAlive”,您可以在该点简单地调用 return 而不是继续其余的检查凭据等等
-
我愿意!但是,服务器永远不会被击中(我需要它被击中,因为我想知道它是否“还活着”)。原因是我的绑定、客户端和服务器端需要设置凭据。我当然可以用虚拟值填充它,但这似乎是“一起被黑”
-
这意味着您需要在服务器端和客户端实现相同或相当相似的逻辑,当然假设您正在编写两者。因此,在客户端验证器中,您将检查传出请求是否要进行 Isalive 操作,如果是,则返回并且不继续其余的身份验证逻辑,然后在服务器端,如前所述检查,如果传入的请求将进入 IsAlive 操作并再次简单地返回,不执行其余的身份验证逻辑