65.9K
CodeProject 正在变化。 阅读更多。
Home

WCF REST 服务上的基本身份验证

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (44投票s)

2011 年 1 月 24 日

CPOL

6分钟阅读

viewsIcon

364933

downloadIcon

10857

一个程序集,可为 IIS 托管的 WCF REST 服务添加基本身份验证,以便针对任何后端进行验证。

目录

引言

本文介绍了一种使用基本身份验证保护基于 REST 的服务的方法。该服务本身使用 Microsoft Windows Communication Foundation 实现。应对任何类型的后端进行凭据身份验证。例如,针对 Active Directory、自定义文件或数据库进行身份验证。基本身份验证是 WCF 和 IIS 组合的标准功能,但缺点是仅能针对 Active Directory 进行身份验证。

尽管基本身份验证是保护网站或服务的一种方法,但身份验证机制本身并不安全。用户名和密码以 Base64 编码形式通过 Internet 发送。为了提高安全性,服务器应使用 HTTPS 提供服务。HTTPS 可保护通道,使 Base64 编码的用户名和密码无法解密。基本身份验证的替代方法是摘要身份验证,这也适用于 WCF REST。我创建了一篇 CodeProject 上的独立文章,介绍了 WCF REST 服务上的摘要身份验证。

本文及提供的源代码可以有两种用途。第一,只需下载代码,然后向下滚动到“使用代码”部分,并使用基本身份验证实现你的 WCF 服务。第二,你可以使用本文来了解基本身份验证到底是什么,以及 WCF REST 如何扩展。

基本身份验证

基本身份验证是 HTTP 1.0 中定义的标准协议,它定义了一种身份验证方案。在这种方案中,客户端必须使用用户 ID 和密码向自己进行身份验证。基本身份验证在 RFC 2617 中有描述。当客户端请求一个受基本身份验证保护的网站的资源时,服务器会返回一个 401“未授权”响应。在此响应中,服务器添加了一个指示该网站受基本身份验证保护的标识。服务器会将 WWW-Authenticate: Basic realm="site" 添加到响应头中;Basic 表示身份验证方案是基本身份验证,而 realm 是一个字符串,指示网站的哪个部分受到保护。它在实际的身份验证机制本身中没有进一步的用途。Internet 浏览器根据此响应消息生成一个对话框。此对话框显示区域(realm)并允许用户输入用户名和密码。

当用户输入用户名和密码后,客户端会重新发送请求,但会在请求头中添加 "Authorization: Basic SGVsbG8gQmFzZTY0"Basic 后面的字符是用户名和密码,它们用单个冒号 ":" 分隔,并以 Base64 编码的字符串形式表示。服务器解码该字符串,提取凭据,并根据后端进行验证。当凭据成功验证后,服务器会将请求的内容返回给客户端。

扩展 WCF REST

为了能够将基本身份验证与 WCF REST 集成,我们需要扩展 WCF 框架的功能。该扩展分为三个步骤:

  • 找到应用于服务所有操作的行为的扩展点
  • 创建基于现有标准的自定义身份验证机制
  • 根据给定的凭据创建安全上下文

本文将详细介绍每个步骤。

查找扩展点,RequestInterceptor

WCF REST 是 WCF REST Starter Kit 的一部分。与标准 WCF 不同,WCF Starter Kit 提供了一种简单的方法将行为应用于所有服务器操作。在标准 WCF 中添加此行为可能会相当复杂。WCF REST 使用 Request Interceptors。Request Interceptors 将你与更复杂的扩展点隔离开来。Request Interceptors 在 WCF 通道级别执行,使你能够解释传入的请求并生成适当的响应。

public class MyRequestInterceptor : RequestInterceptor
{
   public override void ProcessRequest(ref RequestContext requestContext)
   {
      //Access request with requextContext.RequestMessage
   }
}

通过从 RequestInterceptor 基类派生新类,你可以创建自定义请求拦截器。ProcessRequest 方法会为到达服务的每个请求执行。在此方法中,你可以读取请求头,并在存在时解码 Base64 编码的字符串。如果不存在,则生成一个在头中包含 WWW-Authenticate: Basic realm="site" 的响应。如果 **存在**,我们创建一个新的安全上下文并将凭据设置在此上下文中。

