[转]WCF 4 安全性和 WIF 简单介绍

本章内容

转自:

q
序幕

本帖简介 .NET 新一代的 Windows Identity Foundation (WIF) 框架,及其与
WCF 的协同运用。文后提供两本 WCF 4 书籍、一本 WIF
书籍的下载地址,及许多网络上的学习资源。

q
利用声明保证资源安全

本帖并非要详述 WIF 的功能细节,事实上重点在文后的 30
多项学习资源,提供网友将来需要学习时作为参考。不然此等新技术,等到它普及不知要多少年后。一般传统产业、非软件公司的
IT 部门,可能到现在连 WCF、WF
都没听过,小弟我还知道台湾某些金融业的开发部门,还在用 .NET 1.1 / VS
2003 开发,系统只求稳定即可;至于新技术…,浅尝学习一下即可
(除非时间太多),重点在于怎么搭配产业 know-how
和企业的需求,以作导入与否的评估。

q
使用XSI运用基于声明的安全

书籍下载地址,请参考文后「学习资源」的 [3], [31]。本帖后续也简单介绍「Programming
WCF 4 (Wrox)」这本书第九章里,与 WIF 有关的部分内容。

q
基于声明的安全和联合安全

威尼斯官方网站 1 威尼斯官方网站 2
图 1 书籍封面及目录

10.1 序幕

 

从华盛顿州的西雅图向南,驾车前往俄勒冈,在刚路过雄伟的奥林匹亚州议会大厦后你就需要选择一下线路了。你可以继续在州际5号公路上行驶,或者先向西然后往南转上美国101号高速公路。

若您下载「Programming WCF 4
(Wrox)」书上的代码,发现编译时找不到「Microsoft.IdentityModel
程序集」,那是因为您的 Windows 尚未安装 WIF。只要去微软的网站下载和安装
[7] (安装文件仅约 1.5 MB),Visual
Studio 2008/2010 即可正确编译。以下是 WIF 安装的系统需求:

在州际5号公路上开车会让你感觉很轻松,因为往波特兰的大部分路段都是双向多车道的,能满足很大的车流量,并且沿途众多的加油站、餐厅、商店和旅馆都有清晰的指示牌。


相反,美国101号高速公路大部分的路段都是双向单车道的。路上的一些紧连着的弯道使司机很少甚至没有犯错的余地。在陡坡上被一辆大卡车一直挡着而又不能超车,是常见的事情。路边的设施又少又不大好,而且还隔得很远。

WIF 安装需要下列操作系统之一:
    * Windows 7(32 位或 64 位)
    * Windows Server 2008 R2 (64 位)
    * Windows Server 2008 + Service Pack 2 (32 位或 64 位)
    * Windows Vista + Service Pack 2 (32 位或 64 位)
    * Windows Server 2003 + Service Pack 2 (32 位或 64 位)
    * Windows Server 2003 R2 (32 位或 64 位)

然而这条路的风景却让人情不自禁地赞叹,那些紧连着的弯道两旁,一边是茂密的森林,一边就是浩瀚的太平洋。

WIF 需要有下列 .NET 版本或更高之版本:

上101高速公路后不久,你还可以在格雷斯港县的卡拉克斯餐厅停车休息一下,问问服务小姐,在那一长串的菜单里有什么可以推荐的。她的回答会让你有点吃惊:上面所有的都很不错啊。不过,最后你也会发现她说的一点也不假。

    * .NET 3.5 + Service Pack 1

至于 Windows Identity Foundation (WIF) 为何物?我曾在今年年初的「dotNET
Framework 4 新版 API
和命名空间」一文中
[2] 有稍微提到,当时并提供 .NET 4
的海报 pdf 下载,其类库在 .NET 4 新建的内容如下图
2,除此之外的命名空间应是在 .NET 3.5 时代即已存在 [6]。

威尼斯官方网站 3
图 2 WIF 在 .NET 4
里新加入的命名空间和类

 

Windows Identity Foundation (WIF, Windows 身份验证基础),先前代号为
Geneva 框架。若要用最简单的几句话来介绍,其为微软 .NET
平台的一个身份标识新框架,它将 WS-Trust 和 WS-Federation
协议抽象化,帮助开发者从应用程序中排除掉身份验证的处理,只用单一的编程模式和工具,即可构建基于「声明」的身份验证、授权、及其他安全功能,改善开发者生产力
(避免过去应用程序复杂的认证过程)、增强程序安全性、提供协同合作性,以在
Azure 云端,或非云端的 ASP.NET 与 WCF 应用程序,实现「单点登陆
(SSO)」、个性化、联合化、强验证、身份验证委托,以及其他验证功能。 WIF
可便捷地将非云端应用程序迁移至 Windows Azure
(从身份验证的角度),反之亦然 [10], [16]。

在 WIF 中,除了更容易实现单点登录,且应用程序不用再负责以下操作 [4]:

    *  对用户进行身份验证。
    *  存储用户帐户和密码。
    *  调用企业目录以查看用户标识的详细信息。
    *  从其他平台或公司,与既有的标识系统集成。

WIF 可用于任何使用 .NET 3.5 SP1、.NET 4 的 Web 应用程序或 Web 服务。但
WIF
只是微软的「联合身份标识」软件系列的一部分。「联合身份标识」包含三种组件:Active
Directory Federation Services (ADFS) V2
(之前称为「Geneva」服务器)、Windows CardSpace、WIF。
这三种组件共同构成了微软基于「声明」的新访问平台其核心。

对于使用 WIF 的 WCF 服务,开发者不再需要用典型 WCF
身份验证和授权行为初始化 ServiceHost 实例。WIF
取代了这些行为,并提供更简洁的方式进行一般的安全配置、身份验证设置 [5], [17]。

ServiceHost 类 (.NET 4、.NET 3.5)

WSTrustServiceHost 类

威尼斯官方网站 4
图 3 WIF 的 WSTrustServiceHost
类,继承自 WCF 的 ServiceHost 类,并添加了「安全令牌服务 Security Token
Service (STS)」的功能

 

WIF 框架提供了用于托管 STS 实例的自定义 ServiceHost 类型
WSTrustServiceHost。下方代码说明了如何在自托管环境中构造
WSTrustServiceHost 类型 [20]:

威尼斯官方网站 5
图 4 书中第九章的示例,用 STS
项目、WSTrustServiceHost 类的实例,做 WCF 的自托管 (Self-hosting)


书上也有对「安全令牌服务 (STS, Security Token
Service)」和「安全声明标记语言 (SAML, Security Assertions Markup
Language)」的介绍。若搭配 MSDN Magazine 的图片和中文说明,会更容易了解
[19]。

威尼斯官方网站 6
图 5 STS 实现了 WS-Trust
规格,可处理不同类型凭据的身份验证,以颁发出安全令牌,大幅简化
ASP.NET、WCF 等不同应用程序中的验证和授权代码

