【问题标题】:WCF doesn't work with disabled anonymous authentication option in IISWCF 不适用于 IIS 中禁用的匿名身份验证选项
【发布时间】:2014-10-24 12:59:09
【问题描述】:

我做了一个非常简单的 WCF 应用程序,发现了一个我无法解决的问题。关于 WCF 服务,如果启用了匿名身份验证,它实际上正在工作,但是当我在 IIS 上禁用此功能时,它给了我一个错误:HTTP 请求未经客户端身份验证方案“匿名”授权。从服务器收到的身份验证标头是 ''

这是 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>
<services>
  <service name="WCFTestApplication.WCFTestApplication">
    <endpoint address="" 
              binding="wsHttpBinding"
              bindingConfiguration="WCFTestAppBinding" 
              contract="WCFTestApplication.IWCFTestApplication" 
     />
    <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
  </service>
  </services>
  <bindings>
  <wsHttpBinding>
    <binding messageEncoding="Text" name="WCFTestAppBinding">
      <security mode="TransportWithMessageCredential">
        <message clientCredentialType="Windows"/>
        <transport clientCredentialType="Windows" proxyCredentialType="Windows"/>
      </security>
    </binding>
  </wsHttpBinding>
  </bindings>
  <behaviors>
  <serviceBehaviors>
    <behavior>
      <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
      <serviceMetadata httpGetEnabled="false" 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="false"/>
    </behavior>
    </serviceBehaviors>
  </behaviors>
  <!--protocolMapping>
    <add binding="basicHttpsBinding" scheme="https" />
  </protocolMapping-->    
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
 </system.serviceModel>
 <system.webServer>
 <validation validateIntegratedModeConfiguration="false"/>
 <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="false"/>
 </system.webServer>

这是客户端应用程序源:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net;

namespace WCFClientApplication
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private string _name = "";
    private string _passwd = "";
    private string _domain = "";

    public string UserName
    {
        get { return _name; }
        set { _name = value; }
    }

    public string Password
    {
        get { return _passwd; }
        set { _passwd = value; }
    }

    public string Domain
    {
        get { return _domain; }
        set { _domain = value; }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //if (!String.IsNullOrEmpty(usrTxt.Text) || !String.IsNullOrEmpty(passTxt.Text) || !String.IsNullOrEmpty(domainTxt.Text))
        //{
            UserName = usrTxt.Text;
            Password = passTxt.Text;
            Domain = domainTxt.Text;

            WCFClientProxy.WCFTestApplicationClient client = new WCFClientProxy.WCFTestApplicationClient();
            client.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(UserName, Password, Domain);
            //System.Net.ServicePointManager.ServerCertificateValidationCallback += (se, cert, chain, sslerror) => { return true; };
            textBox1.Text = client.GetData();
        //}
        //else
        //{
        //    MessageBox.Show("Fields like username, password and domain must not be blank...!","Warning...!",MessageBoxButtons.OK, MessageBoxIcon.Warning);
        //}
    }

    private void domainTxt_MouseHover(object sender, EventArgs e)
    {
        tipLbl.Visible = true;
    }

    private void domainTxt_MouseLeave(object sender, EventArgs e)
    {
        tipLbl.Visible = false;
    }
}
}

wcf 接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFTestApplication
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IWCFTestApplication
{

    [OperationContract]
    string GetData();

    // TODO: Add your service operations here
}
}

类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFTestApplication
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
public class WCFTestApplication : IWCFTestApplication
{
    public string GetData()
    {
        return "WCF is working, and message is authenticated and encrypted";
    }
}
}

我在某处读到匿名身份验证与 MEX 端点和通信有关,但我如何才能保留 Windows 登录,并禁用匿名用户登录,以便在没有适当凭据的情况下无法使用 WCF?

【问题讨论】:

  • 为什么要启用表单验证?此外,根据配置,您使用了“Windows”身份验证,并且它不存在于 IIS 中。
  • 嗨,需要解释一下吗?我在配置文件中设置了 Windows 身份验证,但您不需要为客户端凭据提供用户名、密码和域吗? client.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(UserName, Password, Domain);
  • 你是对的。在 IIS 身份验证中不显示“Windows 身份验证”。您必须通过进入“启用或禁用功能”来安装该协议。
  • 您可以测试的另一件事是“尝试不使用 https”。
  • 你尝试过这样的事情吗? msdn.microsoft.com/en-us/library/ff648505.aspx

标签: c# wcf


【解决方案1】:

我终于找到了解决办法:

Web.config

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

<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<authentication mode="Windows" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<services>
  <service name="WCFTestApplication.WCFTestApplication">
    <endpoint address="" 
              binding="wsHttpBinding"
              bindingConfiguration="WCFTestAppBinding" 
              contract="WCFTestApplication.IWCFTestApplication"
     />
    <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
  </service>
