我如何使用基本身份验证的WCF端点托管在IIS上的SSL与json



我有一个rest式的WCF服务,它托管在IIS中,通过SSL使用WebHttpBinding和json。

服务现在需要认证/授权。似乎有很多旧的信息可能仍然与实现身份验证的不同方法相关,也可能不相关,但它们似乎都不适用于这种特定的端点绑定混合。

我尝试过许多不同类型的绑定,使用基于SOAP的身份验证实现起来相对简单,但使用json就不是这样了。大多数信息都表明身份验证被IIS篡夺了,但我已经克服了这个障碍。

我想使用基本的身份验证,像这样(示例提琴请求):

GET https://secure.dev.myco.local/api/users/v1/0b7478c5-d25a-4039-9cf4-614b2ef4e04d HTTP/1.1
User-Agent: Fiddler
Host: secure.dev.myco.local
Authorization: Basic dGVzdHVzZXI6UEAkJHcwcmQh

我的业务行为配置如下:

<behavior name="SslBehavior">
  <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
  <serviceDebug includeExceptionDetailInFaults="true" />
  <serviceCredentials>
    <serviceCertificate findValue="CN=*.dev.myco.local" x509FindType="FindBySubjectDistinguishedName" storeLocation="LocalMachine" />
  </serviceCredentials>
  <serviceAuthenticationManager authenticationSchemes="Basic" 
                                serviceAuthenticationManagerType="Test.Api.TestAuthenticationManager, Test.Api" />
</behavior>
绑定定义:

<webHttpBinding>
  <binding name="ProductBinding">
    <security mode="Transport" />
  </binding>
  <binding name="UserBinding">
    <security mode="Transport" />
  </binding>
</webHttpBinding>

和服务定义:

<service name="Test.Api.ApiService" behaviorConfiguration="SslServiceBehavior">
    <host>
      <baseAddresses>
        <add baseAddress="https://secure.dev.myco.local/api/"/>
      </baseAddresses>
    </host>
    <endpoint address="products/v1" name="ProductService" contract="Test.Api.Product.V1.IProductService" binding="webHttpBinding" bindingConfiguration="ProductBinding" behaviorConfiguration="ProductEndpointWebHttpBehavior" />
    <endpoint address="users/v1" name="UserService" contract="Test.Api.User.V1.IUserService" binding="webHttpBinding" bindingConfiguration="UserBinding" behaviorConfiguration="UserEndpointWebHttpBehavior" />
</service>

我的用户帐户将保存在数据库中的一个表中,身份验证将凭据传递给身份验证提供者,如果用户进行身份验证,该提供者将返回IIdentity、IPrincipal或布尔值。然后根据需要将其附加到上下文。因此,我需要我的应用程序来处理身份验证,而不仅仅是将身份验证交给IIS。

我能够成功地访问来自HTTP报头的凭据,并通过从身份验证管理器派生并使用以下示例代码重写authenticate方法来对用户进行身份验证—生成IPrincipal并将其附加到我所期望的上下文中:

public class TestAuthenticationManager : ServiceAuthenticationManager
{
    public override ReadOnlyCollection<IAuthorizationPolicy> Authenticate(ReadOnlyCollection<IAuthorizationPolicy> authPolicy, Uri listenUri, ref Message message)
    {
        var requestProperties =
            (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name];
        var rawAuthHeader = requestProperties.Headers["Authorization"];
        AuthenticationHeader authHeader = null;
        if (AuthenticationHeader.TryDecode(rawAuthHeader, out authHeader)) ;
        {
            var identity = new GenericIdentity(authHeader.Username);
            var principal = new GenericPrincipal(identity, new string[] {});
            var httpContext = new HttpContextWrapper(HttpContext.Current)
            {
                User = principal,
            };
            if (httpContext.User != null)
                return null;
        }
        SendUnauthorizedResponse();
        return base.Authenticate(authPolicy, listenUri, ref message);
    }
    private void SendUnauthorizedResponse()
    {
        HttpContext.Current.Response.StatusCode = 401;
        HttpContext.Current.Response.StatusDescription = "Unauthorized";
        HttpContext.Current.Response.Headers.Add("WWW-Authenticate", "Basic realm="site"");
        HttpContext.Current.Response.End();
    }
}

在退出验证方法时,我的服务响应以下错误:

服务器在处理请求时遇到错误。一个例外消息是"调用者未通过服务进行身份验证。"看到有关服务器日志的详细信息。异常堆栈跟踪是:atSystem.ServiceModel.Dispatcher.AuthenticationBehavior.Authenticate (MessageRpc&rpc)System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11 (MessageRpc&System.ServiceModel.Dispatcher.MessageRpc.Process(布尔值isOperationContextSet)

如果我从配置中删除身份验证引用(如您所料),那么服务可以正常工作,但是用户是未经过身份验证的,因此,如果我向任何服务方法添加任何授权,它将无法处理该部分。

该方法似乎没有错误地退出。有人能解释一下为什么这个配置会出现这个异常吗?我一直在谷歌上搜索,直到奶牛回家,我发现的信息都与其他配置的有限数量的变化有关,但没有WebHttpBinding与Json托管在IIS中使用SSL。

if (httpContext.User != null) return null;行是问题所在。如果Authenticate方法返回Null作为授权策略,Dispatcher将抛出异常。

这里的条件总是为真,所以它总是返回null。您需要返回一个授权策略,而不是null。

最新更新