书上和 MSDN Magazine 也有提到,用 STS 达到跨域做单点登录的议题 [19]。在联合方案中 (Federation
Authentication),STS
可在两个或多个安全域之间建立信任关系,以便用户可以对管理其凭据的域进行身份验证,同时仍可获得另一个域中资源的访问权限,如下图
6。因为 STS 会对其中一个域 B 的用户,在进行身份验证后,颁发携带声明的
SAML
安全令牌,里面包含了被验证用户的身份标识、用户的访问权限,然后此令牌会作为
Cookie 返回到浏览器,如上图 5;之后当用户浏览至域 A 中的 Web
应用程序,会将 SAML 令牌作为 Cookie 进行传递,此时由于两个域中的 STS
存在信任关系,域 A 中的 STS 会信任此令牌的签名。

威尼斯官方网站 7
图 6 使用 STS 达到跨 Domain 做
SSO (Single Sign On),无需再跨多个应用程序和域维护用户凭据

通过使用诸如 Active Directory Federation Server (ADFS) 和 WIF
的联合安全平台,在域之间添加新的信任关系也变得更加容易。将应用程序扩展到企业实体内的其他域,甚至扩展到外部合作伙伴的域也变得更容易
[17]。

另附带一提,微软云计算 Windows Azure AppFabric 服务,包含 Access
Control、AppFabric Service Bus 两大服务功能。前者 (ACS, Access Control
Service) 对用户访问的安全权限控制,也与 STS、SAML
Token、声明式的授权有关,以避免某些复杂的验证过程。 Azure 应用程序取得
Token 其步骤大致如下:

  1. 用户将它的 SAML Token (Security Assertions Markup Language) 传送给
    Access Control Service。
  2. STS (Security Token Service) 依据规则,产生新的 SAML Token。
  3. 在访问控制内的 STS,将新产生的 Token 传送给客户端。
  4. 客户端再将新产生的 Token 传送给应用程序 (如上图 5 的
    WCF、ASP.NET、…)
  5. 应用程序就依据这个 Token,来决定用户的权限为何、能做什么、…等等。

附注:名词解释 1

[18]

安全令牌服务 (STS)

安全令牌服务 (STS) 是用于根据 WS-Trust 和 WS-Federation
协议构建、签署和颁发安全令牌的服务组件。实施这些协议需要进行大量的工作,但
WIF 能为您完成所有这些工作,让那些不精通协议的人很容易即可启动并运行
STS。 您可以使用云 STS (如 LiveID STS)、预先构建的 STS (如 Active
Directory Federation Service (AD FS)
2.0),或者如果想要颁发自定义令牌或提供自定义身份验证或授权,则可以使用
WIF 构建自己的自定义 STS。借助 WIF 即可轻松地构建自己的 STS。

安全令牌服务允许进一步合并安全逻辑的工具。STS
的典型任务是验证用户,随后创建可以包含声明的安全令牌。客户端必须首先使用
STS 进行验证,然后将返回的令牌转发到客户端希望进行通信的服务。

因为 STS 了解服务
(该信息是令牌请求的一部分),所以它可以进行集中授权并预先生成服务所需的声明。这样声明转换完全不需要在服务端点进行,它可以通过
STS 集中完成。当系统复杂到一定程度时,它可以大幅简化安全基础结构。

当多个信任域联合时,安全令牌服务也是重要的基础结构组件。通过在多个令牌服务间建立信任,您可以跨越服务所能使用的信任边界交换安全令牌。

WCF 具备支持前述方案的自动客户端/服务端,以及编写 STS
所需的所有基类。但正确实现所有 WS-*
相关规范是一项非常复杂的任务。您应该购买商业 STS 或使用更高级别的工具包
(例如:Zermatt) 编写自定义规范。即将面世的 Microsoft Active Directory
联合身份验证服务版本旨在为 WCF 提供功能完备的 STS。


附注:名词解释 2

[11]

Active Directory Federation Services 2.0
(ADFS 2.0)

Active Directory Federation Services 2.0 是对于 Active Directory
的一个新拓展,它使得 Active Directory
成为了对于那些具有声明意识的应用程序的基础设施。 通过 Security Token
Service (STS), AD FS 2.0 使得 Active
Directory的用户可以验证那些具有声明意识的应用程序,并为担当关于那些用户的声明
(属性) 的“权威人士”,无论用户信息储存于 Active Directory,
SQL数据库,还是其他存储。作为一个联合服务,AD FS 2.0
提供了为联合关系的单点管理。使用例如 SAML 2.0
此类的工业标准协议,可以实现从合作伙伴组织或云端,Active Directory
用户的单点登陆。
 
开发者可同时使用 ACS、WIF,以及 AD FS v2 来开发网络服务,其结合了 Active
Directory
安全性与功能性,灵活性,定制的访问控制规则,简便的紧密结合的开发体验。


学习资源:

[1] Claims 认证详解(1)

[2] dotNET Framework 4 新版 API 和命名空间、API 海报下载

[3] WCF 4 书籍下载
Programming WCF 4 (Wrox)
Programming WCF Services, 3rd.Edition (O’Reilly, Aug 2010)

出版社的书籍介绍、代码下载

[4] Windows Identity Foundation 介绍

[5] WCF 应用程序与 WIF 兼容性

[6] Microsoft.IdentityModel 类库、命名空间 (Windows Identity
Foundation)

WSTrustServiceHost 类

[7] Windows Identity Foundation 安装程序下载
(四种版本,安装后才能编译书上第九章的示例)

系统需求:

[8] Active Directory Federation Services 2.0 RTW 下载 (2010/05/05)

[9] Active Directory Federation Services 2.0 homepage (Windows Server
2008 R2)

[10] Windows Identity Foundation (WIF) 正式发布

Web 单点登录系统

[11] Windows Identity Foundation 官网 (英文, 有许多资源和文档可下载)

[12] WIF Support for Windows Server 2003

[13] download Microsoft’s Identity and Access Management Platform
Whitepapers

Active Directory Federation Services 2.0, Windows Identity Foundation &
Windows CardSpace comprise an identity & access solution to help
developers and IT professionals simplify access to applications and
other systems with an open claims-based model.

[14] WIF Workshop 7: WIF and WCF (2010/06/23, 在线影片, 印度腔很重)

[15] Claims based access platform (CBA), code-named Geneva (MSDN
Forum)

[16] Windows身份验证基础(Windows Identity Foundation) 常见问题解答 :
什么是 Windows身份验证基础(Windows Identity Foundation (WIF)) ?
Windows Azure 平台可以使用 WIF 吗 ?
什么是 Active Directory Federation Services 2.0 (ADFS 2.0) ?

[17] 通过 WIF 实现基于声明的授权 (MSDN Magazine, 11 月 2009)
什么是「安全令牌服务 (STS)」?

[18] 基于 WCF 服务中的授权 (MSDN Magazine, 10 月 2008)
什么是「安全令牌服务 (STS)」?

[19] Geneva 框架:构建基于声明的 WCF 服务的更好方法 (MSDN Magazine, 12
月 2008)