</services>
<bindings>
  <wsHttpBinding>
    <binding messageEncoding="Text" name="WCFTestAppBinding">
      <security mode="Transport">
        <transport clientCredentialType="Ntlm"/>
      </security>
    </binding>
  </wsHttpBinding>
</bindings>
<behaviors>
  <serviceBehaviors>
    <behavior>
      <serviceCredentials>
        <windowsAuthentication allowAnonymousLogons="false" includeWindowsGroups="true"/>
      </serviceCredentials>
      <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
      <serviceMetadata httpGetEnabled="false" 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>
<!--protocolMapping>
    <add binding="basicHttpsBinding" scheme="https" />
</protocolMapping-->    
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<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="false"/>
</system.webServer>
</configuration>

app.config

<?xml version="1.0"?>
<configuration>
<configSections>
</configSections>
<startup>
  <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
<system.serviceModel>
    <bindings>
        <wsHttpBinding>
            <binding name="WSHttpBinding_IWCFTestApplication">
                <security mode="Transport">
                    <transport clientCredentialType="Ntlm" />
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>
    <client>
        <endpoint address="https://vladimir.intra.jv.hr/WCFTestApplication.svc"
            binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IWCFTestApplication"
            contract="WCFTestApplication.IWCFTestApplication" name="WSHttpBinding_IWCFTestApplication">
            <identity>
                <servicePrincipalName value="host/vladimir.intra.jv.hr" />
            </identity>
        </endpoint>
    </client>
   </system.serviceModel>
  </configuration>

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net;

namespace WCFClientApplication
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private string _name = "";
    private string _passwd = "";
    private string _domain = "";

    public string UserName
    {
        get { return _name; }
        set { _name = value; }
    }

    public string Password
    {
        get { return _passwd; }
        set { _passwd = value; }
    }

    public string Domain
    {
        get { return _domain; }
        set { _domain = value; }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //if (!String.IsNullOrEmpty(usrTxt.Text) || !String.IsNullOrEmpty(passTxt.Text) || !String.IsNullOrEmpty(domainTxt.Text))
        //{

            UserName = usrTxt.Text;
            Password = passTxt.Text;
            Domain = domainTxt.Text;

            WCFTestApplication.WCFTestApplicationClient client = new WCFTestApplication.WCFTestApplicationClient();

            client.ClientCredentials.Windows.ClientCredential.Domain = Domain;
            client.ClientCredentials.Windows.ClientCredential.UserName = UserName;
            client.ClientCredentials.Windows.ClientCredential.Password = Password;
            //this part needs to be modified, so certificate can be accepted from other machines as well. 
            System.Net.ServicePointManager.ServerCertificateValidationCallback += (se, cert, chain, sslerror) => { return true; };
            textBox1.Text = client.GetData();
            client.Close();
        //}
        //else
        //{
        //    MessageBox.Show("Fields like username, password and domain must not be blank...!","Warning...!",MessageBoxButtons.OK, MessageBoxIcon.Warning);
        //}
    }

    private void domainTxt_MouseHover(object sender, EventArgs e)
    {
        tipLbl.Visible = true;
    }

    private void domainTxt_MouseLeave(object sender, EventArgs e)
    {
        tipLbl.Visible = false;
    }
    }
}

界面

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFTestApplication
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IWCFTestApplication
{

    [OperationContract]
    string GetData();

    // TODO: Add your service operations here
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFTestApplication
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
public class WCFTestApplication : IWCFTestApplication
{
    public string GetData()
    {
        return "WCF is working, and message is authenticated and encrypted";
    }
}
}

我粘贴了整个代码,以便其他人可以研究一下并找到创建自己的配置所需的一切。所以我的设置包括 SSL、wsHttpBinding 和 Windows/NTLM 身份验证到 Web 服务。

问题是,我走的是完全不同的方向,在 IIS 配置上浪费时间,并试图打破我的想法,为什么在我禁用匿名身份验证时身份验证一开始就不起作用。问题是:我不必为 IIS 烦恼,因为整个安全部分都是通过 WCF 完成的。所以我所做的是启用匿名身份验证。您需要它有一个很好的理由:没有它,您将无法更新客户端 Web 参考。 (至少我不能……)。是啊。这并不意味着任何人都可以在不登录的情况下使用 WCF 服务......第二件事让我感到困惑,我在同一个域中,所以每次启用匿名身份验证时,我都会从 WCF 服务获得结果而没有任何日志记录信息在类的客户端代理对象中提供。问题是,当我使用不在域中的其他计算机时,只要我不提供用户名、密码和域,我就无法获得结果。

附:最后但并非最不重要的一点是,客户端应用程序中的代码部分:System.Net.ServicePointManager.ServerCertificateValidationCallback += (se, cert, chain, sslerror) =&gt; { return true; }; 是从安全角度来看的,这是一个真正的安全风险。我添加了此代码进行测试,因为不在域中的其他机器无法为具有权限“server.domain”的 SSL/TLS 安全通道建立信任关系。

最后,感谢大家的帮助。 干杯。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-09
    相关资源
    最近更新 更多