WCF 和自定义身份验证 (username/password)

WCF and custom Authentication (username/password)

我有 Windows 通过 Internet 连接到 WCF 服务的表单客户端。 WCF 服务托管在专用 Windows 帐户下的 Windows 服务 运行 中。 我想通过检查用户名+密码对来根据数据库对客户端进行身份验证。

为了避免像这样的数百种方法:

public int Add(string User, string Password, int A, int B)

我使用以下指南覆盖 UserNamePasswordValidator class:

http://blog.clauskonrad.net/2011/03/how-to-wcf-and-custom-authentication.html

我的自定义身份验证器 class 匹配示例

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>

我制作了一个自签名证书并导入到根目录下,所以证书配置也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.

找了好几天答案,也试了很多其他配置。我最好的猜测是幕后发生了某种其他类型的身份验证?如果是这样 - 我该如何禁用它?

官方文档中有完整的demo,使用了用户名密码验证,也有证书验证。您只需按照教程中的步骤操作即可运行成功。可以参考reference.

我遗漏的步骤如下:

  1. 按照此处所述,为 Windows 服务帐户授予对证书私钥的“读取”权限

To grant permission on the private key to the account one can use Certificate Snap-In of mmc. One can start mms.exe, choose "Add/Remove Snap-in" in the "File" menu, choose "Certificates" Snap-in and to choose "Computer account" of the Local computer. Then one should select the SSL certificate of Personal store and then use context menu "Manage Private Keys...".

  1. 计算机 运行 客户端应用程序需要在 CurrentUser\Trusted People 存储中拥有证书(即使客户端和服务器 运行 在同一台计算机上) .因此,我使用上一步中的 mmc 工具将没有私钥的证书导出到一个文件中,然后将该文件中的证书导入到 CurrentUser\Trusted People 存储中。我想我必须将这个文件包含到我的客户的安装包中。

  2. 另外这次我使用了makecert.exe工具来创建证书,正如@Theobald Du提供的msdn参考中所建议的 命令是:

    makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=<I used server's IP address> -sky exchange -pe

P.S。我在那台机器上也找不到 makecert.exe,所以我从 microsoft

下载了 Microsoft SDK