[20] Geneva 框架:构建自定义安全令牌服务 (MSDN Magazine, 1 月 2009)

[21] WCF 应用程序兼容性、如何在 WCF 服务上启用 WIF

[22] 构建 WCF 信赖方应用程序 (TechNet)

[23] Windows Communication Foundation (MSDN)
(WCF 4)
(WCF 3.5)

[24] Improving Web Services Security: Scenarios and Implementation
Guidance for WCF、Download the Guide

[25] WIF – Pablo M. Cibraro (aka Cibrax)

[26] WCF 分布式安全开发实践、WCF 4.0 新特性体验

[27] WCF 身份验证之用户名密码认证、通信安全

[28] WCF 安全 – 迷失code的博客 – 博客园

[29] WCF – Artech – 博客园

[30] MonoTouch, WCF 4 and WIF on iPad ?

[31] Programming Windows Identity Foundation 书籍下载 (September,
2010)
a.
b.

   

书籍介绍

[32] A Guide to Claims-Based Identity and Access Control (Patterns &
Practices) 书籍介绍

[33] Exercise 1: Enabling claims based access for an ASP.NET Web
Application by generating a local STS

[34] Exercise 2: Using ACS with SAML Tokens

[35] 为什么微软要推 WCF Data Service (REST)

[36] Access Control in the Cloud: Windows Azure AppFabric’s ACS

[37] OASIS Web Services Federation (WSFED) TC、 WS-Trust 1.3 OASIS
Standard

 

最重要的是,再经过几个小时之后,你会到达一个地方,它完全被卡侬海滩包围着。连绵数英里的宽阔海岸刚被细雨洗刷过,还有那宏伟的山景,这种大自然的壮丽都已经无法用任何语言来描述了。

软件安全之路就像在州际5号公路和美国101高速之间的选择一样。州际5号就是当今的Windows安全系统,它有丰富的设施能让你快速地到达目的地。101号高速则是基于声明的安全系统。虽然这条路会更远,设施也贫乏些,但它不仅漂亮,而且能带你直达天堂。本章要像卡拉克斯餐厅一样,让疲惫的旅行者能看到满菜单的精致美食,使他们在前行的路上精力倍增。

声明这个概念已经在前面几章多次出现过了。在第7章,我们解释了WCF的XSI(Extensible Security
Infrastructure,可扩展安全框架)会接受所有用来验证用户本身的凭据,并且将它们翻译成一个声明集。然后在介绍WCS的第8章和第9章提出了数字标识只是一个可信也可不信的声明集合,而CardSpace为用户提供了一种管理多个数字标识的方法。现在,声明这个概念会从树丛间跳到空地上,一起让我们来看看它的真面目吧。

利用声明保证资源安全

考虑下面这个场景:一个人走进了一间酒吧,把他的驾照和一叠钱放在吧台上,然后向侍者要了一扎啤酒。侍者看了看那人的驾照,拿了钱,然后给了他啤酒。

这里,酒吧提供了一种服务。给顾客啤酒是这种服务的一个操作,而啤酒在这里是一种受保护的资源。为了得到啤酒,一个人必须达到法定的饮酒年龄,而且还要为此付点钱。

那个司机的驾照和钱代表了一个声明集(claim
set)。声明集是一些由同一个发布者提供的声明。对司机的驾照来说,发布者就是向司机颁发驾照的政府部门。而对钱来说,发布者就是国家的中央银行。这些发布者本身只是体现为一些声明的集合,比如图标、签名还有司机的驾照和钱上面的图案。

声明包含了类型、权限和值。在司机驾照所体现的声明集中,其中一个是司机的出生日期。这个声明的类型是出生日期。一个声明赋予其所有者的权限定义了他可以对声明值所做的操作。对于司机的出生日期,其权限只是持有权。司机可以持有这个出生日期,但是不能更改它。

检验司机的驾照和钱时,侍者将关于司机驾照所提供的出生日期的声明翻译成关于驾照持有者年龄的声明。侍者也将吧台上每一张钞票的面值翻译成关于这些钱总和的声明。当侍者在把输入声明集翻译成输出声明集时,这个过程中所遵循的规则便构成了他的授权策略(authorization
policy)。授权策略的输入声明集称为评估上下文(evaluation
context),而输出声明集称为授权上下文(authorization
context)。一组授权策略构成了一个授权域(authorization domain)。

在收钱和给顾客啤酒时,侍者将关于顾客年龄的声明和最小的饮酒年龄进行比较,然后又比较所付的钱的总数和啤酒的价格。在这个过程中,侍者是在对授权策略生成的授权上下文和给顾客啤酒操作的请求进行比较。由于授权上下文声明集(顾客的年龄和所付钱的总和)满足了对操作的访问请求,所以侍者给了那人一扎啤酒。

这就是XSI所提供的基于声明的安全系统工作原理。访问一个作用在受保护资源上的操作是通过基于声明来授权的。声明有类型、权限和值。声明集是一些由同一个发布者提供的声明集合。声明集的发布者本身也是一个声明集。基于声明的授权通过两步实现:首先,执行一个授权策略,它将一个评估上下文声明集作为输入,并翻译成一个授权上下文作为输出;其次,授权上下文声明集中的声明与操作的访问请求进行比较,然后,根据比较的结果对这个操作的请求予以拒绝或接受。

10.2.1 基于声明授权和基于角色授权

基于角色授权(role-based
authorization)经常被用来控制用户对软件应用程序的操作使用。那么这种基于声明的授权与它相比又如何呢?在这里,基于角色授权的定义可以帮助我们回答这个问题。

“基于角色授权是这样一种机制,它利用角色来赋予用户进行系统操作的适当权限和访问资源的许可”(Tulloch 2003,281)。角色是一个“集合了具有相同等级安全权限用户的符号类”(Tulloch 2003,281)。

基于角色授权要求首先识别用户,然后确定用户被赋予的角色,最后对这些角色和被授权可以访问某个资源的角色进行比较。所以,例如在微软.NET基于角色的安全机制提供的基于角色授权系统中,最重要的元素就是原对象(principle
object),它集成了用户标识和其所具有的角色(.NET Framework Class Library 2006; Freeman and Jones 2003,
249)。

相反,在前一个场景中,侍者在决定是否给顾客啤酒时,识别那个人的身份并不重要。当然,驾照也可以用来标识那个人的身份(因为司机的驾照通常给出了关于持有者身份的声明),但是这些声明对侍者来说并不重要,侍者仅仅对驾照里关于持有者出生日期的声明感兴趣。但是,如果顾客后来抢劫了侍者,识别他的身份当然就变得重要了。

一般地,基于声明授权包括了基于角色授权。更准确一点,标识只是对声明值的一种权限,也就是利用声明值标识自己的权限。你没有权限用出生日期这个声明值来标识自己,因为很多人都有同样的出生日期。而身份照片,你确实可以用这个声明值来标识你自己。另外,角色也只是一种类型的声明而已。

10.2.2 基于声明授权和访问控制列表