将自定义 RequestInterceptor 链接到服务

可以通过创建自定义 ServiceHostFactory 来将 RequestInterceptor 链接到服务。ServiceHostFactory 负责在托管环境(Managed Hosting Environments)中提供 ServiceHost 实例。托管环境是指在 IIS 中托管的服务。ServiceHostFactoryCreateServiceHost 方法会创建一个新的 WebServiceHost,并将新的 RequestInceptor 添加到 Interceptors 集合中。

public class BasicAuthenticationHostFactory : ServiceHostFactory
{
   protected override ServiceHost CreateServiceHost(Type serviceType, 
                                  Uri[] baseAddresses)
   {
      var serviceHost = new WebServiceHost2(serviceType, true, baseAddresses);
      serviceHost.Interceptors.Add(RequestInterceptorFactory.Create(
                    "DataWebService", new CustomMembershipProvider()));
      return serviceHost;
   }
}

这个新的 ServiceHostFactory 通过服务的标记文件(.svc)链接到服务。在标记文件中,你添加服务的宿主工厂。

Factory="WCFServer.BasicAuthenticationHostFactory"

自定义身份验证方法,创建自定义 MembershipProvider

提供的源代码使用 MembershipProvider 来实际执行客户端凭据的身份验证。你可以通过从 MembershipProvider 派生来编写自己的成员身份提供程序。唯一需要实现的方法是 ValidateUser 方法。这是代码用来验证用户的。这个自定义的 MembershipProviderServiceHostFactoryCreateServiceHost 方法中链接到 RequestInterceptor

public class CustomMembershipProvider : MembershipProvider
{
   public override bool ValidateUser(string username, string password)
   {
      //perform validation
      return false;
   }

   .....
}

创建安全上下文

在对传入请求进行身份验证后,我们必须创建并设置安全上下文。这使得可以在服务方法中使用客户端凭据。WCF 使用线程本地的 ServiceSecurityContext 来实现此目的。请求经过身份验证后,会创建一个新的 ServiceSecurityContext 并添加到传入的请求中。请参阅下面提供的源代码中创建新的 ServiceSecurityContext 的代码。

internal ServiceSecurityContext Create(Credentials credentials)
{
   var authorizationPolicies = new List<iauthorizationpolicy>();
   authorizationPolicies.Add(authorizationPolicyFactory.Create(credentials));
   return new ServiceSecurityContext(authorizationPolicies.AsReadOnly());
}

这样就可以在服务方法中检索用户名,如下所示:

[OperationContract]
public string DoWork()
{
   string name = ServiceSecurityContext.Current.PrimaryIdentity.Name;
   return "Hello " + name;
}

使用代码

如果你想使用提供的源代码通过基本身份验证保护你自己的 WCF REST 服务,你需要执行以下步骤:

  • 添加对 BasicAuthenticationUsingWCF 程序集的引用
  • 创建一个新的派生自 MembershipProvider 的 Membership Provider
  • 针对你的后端安全存储实现 ValidateUser 方法
  • 创建一个自定义的 BasicAuthenticationHostFactory,请参阅提供的源代码中的示例
  • 将新的 BasciAuthenticationHostFactory 添加到 .svc 文件的标记中

值得注意的点

尽管基本身份验证是保护网站或服务的一种方法,但身份验证机制本身并不安全。用户名和密码以 Base64 编码形式通过 Internet 发送。为了提高安全性,服务器应使用 HTTPS 提供服务。HTTPS 可保护通道,使 Base64 编码的用户名和密码无法解密。

当你想向第三方提供服务并提供易于互操作的服务时,基本身份验证与 HTTPS 结合使用的情况很常见。然而,如果你同时控制客户端和服务器端,WCF 提供了一种可以通过配置添加到你的服务中的标准安全机制。

本文及源代码基于 Pablo M. Cibraro 的示例 Pablo M. Cibraro,该解决方案的功劳归于他。

提供的源代码是使用 TDD 开发的,并使用 NUnit 框架来创建和执行测试。在单元测试中,Rhino Mocks 被用作模拟框架。

历史记录

  • 2011 年 1 月 23 日
    • 初始发布和第一个版本。
  • 2011 年 2 月 28 日
© . All rights reserved.