WCF的基本身份验证

11

我正在尝试使用WCF进行非常基本但安全的用户名/密码身份验证。

然而,当我查看ServiceSecurityContext.Current.PrimaryIdentity;的值时,它包含了我的Windows机器的凭据,并声称已得到授权(即使我尚未进行任何授权),而不是我提供给服务的用户名和密码。

我的服务的web.config如下所示:

<?xml version="1.0"?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="WsHttpBindingConfig">
          <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="None" />
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>

    </bindings>
    <protocolMapping>
        <add binding="wsHttpBinding" scheme="http" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        To browse web app root directory during debugging, set the value below to true.
        Set to false before deployment to avoid disclosing web app folder information.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

并且客户端应用程序的 app.config 如下

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IService1" />
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost/WcfSecuredService/Service1.svc"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
                contract="ServiceReference1.IService1" name="WSHttpBinding_IService1">
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

我使用以下代码调用服务

ServiceReference1.Service1Client clnt = new ServiceReference1.Service1Client();
            clnt.ClientCredentials.UserName.UserName = "peter";
            clnt.ClientCredentials.UserName.Password = "grr";

            string result=clnt.GetSecuredData();

我做错了什么?

请注意客户端应用程序和服务都在同一台机器上运行。我不知道身份是运行服务的机器还是从客户端传递给它的身份,因为它们都是相同的凭据...

我想另一个问题可能是“我如何获取传递给服务的用户名和密码?”

3个回答

5

我已经解决了这个问题

我需要创建一个自定义验证类,我在这里找到了 如何使用自定义用户名和密码验证器

我还需要对web.config进行一些更改

<?xml version="1.0"?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfService1Secure.Auth,WcfService1Secure" />
          </serviceCredentials>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="WsHttpBindingConfig">
          <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="None" />
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>

    </bindings>
    <protocolMapping>
        <add binding="wsHttpBinding" scheme="https" bindingConfiguration="WsHttpBindingConfig" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        To browse web app root directory during debugging, set the value below to true.
        Set to false before deployment to avoid disclosing web app folder information.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

以及app.config文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>

    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IService1" />
            </basicHttpBinding>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IService1">
                    <security mode="TransportWithMessageCredential">
                        <transport clientCredentialType="None" />
                        <message clientCredentialType="UserName" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://localhost/WcfService1Secure/Service1.svc"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
                contract="ServiceReference1.IService1" name="WSHttpBinding_IService1" />
        </client>
    </system.serviceModel>
</configuration>

现在用户已经通过请求验证,可以使用

ServiceSecurityContext.Current.PrimaryIdentity;

获取用户名。


我尝试了几次,但是当我访问 ServiceSecurityContext.Current.PrimaryIdentity 时得到的用户名似乎不是用于身份验证的那个。你能在这里解释一下吗? - Muli Yulzary

4

你需要在服务器端添加一个可以接收登录名和密码的方法,并将它们存储在变量或数据库中。
你的方法应该长成这样:

ServiceReference1.Service1Client clnt = new ServiceReference1.Service1Client();
clnt.ClientCredentials.UserName.UserName = "peter";
clnt.ClientCredentials.UserName.Password = "grr";

clnt.SignUp(clnt);  

或者
clnt.SignUp(login,password); 

你所做的工作可以在独立应用中运行,但无法在网络调用中运行。你对服务方法的调用应该像这样:http://MyService/IMyContract/MyAction1。希望这能帮到你。

我并不想每次调用时都手动传递凭据。Service1Client难道不应该自动传递它们吗? - coolblue2000
你现在的操作我怀疑不会自动通过,因为你处于SOAP环境中,这意味着一切都是分离的。 - BRAHIM Kamel
我认为代理通过SOAP头传递凭据?否则,为什么要有凭据属性呢?我看过许多这样的例子,只是它们似乎都使用sqlmembership提供程序以及其他我不想使用的东西。我只想通过自动传递的用户名和密码进行身份验证。 - coolblue2000

1
如果您正在使用PerSession实例,则可以在服务器上首先调用Authenticate,并在成功后在服务器上维护一个标志,在执行之前检查标志。这意味着客户端需要首先调用Authenticate方法,然后才能调用其他方法。
如果不使用PerSession实例,则可以为服务器创建自己的代理类,并在创建代理实例时接受LoginName/Password,它可以在每个调用中通过头传递并在服务器端实现自定义ServiceAuthorizationManager,它可以从OperationContext.Current.IncomingMessageHeaders中检索凭据并进行验证。

关于非持久化实例,这不是 scf 默认应该执行的操作吗(因此有凭证属性)? - coolblue2000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接