ACL(Access Control
List,访问控制列表)是在管理网络资源访问时普遍采用的方式。XSI提供的基于声明的授权方式和这种利用ACL控制资源使用的方式比较起来又如何呢?同样,ACL的定义会帮助我们回答这个问题。

“ACL由一系列ACE(Access Control Entry,访问控制条目)组成,而ACE定义了一个用户或组对某个资源可以进行的操作。”(Tulloch 2003,7)。ACE包含了一个用来标识用户或组的SID(security
identifier,安全标识符),以及一组访问权限,这些权限定义了这个用户或组在这个资源上被许可或被禁止的操作(Tulloch 2003,7)。

ACL“用在微软Windows平台上,用来控制对文件、进程、服务、共享和打印机等需要安全保护的资源的访问”(Tulloch 2003,7)。具体地说,“当在微软的Windows平台上创建一个用户账号时,它被赋予了一个SID,用来唯一标识针对该操作系统中的这个账号”(Tulloch 2003,7)。当用户使用这个账号登录时,系统会生成一个包含该账号的SID和该账号所在组的SID的访问令牌。这个令牌“然后就被复制到这个账号拥有的所有进程和线程中”(Tulloch 2003,7)。当用户试图访问一个受ACL保护的资源时,令牌包含的所有SID就会与ACL中的每个ACE包含的SID进行比较,直到发现有匹配的SID为止,最后根据比较的结果对访问予以接受或拒绝。

同样,ACL实际上是基于声明授权的一个特例,也就是说它是被基于声明授权涵盖的。用户用来登录操作系统的凭证和访问令牌包含的SID都是声明集。操作系统将用户登录使用的凭证转换成访问令牌中包含的SID,这恰恰就是一个授权策略执行的实例。比较访问令牌中的SID和ACL里的SID,也是授权上下文声明集里的声明和受ACL保护的资源的操作访问请求相比较的一个示例。

XSI所提供的这种更全面的模型远比ACL更加符合分布式系统中访问授权的要求。这有3个原因:第一,访问令牌从来没有被设计成可在不同平台间交换。相反,声明已经可以用标准的可互操作的格式来表示,比如SAML。第二,访问令牌是由操作系统发布的,而声明却可以由任何源发布。第三,也是最重要的,访问令牌和ACL中的SID一般只在发布这些令牌的操作系统范围内才有效。比如,如果操作系统是一个域控制系统,它发布的SID在这个域里都有效。相反,对于声明来说,只要发布者可信,它就是有意义的。尽管基于声明授权比基于角色授权和ACL有更多优势,你也不一定要马上丢掉基于角色授权和ACL而转向使用基于角色授权。因为对于基于角色授权和ACL,已经有大量的强大工具支持着它们的运用。这些工具很多就内嵌在Microsoft
Windows系统中,并且网络管理员也习惯了使用它们。而对于基于声明授权的支持却只限于XSI、WCS和活动目录联合服务(Active
Directory Federation Services)。

所以,一个更明智的方法是将它们都利用起来,发挥它们各自在不同场合中的优势,而不应该把基于声明授权看成是基于角色授权和ACL的高级替代品。基于声明授权在跨平台、跨机构访问控制方面非常有效。所以,如果一个机构的用户需要访问另外一个机构系统管理的资源,就可以将访问令牌转换成声明,这样另一个机构就可以用它来决定要不要授予用户访问权限。

那这样的解决方案应该怎样来实现呢?WS-Trust (Web Services Trust
Language,WS-Trust语言)就是这样一种用来请求和发布声明集的标准语言。利用这种语言发布声明的系统称为STS(Gudgin和Nadalin 2005,7;Cabrera和Kurt 2005,24-27)。如果一个机构(A)的用户需要访问另外一个机构(B)的服务,它可以给用户提供一个安全令牌服务,这样用户就可以向它请求机构B所能够接受的声明集。这个安全令牌服务可以将用户访问令牌中SID的声明作为输入,然后运行一个授权策略生成一组类型、权限和机构B都能理解的声明。而机构B也会有一个安全令牌服务用来接收这些声明,然后运行它自己的一个授权策略生成一组新的声明,使得这个机构中的其他系统可以用这些新的声明来决定是不是要让用户访问他们的资源。图10-1描述了这个方案,这个方案有下列几个非常重要的优点。

图10-1 跨机构的基于声明授权

第一,互信关系被最小化了,而且互信关系的管理也集中了。具体地来看,具有保护资源的服务只需要信任一个发布者,也就是他们自己机构的安全令牌服务。这个安全令牌服务可以通过设置,从而信任其他任意机构的安全令牌服务所发布的声明。而且,将一个安全令牌服务配置成信任其他机构的安全令牌服务,只需要让它能使用那个机构的公钥即可。

第二,一个机构为其用户生成的用来访问另一机构服务的声明,对于另一机构的这些服务是不可见的。这些服务所信任的安全令牌服务的授权策略将这些声明隐藏了起来。安全令牌服务运行授权策略将另一个机构生成的声明转换成了这些服务所熟悉的声明。这个将各种其他机构生成的声明翻译成一系列服务所熟悉的声明的过程,一般称为声明正则化(claim normalization)。

第三,对服务访问的控制是相对独立的。在一个机构中,系统管理员可以通过添加/删除用户或提升/降低用户的权限来控制用户访问其他机构的服务的权限,而这个过程并不需要另一个机构的系统管理员参与。这样做的好处可以在下面的练习中得到很好的体现。

10.3 使用XSI运用基于声明的安全

在这个练习里,我们首先建创一个通过基于角色授权来控制对一个网络资源进行访问的WCF解决方案。这个方案将讲述如何利用现有的大家所熟悉的Microsoft Windows和微软.NET工具来保护WCF应用。这种方式将节省系统管理员学习新知识和工具的时间,也减少了软件开发工程师学习新概念和新类库的时间。

然后,这个练习将继续向大家讲述,通过利用XSI的基于声明方式的授权访问请求,使同一个资源可以被部署在另一个联合机构里的同一客户端所访问。在这过程中,让人惊讶的是,不管是客户端的代码还是管理被访问资源的服务端代码都不需要做任何改动,但仍然可以完成访问控制方式的彻底改变。这其实再一次演示了WCF为软件通信所提供的软件工厂模板的强大,无须修改代码,只要对模型做些更改就可以彻底地改变一个应用程序的运行方式。

10.3.1 利用Windows标识授权对局域网资源的访问

第一步,我们先来演示在WCF解决方案中使用基于角色授权来控制对局域网资源的访问。基于角色授权使用了.NET Role-Based Security和ASP.NET
2.0提供的AuthorizationStoreRoleProvider和Windows授权管理器(通常指AzMan)。

使用Windows XP SP2 的读者可以通过安装Windows Server 2003 Service Pack 1 Administration
Tools
Pack来安装Windows Server
2003授权管理器。这个安装包可以在微软的下载中心通过查找Windows Server 2003 SP1 Administration Tools
Pack来获得。

