WCF 和自定义身份验证(用户名/密码)
Posted
技术标签:
【中文标题】WCF 和自定义身份验证(用户名/密码)【英文标题】:WCF and custom Authentication (username/password) 【发布时间】:2021-06-30 13:48:22 【问题描述】:我有通过 Internet 连接到 WCF 服务的 Windows 窗体客户端。 WCF 服务托管在在专用 Windows 帐户下运行的 Windows 服务中。 我想通过检查用户名+密码对来根据数据库对客户端进行身份验证。
为了避免数百种方法,例如:
public int Add(string User, string Password, int A, int B)
我使用以下指南覆盖 UserNamePasswordValidator
类:
http://blog.clauskonrad.net/2011/03/how-to-wcf-and-custom-authentication.html
我的自定义身份验证器类与示例匹配
class UsernameAuthentication : UserNamePasswordValidator
public override void Validate(string userName, string password)
//Will be checked against database
var ok = (userName == "Ole") && (password == "Pwd");
if (ok == false)
throw new AuthenticationException("u/p does not match");
我的服务器配置是:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<system.serviceModel>
<services>
<service name="TestWCFCustomAuth.CalculatorService" behaviorConfiguration="customCred">
<endpoint address="CalcSvc"
binding="netTcpBinding"
bindingConfiguration="secUP"
contract="ServiceInterface.ICalculatorService"/>
<endpoint address="mex" binding="mexHttpBinding" contract="ServiceInterface.ICalculatorService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:81/"/>
<add baseAddress="net.tcp://localhost:82/"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="secUP">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="customCred">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceCredentials>
<!--Service identity + encryption certificate-->
<serviceCertificate findValue="SHKIService" storeLocation="LocalMachine" storeName="Root" x509FindType="FindBySubjectName"/>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="TestWCFCustomAuth.UsernameAuthentication, TestWCFCustomAuth" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
并且客户端是自动生成的:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_ICalculatorService">
<security mode="Message">
<transport sslProtocols="None" />
<message clientCredentialType="UserName" />
</security>
</binding>
</netTcpBinding>
<wsHttpBinding>
<binding name="MetadataExchangeHttpBinding_ICalculatorService">
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:82/CalcSvc" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_ICalculatorService" contract="ServiceReference1.ICalculatorService"
name="NetTcpBinding_ICalculatorService">
<identity>
<certificate encodedValue="AwAAAAEAAAAUAAAA5Miz/2tl3pOxgQepIM62wzcAU+8gAAAAAQAAAK0BAAAwggGpMIIBCqADAgECAgkArWjRy0f0w98wCgYIKoZIzj0EAwIwFjEUMBIGA1UEAxMLU0hLSVNlcnZpY2UwHhcNMjEwMzI5MjEzNjUwWhcNMjYwMzI5MjEzNjUwWjAWMRQwEgYDVQQDEwtTSEtJU2VydmljZTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEAXE/ZelSAJ7+qjUTECzz2ZHFHTd6KtYsrbgdvBw3Xqt/G9R4IO01Rvxltir7FMfHzLgzsa4LdLkY3yAk/4rOqTPqAXY/sd/TpZOOB7ntZx02BEUvNiKouVu+yzIBMQxyW4aGZGOftiSOA28VKxNnaARN97/IoYyO0FhN+4vlKaPlTGFQMAoGCCqGSM49BAMCA4GMADCBiAJCAa88D2F5LuEFF0BL2+Vn1xIGSrjLo1YpiJk0DNJEbF0OOzH+xuKk/8H4yjQGO/yMmI8/pQWeU36Bu/D2xxJ0XqvtAkIA9udnx+h7lAsAYOtFMT12qHkVHInGWTzGHjNF0nrOldURa7X8B+tDeYrDJGBD+/9R2E4koJeGb0ubAmUl4Hrwyik=" />
</identity>
</endpoint>
<endpoint address="http://localhost:81/mex" binding="wsHttpBinding"
bindingConfiguration="MetadataExchangeHttpBinding_ICalculatorService"
contract="ServiceReference1.ICalculatorService" name="MetadataExchangeHttpBinding_ICalculatorService" />
</client>
</system.serviceModel>
</configuration>
我自己做了一个自签名证书,导入到Root目录下,所以证书配置也OK了。
测试客户端代码是:
var client = new CalculatorServiceClient("NetTcpBinding_ICalculatorService");
client.ClientCredentials.UserName.UserName = "Ole";
client.ClientCredentials.UserName.Password = "Pwd";
var res = client.Add(1, 2);
Console.WriteLine("Result: 0", res);
虽然我的代码与指南几乎相同,但出现错误:
System.ServiceModel.Security.SecurityNegotiationException: 'The caller was not authenticated by the service.'
Inner Exception:
FaultException: The request for security token could not be satisfied because authentication failed.
我搜索了几天的答案并尝试了许多其他配置。我最好的猜测是幕后发生了其他类型的身份验证?如果是这样 - 我该如何禁用它?
【问题讨论】:
【参考方案1】:官方文档中有完整的demo,使用用户名密码验证,也有证书验证。您只需要按照教程中的步骤操作即可成功运行。可以参考reference。
【讨论】:
谢谢!它与证书的创建方式或我没有将其复制到“CurrentUser/Trusted People”存储中的事实有关。稍后我将发布更多详细信息,以便任何有类似问题的人清楚。附: wsHttpBinding 和 netTcpBinding 都有效,我们现在可以选择!【参考方案2】:我缺少的步骤如下:
-
为 Windows 服务帐户授予对证书私钥的“读取”权限,如此处所述here
在
要将私钥的权限授予可以使用的帐户 mmc 的证书管理单元。可以启动mms.exe,选择“添加/删除 管理单元”在“文件”菜单中,选择“证书”管理单元并 选择本地计算机的“计算机帐户”。那么应该 选择个人商店的 SSL 证书,然后使用上下文菜单 “管理私钥...”。
运行客户端应用程序的计算机需要在CurrentUser\Trusted People
存储中拥有证书(即使客户端和服务器在同一台计算机上运行)。因此,我使用上一步中的 mmc 工具将没有私钥的证书导出到文件中,然后将该文件中的证书导入到 CurrentUser\Trusted People
存储中。我想我必须将此文件包含到我的客户的安装包中。
这次我也使用makecert.exe
工具创建证书,正如@Theobald Du 提供的msdn 参考中所建议的那样
命令是:
makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=<I used server's IP address> -sky exchange -pe
附:我也错过了那台机器上的 makecert.exe,所以我从 microsoft 下载了 Microsoft SDKs
【讨论】:
以上是关于WCF 和自定义身份验证(用户名/密码)的主要内容,如果未能解决你的问题,请参考以下文章