c# - WCF "Basic"在 IIS 中托管时出现传输安全问题

标签 c# .net wcf wcf-security

我正在尝试使用 HTTPS/SSL、基本客户端凭据和 WebHttpBinding 来保护新的 .Net 4.5 WCF 服务的安全。通过在线阅读,我发现了一系列很好的Blog Posts from Allen Conway我用它作为模板。

WCF配置

 <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="webInteropSecureBinding" allowCookies="false" maxBufferPoolSize="2097152" maxBufferSize="2097152" maxReceivedMessageSize="2097152">
          <security mode="Transport">
            <transport clientCredentialType="Basic"></transport>
          </security>
        </binding>
      </webHttpBinding>
    </bindings>
    <services>
      <service name="PsmDataProvider.PsmProvider" behaviorConfiguration="SecureRest">
        <clear />
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="webInteropSecureBinding" name="PsmProvider" contract="PsmDataProvider.IPsmProvider" behaviorConfiguration="webHttpBehavior" />
        <endpoint address="mex" binding="mexHttpsBinding" name="mex" contract="IMetadataExchange" listenUriMode="Explicit" />
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost:44300/PsmProvider/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SecureRest">
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom" 
                                    customUserNamePasswordValidatorType="PsmDataProvider.Security.CustomerUserNamePasswordValidator, PsmDataProvider"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="webHttpBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

客户用户名密码验证器

我已经删除了 CustomerUserNamePasswordValidator 实现,并确认在引发异常之前调用了构造函数。

using System;
using System.IdentityModel.Selectors;

namespace PsmDataProvider.Security
{
    internal class CustomerUserNamePasswordValidator : UserNamePasswordValidator, ICustomerUserNamePasswordValidator 
    {

        public CustomerUserNamePasswordValidator()
        {
        }

        public override void Validate(string userName, string password)
        {          
            if (userName == null) throw new ArgumentNullException("userName","The username must be provided in the request to access this service");
            if (password == null) throw new ArgumentNullException("password", "The password must be provided in the request to access this service");

        }
    }
}

当我尝试通过 IIS Express 在 VS2012 中运行代码时,服务无法启动并出现以下错误。

enter image description here

如果我删除 clientCredentialType从配置中,它可以工作,但我需要在服务上以及将来可能在方法级别上使用用户名/密码验证的额外安全性。

这是我在 WCF 配置中配置不正确还是 IISExpress 中的配置有问题?

请帮忙...

最佳答案

问题似乎是在 IIS 中托管服务时使用基本身份验证,因为 IIS 想要处理身份验证。

这在 this MSDN blog post 中进行了讨论

In the version of WCF that shipped with .Net Framework 3.0 we didn't support custom validators with transport level HTTP security. We received much feedback from the community that this was a highly desired feature, so I'm happy to say we added support for this scenario in the 3.5 release of the .Net Framework. Note that this is only supported under self hosted services.

有一个决议,如 Allen Conway's Blog Post 中讨论的那样通过实现源自 ServiceAuthorizationManager 的自定义授权管理器

自定义授权管理器

public class CustomAuthorizationManager : ServiceAuthorizationManager 
{
    private const string UserName = "username";
    private const string Password = "password";

    protected override bool CheckAccessCore(OperationContext operationContext)
    {
        string authHeader = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];

        if ((authHeader != null) && (authHeader != string.Empty))
        {
            string[] svcCredentials = System.Text.ASCIIEncoding.ASCII
                                        .GetString(Convert.FromBase64String(authHeader.Substring(6)))
                                        .Split(':');

            var user = new { Name = svcCredentials[0], Password = svcCredentials[1] };

            if ((user.Name.Equals(UserName) && user.Password.Equals(Password)))
                return true;
            else
                return false;
        }
        else
        {
            WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate: Basic realm=\"PsmProvider\"");
            throw new WebFaultException(HttpStatusCode.Unauthorized);
        }
    }

}

配置

  <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="webInteropSecureBinding" allowCookies="false" maxBufferPoolSize="51200" maxBufferSize="51200" maxReceivedMessageSize="51200">
          <security mode="Transport"/>
        </binding>
      </webHttpBinding>
    </bindings>
    <services>
      <service name="PsmDataProvider.PsmProvider" behaviorConfiguration="SecureRest">
        <clear />
        <endpoint binding="webHttpBinding" bindingConfiguration="webInteropSecureBinding" 
                    name="PsmProvider" contract="PsmDataProvider.IPsmProvider" behaviorConfiguration="webHttpBehavior" />
        <endpoint address="mex" binding="mexHttpsBinding" name="mex" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost:44300/PsmProvider/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SecureRest">
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceAuthorization serviceAuthorizationManagerType="PsmDataProvider.Security.CustomAuthorizationManager, PsmDataProvider"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="webHttpBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

注意

另请注意 Travich 关于 IIS/IIS Express 配置的评论

Travich said... One thing to help other users. It was briefly stated, but something I overlooked... Turn off Basic Auth in IIS and remove tag from your webHttpBinding!

对我有用。

关于c# - WCF "Basic"在 IIS 中托管时出现传输安全问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18123082/

相关文章:

c# - 文本框自动完成无法正常工作

c# - 在 Windows 64 位下设计 C# 应用程序是一个好习惯吗?

c# - 如何在触发时加载预制件以便我的游戏继续进行?

.net - 检查 CD-ROM 托盘状态

c# - 应用在添加 Oauth 时重定向到 Account/AccessDenied

.net - Win64 下可靠的 .NET 3.5 CPU 分析器,具有命令行支持?

.net - 如何确定用户是否可以以管理员身份运行?

wcf - Windows 8 Metro RSA 加密 : AsymmetricKeyAlgorithmProvider ImportPublicKey Fails

wcf - 关于使用 WCF 进行 HMAC 身份验证的说明

c# - EF、Code First、WCF => 空集合问题