按下面的步骤开始吧。

(1)
从 Studio解决方案Security.sln。

(2)
在Visual Studio里打开这个解决方案。Service这个项目用来创建一个承载WCF服务的控制台程序。Client项目包含了一个Windows
Forms程序,它用来访问这个服务所提供的资源。

(3)
如果因为某些原因这个解决方案不在C:WCFHandsOnAdvancedSecurityIntranetSolution目录里,打开Service项目里的App.config文件,然后修改下面这项:

so
that it refers to the actual path to the AuthorizationStore.xml file
that is in the same folder as the Security.sln solution file
itself.

 

使其指向AuthorizationStore.xml所在的那个目录,这个目录也应该包含了Security.sln解决方案文件。

(4)
从Visual Studio中选择Debug、Start
Debugging进行调试。如图10-2所示,服务的控制台程序和资源访问客户端程序的用户界面都应该显示出来了。在客户端的用户界面里有两个按钮,左边的按钮上有一张煤块的图片,而右边的按钮上有一张钻石图片。

(5)
在服务的控制台程序显示了一些活动之后,点击煤块按钮。如图10-3所示,有个消息框会弹出来并显示已经得到煤块这个不怎么值钱的资源了。

(6)
现在点击钻石按钮。对宝贵钻石的访问应该会被拒绝。如图10-4所示的消息框会弹出来。

                                           

图10-2 Resource
Access客户端用户界面图            
10-3 成功地获取了煤块图

10-4 尝试获取钻石时失败了

(7)
从Visual Studio的菜单里依次选择Debug、Stop
Debugging,关闭服务的控制台程序。

下面这几步就来解释为什么煤块能被访问而钻石不能被访问。先通过下面的步骤打开AzMan用户界面。

(1)
从Windows开始菜单里选择Run,然后输入回车,启动Microsoft
Management Console。

(2)
从Microsoft Management Console的菜单里依次选择File、Add/Remove
Snap-in。

(3)
点击Add/Remove Snap-in对话框中的Add按钮。

(4)
从Add Standalone Snap-in对话框里选择Authorization Manager,然后点击Add按钮,再点击Close按钮。

(5)
重新回到Add/Remove Snap-in对话框,点击OK按钮。

这样,Windows授权管理器的用户界面应该就已经打开了,如图10-5所示。我们继续检查一下用来控制服务资源访问的授权库。

(6)
在Microsoft Management Console里,右击Authorization
Manger用户界面左边框中的树型列表里的授权管理器,然后从显示的菜单里选择Open Authorization Store。

(7)
点击图10-6中Open Authorization
Store对话框的Browse按钮,在文件对话框里选择C:WCFHandsOnAdvancedSecurityIntranetSolutionAuthorizationStore.xml这个文件,然后点击Open按钮。

图10-5 Windows Authorization
Manager用户界面

图10-6 打开一个授权存储

(8)
如图10-7所示,展开左边框的Authorization Manager的树状列表。从中选择StaffMember,观察右边框可以发现Everyone组里的用户已经被赋予了StaffMember角色。

图10-7 角色指派

(9)
再选择Manager节点,可以发现没有任何用户被指派为这个角色。

(10)
右击Manager节点,从右键菜单中选择Assign Windows Users and Groups。在Select Users and
Groups对话框里,输入当前登录用户的用户名,然后选择OK。

(11)
重新开始调试程序。

(12)
当服务的控制台显示了一些活动之后,点击Resource
Access客户端用户界面里的钻石按钮。应该会出现图10-8所示的对话框,显示钻石现在可以被访问了。

图10-8 成功获取钻石

(13)
从Visual Studio的菜单里依次选择Debug、Stop
Debugging,关闭服务的控制台。

(14)
回到授权存储控制台,选择Role Assignments下的Manager节点。右击右边的Administrator条目,然后从右键菜单中选择Delete,将当前登录用户的访问权限恢复成原来的状态。

很明显,WCF服务管理的资源访问现在是根据客户端用户的角色来控制的,这些角色是由Windows Server 2003授权管理器的授权存储C:WCFHandsOnAdvancedSecurityIntranetSolution
AuthorizationStore.xml指派的。下面的步骤将显示这是如何完成的。

(1)
在Visual Studio的Security解决方案里,打开Service项目的ResourceAccessServiceType.cs文件。代码清单10-1显示了该文件中的代码。这里可以明显地看出服务中对资源的访问操作是由.NET Role-Base Security
System.Security.Permissions.PrincipalPermission中特性控制的,它指定了只有赋予了Manager角色的用户才可以访问更加宝贵的钻石资源。

代码清单10-1 使用PrincipalPermission特性

using
System;

using
System.Collections;

using
System.Collections.Generic;

using
System.Security.Permissions;

using
System.Runtime.Serialization;

using
System.ServiceModel;

using
System.Text;

using
System.Web;

using
System.Web.Security;

namespace
Service

{

   
public class ResourceAccessServiceType: IResourceAccessContract

   
{

       
#region IResourceAccessContract Members

       
[PrincipalPermission(SecurityAction.Demand, Role =
“StaffMember”)]

       
[PrincipalPermission(SecurityAction.Demand, Role = “Manager”)]

       
string IResourceAccessContract.AccessCoal()

       
{

           
return “Here is your coal!”;

       
}

       
[PrincipalPermission(SecurityAction.Demand, Role = “Manager”)]

       
string IResourceAccessContract.AccessDiamond()

       
{

           
return “Here is your diamond!”;

    
   }

       
#endregion

   
}

}

 

(2)
打开Service项目里的App.config文件。代码清单10-2中列出了文件的内容。

Windows通信基础服务的绑定是netTcpBinding。默认情况下,这个绑定通过Kerberos或NTLM使用Windows凭据来标识那些使用服务的用户。

服务还有一个名为ServiceBehavior(这个名称是随意取的)的System.ServiceModel.Descri-
ption.ServiceAuthorization行为。这个行为的PrincipalPermissionMode属性值被赋为System. ServiceModel.
Description.PrincipalPermissionMode.UseAspNetRoles,它的RoleProviderName属性值被赋为名为AuthorizationStoreProvider(这个名称也是随意取的)的角色提供程序。

这个角色提供程序将在配置文件的下面一点定义。它被配置成使用C:WCFHandsOnAdvan-
cedSecurityIntranetSolutionAuthorizationStore.xml作为它的授权存储。

所以,根据这样的配置,服务就被配置成根据用户的Windows凭据来验证用户,然后根据用户属于授权存储里维护的哪些组来决定是否授权他们访问申请的资源。这种授权方式在第7章中已经介绍过,这里就不再赘述了。

代码清单10-2 服务配置

<configuration>

 
<system.serviceModel>

   
<behaviors>

     
<serviceBehaviors>

       
<behavior name=’ServiceBehavior’>

         
<serviceAuthorization

           
principalPermissionMode=’UseAspNetRoles’

          
roleProviderName=’AuthorizationStoreRoleProvider’ />

          
<serviceMetadata

            
httpGetEnabled =’true’/>

       
</behavior>

   
  </serviceBehaviors>

   
</behaviors>

   
<services>

     
<service

       
name=”Service.ResourceAccessServiceType”

       
behaviorConfiguration=’ServiceBehavior’>

       
<host>

         
<baseAddresses>

           
<add baseAddress=’net.tcp://localhost:9000/Woodgrove’/>

           
<add baseAddress=’;

         
</baseAddresses>

       
</host>

       
<endpoint address=”ResourceAccess”

         
binding=”netTcpBinding”

         
contract=”Service.IResourceAccessContract” />

       
<endpoint address=”mex”

         
binding=”mexHttpBinding”

         
contract=”IMetadataExchange” />

     
</service>

   
</services>

 
</system.serviceModel>

 
<system.web>

   
<roleManager defaultProvider=”AuthorizationStoreRoleProvider”

  
   enabled=”true”

     
cacheRolesInCookie=”true”

     
cookieName=”.ASPROLES”

     
cookieTimeout=”30″

     
cookiePath=”/”

     
cookieRequireSSL=”false”

     
cookieSlidingExpiration=”true”

     
cookieProtection=”All” >

   
<providers>

     
<clear />

     
<add

       
name=”AuthorizationStoreRoleProvider”

       
type=”System.Web.Security.AuthorizationStoreRoleProvider”

       
connectionStringName=”AuthorizationServices”

       
applicationName=”RoleProvider” />

   
</providers>

 
</roleManager>

</system.web>

<connectionStrings>

 
<add

   
name=”AuthorizationServices”

   
connectionString=”msxml://C:WCFHandsOnAdvancedSecurityIntranetSolution

➥AuthorizationStore.xml” />

 
</connectionStrings>

</configuration>

 

10.3.2 改进初始方案

这个解决方案的缺点就是它严重依赖于System.Security.Permissions.PrincipalPermission特性来控制对服务操作的访问。使用这些特性就会将验证的代码和服务的代码混杂到一起。

在第7章讲到,XSI可以让我们做得更好。它可以将任何用来验证的凭据都翻译成一列声明,并且可以将对这些声明的检查放到由服务的配置指定的其他程序集里。这就立即给服务的设计带来了两个好处:第一,明显地,它可以将业务逻辑的编程和服务访问的编程及管理分开;第二,它还带来一个看似细小但是又非常重要的好处,它使服务的访问与具体的凭据发布者分开了(或者用Web Services Federation
Language规范里的术语,即服务的访问与具体的安全域[security realm]分开了
[Kaler和Nadalin
2003])。之前服务的访问和某个Windows域的凭据绑定在一起,现在它将根据声明来授权,这些声明可以来自任意的发布者,甚至不一定需要有关用户标识的声明。

通过修改服务以使用XSI服务授权管理器授权对其资源的访问,这样大的转变实现起来非常简单。下面的步骤就会证明给你看。

(1)
打开Service项目的ResourceAccessServiceType.cs文件,然后将System.Security.Permis- sions.
PrincipalPermission特性注释掉。如代码清单10-3所示。

代码清单10-3 去掉PrincipalPermission特性

using
System;

using
System.Collections;

using
System.Collections.Generic;

using
System.Security.Permissions;

using
System.Runtime.Serialization;

using
System.ServiceModel;

using
System.Text;

using
System.Web;

using
System.Web.Security;

namespace
Service

{

   
public class ResourceAccessServiceType: IResourceAccessContract

   
{

       
#region IResourceAccessContract Members

       
//[PrincipalPermission(SecurityAction.Demand, Role =
“StaffMember”)]

       
//[PrincipalPermission(SecurityAction.Demand, Role =
“Manager”)]

       
string IResourceAccessContract.AccessCoal()

       
{

           
return “Here is your coal!”;

       
}

       
//[PrincipalPermission(SecurityAction.Demand, Role =
“Manager”)]

       
string IResourceAccessContract.AccessDiamond()

       
{

           
return “Here is your diamond!”;

       
}

       
#endregion

   
}

}

(2)
将Service项目里的App.config文件修改成如代码清单10-4所示。为了减少读者手工修改代码的麻烦,在C:WCFHandsOnAdvancedSecurityIntranetSolutionListing10.4文件夹里有一份配置文件的副本,这里最主要的改动就是System.ServiceModel.Description.ServiceAuthorization行为的配置。现在它被配置成将授权管理交给一个XSI服务授权管理器。这里,就是Service-
AuthorizationManager程序集(名称是随便取的)里的Service.AccessChecker类型:

 

<behavior
name=’ServiceBehavior’>

       
<serviceAuthorization

serviceAuthorizationManagerType=’Service.AccessChecker,

➥ServiceAuthorizationManager’

               
principalPermissionMode=’None’  />

       
[…]

</behavior>

代码清单10-4 ServiceAuthorizationManagerType配置

<?xml
version=”1.0″ encoding=”utf-8″ ?>

<configuration>

 
<configSections>

   
<section

     
name=”operationRequirements”

     
type=”Service.OperationRequirementsConfigurationSection,

     
➥ServiceAuthorizationManager” />

威尼斯官方网站, 
</configSections>

 
<operationRequirements>

   
<operation

   
identifier=”;

     
<role name=”Manager”/>

     
<role name=”StaffMember”/>

   
</operation>

   
<operation

   
identifier=”;

     
<role name=”Manager”/>

   
</operation>

 
</operationRequirements>

 
<system.serviceModel>

   
<behaviors>

     
<serviceBehaviors>

       
<behavior name=”‘ServiceBehavior'”>

         
<serviceAuthorization

         
serviceAuthorizationManagerType=

         
➥”Service.AccessChecker,ServiceAuthorizationManager”

         
principalPermissionMode=”None” />

         
<serviceMetadata httpGetEnabled =”true”/>

       
</behavior>

     
</serviceBehaviors>

   
</behaviors>

 
  <services>

     
<service

       
name=”Service.ResourceAccessServiceType”

       
behaviorConfiguration=”ServiceBehavior”>

       
<host>

         
<baseAddresses>

           
<add baseAddress=”net.tcp://localhost:9000/Woodgrove”/>

           
<add baseAddress=”;

         
</baseAddresses>

       
</host>

       
<endpoint address=”ResourceAccess”

       
binding=”netTcpBinding”

       
contract=”Service.IResourceAccessContract”/>

       
<endpoint address=”mex”

       
binding=”mexHttpBinding”

       
contract=”IMetadataExchange” />

     
</service>

   
</services>

 
</system.serviceModel>

 
<!— Role Provider Configuration —>

 
<system.web>

   
<roleManager defaultProvider=”AuthorizationStoreRoleProvider”

   
enabled=”true”

   
cacheRolesInCookie=”true”

   
cookieName=”.ASPROLES”

   
cookieTimeout=”30″

   
cookiePath=”/”

   
cookieRequireSSL=”false”

   
cookieSlidingExpiration=”true”

   
cookieProtection=”All” >

     
<providers>

       
<clear />

       
<add

       
name=”AuthorizationStoreRoleProvider”

       
type=”System.Web.Security.AuthorizationStoreRoleProvider”

       
connectionStringName=”AuthorizationServices”

       
applicationName=”RoleProvider” />

     
</providers>

   
</roleManager>

 
</system.web>

 
<!— Connection Strings —>

 
<connectionStrings>

   
<add

   
name=”AuthorizationServices”

   
connectionString=”msxml://C:WCFHandsOnAdvancedSecurity

➥IntranetSolutionAuthorizationStore.xml”
/>

 
</connectionStrings>

</configuration>

 

(3)
从Visual Studio的菜单里依次选择File、Add、Existing Project,然后从C:WCFHandsOn AdvancedSecurityIntranetSolutionIntranetServiceAuthorizationManager文件夹中选择ServiceAuthorzation- Manager.csproj,将创建ServiceAuthorizationManager程序集的项目加入到解决方案中。小心,不要从InternetServiceAuthorizationManager文件夹里错选了那个名称与此类似的项目——这个项目待会儿会用到的。

(4)
研究一下ServiceAuthorizationManager项目中的AccessChecker.cs文件中的System.Access Checker类的代码。代码清单10-5给出了全部的代码。System.AccessChecker类由于继承自XSI的System.
ServiceModel.ServiceAuthorizationManager基类,并重写它的CheckAccess()虚函数,所以它是一个XSI Service Authorization
Manager,每一个请求进来时这个方法都会被调用。在这之前,XSI已经把通过NetTcpBinding验证过的Windows凭据魔术般地翻译成一组声明了。System.
ServiceModel.ServiceAuthorizationManager的子类System.AccessChecker会检查这些声明,由它决定声明是否满足处理该请求的要求(请求的实质是由它的Action头决定的)。这里,处理每个请求的要求定义在配置文件的一个定制段中(虽然它们可以在任意地方定义)。

 

<operationRequirements>

 
<operation

 
identifier=”;

   
<role name=”Manager”/>

   
<role name=”StaffMember”/>

 
</operation>

 
<operation

 
identifier=”;

  
 <role name=”Manager”/>

 
</operation>

 
</operationRequirements>

 

判断提交的声明是否满足要求是通过从声明中选择一条有关用户名称的声明,然后查询刚才用过的授权存储来决定用户是否指派了某个给定的角色完成的。

 

if
(string.Compare(

   
claim.ClaimType,

   
“”,

   
true) == 0)

   
{

        
userName = (string)claim.Resource;

        
foreach (string requiredRole in requiredRoles)

        
{

             
if (Roles.Provider.IsUserInRole(

                              
userName,

                                 
requiredRole))

             
{

                   
return true;

             
}

        
}

   
}

 

实际上,对声明的判断可以使用任何方式或标准来完成。后面,我们会使用Windows Workflow Foundation的规则集来完成判断。

代码清单10-5 ServiceAuthorizationManager的实现

sing
System;

using
System.Collections.Generic;

using
System.Configuration;

using
System.IdentityModel.Claims;

using
System.IdentityModel.Policy;

using
System.IO;

using
System.ServiceModel;

using
System.Web.Security;

namespace
Service

{

   
public class AccessChecker : ServiceAuthorizationManager

   
{

       
private Dictionary<string, string[]> accessRequirements =
null;

       
public AccessChecker()

       
{

           
this.accessRequirements = new Dictionary<string,
string[]>();

           
OperationRequirementsConfigurationSection

               
operationRequirementsConfigurationSection

               
= ConfigurationManager.GetSection(“operationRequirements”)

               
as OperationRequirementsConfigurationSection;

           
OperationRequirementsCollection requirements =

          
operationRequirementsConfigurationSection.OperationRequirements;

           
List<string> roles = null;

           
foreach (OperationElement operationElement in requirements)

           
{

               
roles = new List<string>(operationElement.Roles.Count);

       
        foreach (RoleElement roleElement in
operationElement.Roles)

               
{

                   
roles.Add(roleElement.Name);

               
}

 

               
this.accessRequirements.Add(

                   
operationElement.Identifier,

       
            roles.ToArray());

           
}

       
}

 

       
public override bool CheckAccess(OperationContext
operationContext)

       
{

           
string header =

           
operationContext.RequestContext.RequestMessage.Headers.Action;

           
string[] requiredRoles = null;

           
if (!(this.accessRequirements.TryGetValue(

                               
header,

                               
out requiredRoles)))

           
{

               
return false;

           
}

 

           
string userName = null;

           
foreach(ClaimSet claimSet in

operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)

           
{

               
foreach (Claim claim in claimSet)

               
{

                   
if (string.Compare(

  
             claim.ClaimType,

   
“”,

               
true) == 0)

                   
{

                       
userName = (string)claim.Resource;

                       
foreach (string requiredRole in requiredRoles)

                       
{

                           
if (Roles.Provider.IsUserInRole(

                                                               
userName,

                                                               
requiredRole))

                           
{

                               
return true;

                           
}

                       
}

                   
}

               
}

           
}

           
return false;

       
}

   
}

}

 

按照下面的步骤测试修改后的解决方案。

(1)
依次选择Debug和Start
Debugging,开始调试程序。

(2)
当服务的控制台显示一些活动后,在Resource
Access客户端用户界面里点击煤块按钮和原来一样,确认已经取得煤块的消息就会出现。

(3)
点击Resouce
Access客户端用户界面里的钻石按钮,拒绝访问钻石的消息就会出现。

(4)
从Visual Studio的菜单里依次选择Debug和Stop
Debugging。

由于对原来的WCF应用程序进行了更改,用户的授权就不再由服务本身的代码来完成了。应用程序的配置指定了一个类——XSI服务授权管理器,负责对服务操作访问的管理。另外,授权的完成也不再与具体到某个安全域发布的某种类型的凭据关联在一起。XSL会将所有的凭据都翻译成声明的集合,验证机制就可以判定这些声明是否符合要求。虽然在这个例子里我们使用了一个需要有关Windows用户名声明的XSI授权管理器,在判定声明时也用到了Windows Server
2003授权管理器的授权存储,但是只需在配置里做些修改就可以使用不同的授权机制。本章后面的示例里就会包含这样的更改。

10.3.3 添加STS作为联合的基础

假定现在创建的Intranet服务部署在名为Woodgrove的机构里,下面我们就来做一些工作让这个服务可以被Fabrikam机构访问。

我们根据图10-1里给出的架构来完成这项任务。Fabrikam和Woodgrove都会提供STS。Woodgrove
STS会配置成信任由Fabrikam的STS发布的有关用户的声明,Woodgrove服务被配置成信任由Woodgrove STS发布的有关用户的声明。当Fabrikam的一个用户使用Resource
Access客户端程序访问由Woodgrove服务提供的操作时,这个程序将会从Fabrikam STS请求一组声明。Fabrikam
STS会执行一个授权策略以决定它应该签发给用户什么样的声明。这个用到的授权策略使用用户的Windows凭据进行验证,然后利用ASP.NET 2.0 AuthorizatioinStoreRoleProvider和Windows Server 2003
授权管理器来判断用户所分配的角色。根据用户所分配的角色,Fabrikam STS将给Resource
Access程序发布一组有关这个用户角色的声明。

Resource
Access客户端程序会将从Fabrikam STS获取的声明提交给Woodgrove STS,而后者信任由Fabrikam STS发布的声明。Woodgrove
STS会执行一个授权策略,将Fabrikam
STS给出的有关用户角色的声明翻译成另一组Woodgrove服务所熟悉的有关用户角色的声明。

Resource
Access客户端程序会将由Woodgrove STS发布的有关用户角色的这组声明提交给Woodgrove服务,而后者信任由Woodgrove STS发布的声明。服务将Woodgrove
STS有关用户角色的声明和有权访问这个操作(即用户通过Resource Access客户端所请求的操作)的角色进行对比。
这样,它就可以判断用户是否应该被授权访问这个操作了。

1.
证书安装

将Woodgrove STS配置成信任由Fabrikam
STS发布的有关用户的声明,也就是要确保Woodgrove
STS可以验证由Fabrikam
STS签名的声明。所以,需要安装一些X.509证书。依照下面的步骤完成这项任务。

(1)
根据7.4.1节中介绍的内容为Woodgrove和Fabrikam
Enterprises安装X.509证书。

(2)
下面的这些步骤将这些证书从本地机器的个人存储复制到本地机器的Trusted People存储。实际上,Fabrikam Enterprises的证书将放在Woodgrove STS所在机器的Trusted
People存储里,Woodgrove的证书也会放在Fabrikam
STS所在机器的Trusted
People存储里,这样也就将两个STS配置成互相信任的关系。在这个示例里,我们假定Fabrikam STS和Woodgrove
STS都运行在同一台机器上,所以两个证书都将在同一个Trusted People存储里。我们应该认识到,将Woodgrove的证书放在那里,Fabrikam
STS就可以信任Woodgrove的STS;将Fabrikam
Enterprises的证书放在那里,这样Woodgrove
STS就可以信任Fabrikam
STS的声明了。下面我们继续,从Windows开始菜单里选择Run,输入下面的命令打开Microsoft
Management Console。

 

:mmc

 

(3)
从Microsoft Management Console的菜单里依次选择File、Add/Remove
Snap-In。

(4)
在Add/Remove Snap-In对话框里点击Add。

(5)
从可用的独立管理单元列表中选择Certificates,然后点击Add按钮。

(6)
在Certificates管理单元对话框里选择Computer Account,然后点击OK按钮。

(7)
接受Select Computer对话框的默认设置,然后点击Finish按钮。

(8)
点击Add Standalone Snap-In对话框的Close按钮。

(9)
点击Add/Remove Snap-In对话框的OK按钮。

(10)
展开Microsfot Mangement Console左边框里显示的Certificate(Local
Computer)节点。

(11)
展开Certificates节点的Personal子节点。

(12)
选择Personal节点的Certificates子节点。

(13)
把右边显示的FabrikamEnterprises证书和Woodgrove证书都选上。这可以通过下面的操作完成:按住Shift键不放,先点击FabrikamEnterprises证书,然后点击Woodgrove证书。

(14)
右击选中的证书,从右键菜单中选择Copy。

(15)
右击Certificates(Local Computer
Node)的Trusted
People子节点,从右键菜单中选择Paste。

2.
将Fabrikam STS添加到解决方案中

现在我们继续将Fabrikam STS添加到解决方案中。

(1)
将C:WCFHandsOnAdvancedSecurityIntranetSolutionFabrikamSecurityTokenService里的FabrikamSecurityTokenService.csproj项目添加到Security解决方案中。不要冒险从头写一个STS,可以先用那些已经被公认为不错的STS。所以,在这个练习里我们提供了已经创建好的STS。这些STS是由Martin Gudgin编写的,Martin就是WS-Trust规范(这个规范定义了STS)其中的一个作者。STS的行为会在后面的步骤里定制。

(2)
打开Security解决方案中FabrikamSecurityTokenService项目的IsecurityTokenService.cs文件,查看一下Fabrikam STS实现的IsecurityTokenService服务契约:

 

[ServiceContract(

       
Name = “SecurityTokenService”,

       
Namespace = “]

public
interface ISecurityTokenService

{

   
[OperationContract(Action = Constants.Trust.Actions.Issue,

                      
ReplyAction = Constants.Trust.Actions.IssueReply)]

       
Message ProcessRequestSecurityToken(Message rstMessage);

}

 

在这份安全契约的定义中,重要的元素是Constants.Trust.Actions.Issue和Constants.Trust. Actions.IssueReply,它们定义了请求消息的合法WS-Addressing Action头和响应消息的WS- Addressing Action头。它们是在同个项目里的Constant.cs文件里定义的:

 

public
const string Issue =

       
“”;

public
const string IssueReply =

       
“”;

 

这些为WS-Addressing Action头定义的值是由Web Services Trust Language(WS-Trust)(Gudgin and Nadalin
2005)规范定义的,它们规定了与STS交换的SOAP消息的WS-Addressing
Action头。这样,服务契约就定义了一个操作,它可以接收和返回具有WS-Trust协议所定义的WS-Addressing
Action头的SOAP消息。所以,这个服务契约实际上描述了由WS-协议规范所定义的STS接口。或者换句话说,IsecurityTokenService协议定义了由WS-Trust规范所定义的STS服务契约。

(3)
打开Security解决方案里FabrikamSecurityTokenService项目的App.config文件,看看Fabrikam
STS是如何配置的。代码清单10-6给出了配置的内容。

配置里的appSettings被STS的代码用来标识它自己发布的用于签名和加密安全令牌的证书。

 

<appSettings>

       
[…]

       
<add key=”ProofKeyCertificateIdentifier”

               
value=”CN=Woodgrove”/>

       
<add key=”IssuerCertificateIdentifier”

               
value=”CN=FabrikamEnterprises”/>

</appSettings>

 

令牌将使用Fabrikam Enterprises的私钥签名,使用Woodgrove的公钥加密。接收STS(这里即Woodgrove
STS)的证书称为证明密钥,或更正式一点,在WS-Trust Language里它称为持有证明令牌([proof-of-possession token][Gudgin和Nadalin 2005,7]),因为只有持有与该证书对应的私钥,接收者才可以解密和使用令牌。

在配置中为STS终结点指定的绑定是标准的WSHttpBinding,实际上可以是任意的绑定。默认情况下,配置为使用WSHttpBinding的服务会使用Windows凭据来标识用户。

代码清单10-6 Fabrikam配置

<configuration>

 
<appSettings>

   
<add

     
key=”SecurityTokenServiceName”

     
value=”FabrikamEnterprises”/>

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章