WIF基本原理(2)基于声明的标识模型
基于声明的标识模型,简单来讲,就是将用户信息作为声明条件,向应用程序来提供用户标识。一个声明以是用户名,也可能是电子邮件地址。现在的想法是配置外部标识系统,为应用程序提供了解用户及其所做各个请求所需的所有信息,以及从可靠源接收的标识数据的加密保证。
基于声明的标识模型,更容易实现单点登录,并且应用程序可以彻底摆脱以下操作:
1) 对用户进行身份验证。
2) 存储用户账户和密码。
3) 调用企业目录以查看用户标识的详细信息。
4) 从其他平台或公司与标识系统集成。
在基于声明的标识模型中,应用程序将根据用户所提供的声明来做出与标识相关的决策。这可以是任何内容,从包含用户名字的简单应用程序个性化设置,到授予用户访问应用程序中高级功能和资源权限。
基本概念
下面简单介绍基于声明的标识模型的基本术语。
(1) 标识
“标识”一词通常是一个不明确的术语。但为了描述WIF中的编程模型,这里使用术语“标识”来描述一组说明要保护的系统中用户或其他一些实体的特性。比如一个常见的互联网站点,对于一个普通用户,其标识可能是用户名,可能是性别,也可能是爱好,总之是一个完整实体的特性。
(2) 声明
将声明视为一条标识信息,如销售角色中的姓名、电子邮件地址、年龄、成员身份。应用程序接收的声明越多,对用户了解得就越多。你可能想知道为何将这些称为“声明”而不是通常描述企业目录时用到的“特性”。原因就在于送达的方式。在此模型中,应用程序并不在目录中查询用户特性。相反,用户向应用程序传达声明,由应用程序对其进行检查。各声明均由颁发者发出,你对这些声明的信任度与对颁发者的信任度一样。例如,你对公司域控制器发出的声明的信任度高于对用户自己发出的声明。WIF代表Claim类型的声明,此类型声明具有找出声明颁发者的Issuer属性。
(3) 安全令牌
用户向应用程序传递一组声明和一个请求。 在Web服务中,这些声明将在 SOAP封装的安全标头中传递。在基于浏览器的Web应用程序中,这些声明将通过HTTP POST从用户的浏览器中到达,并可能随后缓存在 Cookie中(如果需要会话)。不论这些声明如何到达,都必须对其进行序列化,这也是安全令牌所在之处。安全令牌是一组经过颁发机构数字签名的序列化声明。此签名非常重要:它向你保证用户并不只是生成大量声明然后发送给你。在没必要或不希望加密的安全性较低的情况下,可使用未签名的令牌。
WIF的一个核心功能是,可以创建和读取安全令牌。WIF和.NET Framework可以处理所有的加密工作,并为应用程序提供一组可以读取的声明。
(4) 颁发机构
不同类型的颁发机构有很多,从颁发 Kerberos票证的域控制器到颁发X.509证书的证书颁发机构。这里讨论的颁发机构是可颁发包含声明的安全令牌的机构。此颁发机构是了解如何颁发安全令牌的 Web应用程序或 Web服务。它必须发出正确的声明,提供给目标信赖方和提出请求的用户,并且负责查看声明并对用户本身进行身份验证。
不论选择哪个颁发机构,它在标识解决方案中的作用都十分重要。在通过信任声明将身份验证的因素从应用程序中排除时,你会将责任转交给该机构并要求其以你的名义对用户进行身份验证。
(5) 标准
为使所有这些操作能够交互,使用了多个WS-*标准。使用WS-MetadataExchange检索策略,并根据WS-Policy规范对策略本身进行结构化。STS(安全令牌服务)公开了实施WS-Trust规范的终结点,此规范描述了如何请求和接收安全令牌。如今,大多数 STS可颁发安全声明标记语言(SAML)格式的令牌。SAML是行业认可的XML词汇,可用于以交互方式表示声明。或者,在多平台情况下,这使你可以与完全不同的平台上的 STS进行通信,并在所有应用程序中实现单点登录。
(6) 基于浏览器的应用程序
基于浏览器的应用程序也可以使用标识模型。
首先,用户将浏览器指向Web应用程序(信赖方应用程序)。Web应用程序将此浏览器重定向到STS以便可以对用户进行身份验证。在可以读取传入请求的简单 Web应用程序中托管 STS,使用标准HTTP机制对用户进行身份验证,然后创建SAML令牌并通过一段JavaScript代码给予回复,此代码将使浏览器发起可将SAML令牌发送回RP的HTTP POST。 此POST的正文包含RP请求的声明。此时,RP通常会将声明打包到Cookie以便不必为各个请求重定向用户。
(7) CardSpace和标识选择器
WIF为CardSpace序列化提供API,此API可用于构建托管的信息卡颁发以及在应用程序中启用信息卡登录。
标识选择器提供以下功能:
q 帮助用户管理 Web的多个标识。
q 帮助用户为指定信赖方选择适当标识。
q 帮助保护用户隐私。
基本实现
在具体了解基于声明的标识模型的实现机制之前,先来了解或者说回顾传统的验证模式。当然,本书高级扩展的部分会推荐基于声明的标识模型,不代表传统的方式做得不好,事实上,实践验证它们足够优秀。讨论基于声明的标识模型,给你一个全新的选择,或者在适合的场景下使用它。
(1) Iprincipal接口
管理标识和访问控制,需要得到当前应用环境的用户信息,从而根据用户信息识别其标识,并根据标识来判断权限。
在.NET应用程序中,当前上下文中的用户信息由IIdentity接口来表示。该接口的定义如代码清单15-1所示。
代码清单15-1 IIdentity接口定义
public interface IIdentity
{
// Properties
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
IIdentity接口的主要作用是用来验证,这一点从它的属性中也能看出来。提到验证肯定会想到授权。
(2) Iidentity接口
在授权环节用到的最主要的接口是Iprinicipal,定义如代码清单15-2。
代码清单15-2 Iprinicipal定义
public interface IPrinicipal
{
// Methods
bool IsInRole(string role);
// Properties
IIdentity Identity { get; }
}
在Iprinicipal接口中,用来做授权验证的前提是它的Identity属性。在.NET程序中,总能从上下文中获取Iprinicipal示例,比如在ASP.NET程序中,可以通过HttpContext.Current.User属性获取该值,在一般程序中可以通过Thread.CurrentPrincipal来获取该实例。本书前面章节介绍过.NET对Iprinicipal的不同实现,比如WindowsPrincipal、GenericPrincipal或者自定义的实现。当然,这只是传统的.NET认证和授权内容的冰山一角,不过本书中已经用了足够的篇幅来讨论它们,而且也是目前.NET安全框架的核心。
当使用VS创建一个ASP.NET的Web站点,默认验证模式是Windows,这意味着,应用程序期望IIS来负责验证用户。然而,当检查此应用场景下的Iprinicipal会发现,大部分属性都是空值,这是因为默认情况下Web站点启用了匿名验证。此时需要使用IIS或者修改Web.config来禁用匿名访问,启用Windows验证。
当调整完之后,你应该知道,这样的验证方式只适合当前的局域网络,如果换个环境将无法访问。如果在代码中使用IsInRole类似的方法来做角色判断,那么当站点进行迁移之后,将完全失效。比如换服务器,业务转移到云中。
面对互联网环境,或者为了使应用更具灵活性,选择Form身份验证方式,不可避免地需要构建数据库来存储用户信息。Form身份验证方式带来了很大的灵活性,但是仍然无法兼容异构系统的统一验证,甚至在一个WCF和ASP.NET互相协助的系统中,也无法统一Form认证和WCF身份认证。
将身份验证交给Windows或者交给ASP.NET,都是不错的做法,因为在一定程度上将这样的工作交给“第三方”。但是仍然无法真正地从设计角度将用户标识和访问限制同业务应用分离出来。
(3) 分离应用和标识验证
曾几何时,开发人员被迫在程序中直接处理硬件。如果想打印文档,需要知道如何在程序中调用打印模块,使用各种中断,而且硬件不同,调用方式也会不同。
幸运的是,那些日子已经一去不复返了。今天,我们可以间接地调度硬件通过驱动程序。所有的驱动程序都公开简单的调用接口给上层的应用程序,我们将接口层称作逻辑层,它下面是核心驱动,核心驱动下面才是硬件层。
现在,作为.NET开发人员,只需要调用一个PrintDocument方法就可以解决问题。
直接在程序中处理认证和授权与直接在程序中处理打印模块相比,二者很相似:需要处理太多复杂的情况,要应对各种各样的变化。硬件调用的问题被驱动程序解决,那么是否有理由相信,可以用类似的方法来解决访问控制问题呢?
标识和访问控制的统一面临着巨大的挑战,比如,目前验证和授权的技术和协议属于不同的厂商;不同的架构,不同的业务使用的数据存储格式不同;验证点也各不相同。不过也不用太悲观,在Web 2.0时代,为了不同应用的交换,甚至在安全领域,出现了很多标准化协议,比如SOAP、WS-Security、WS-Trust、 WS-Federation、SAML(Security Assertion Markup Language),以及最近出现的OpenID、OAuth和其他开发协议。如果没听说过这些协议,或者不理解其细节也没关系,暂时将注意力集中在这些开放的协议给我们带来了什么。这里不打算去探讨每个协议的功能和细节,我们从具体的场景中来认识基于协议的安全框架带来的好处。
图15-2是一个简单的验证系统,它包含基于声明的标识模型中几个主要实体。
图15-2 基于声明的标识模型实体
在这个简单的系统中,包括一个用户和一个用户要访问的应用。对于应用而言,它也许不需要关心这个用户的每一个属性,用户在正式访问应用之前,要经过一个用户到标识的转化过程。在本应用场景中将用户虚拟为一个影迷。
应用程序可能是一个网站,可能是一个Web服务,也可能是一个桌面应用,总之,它需要对用户验证和授权。在标识模型中,将这样的应用称为信赖方(Relying Party,RP),将应用虚拟为售票员和电影院。
这个系统会包含多个IP(Identity Providers)。每一个IP针对某一类型的用户而存在。每一标识提供程序是一个抽象的角色,在具体应用时它会被实例化。
假设每一个用户都具有基本的信息,用于系统实施验证,这些基本信息就是声明。从另一个角度讲,声明是对在当前上下文中对用户的一个描述。它是IP的创建者,是RP的消费者。
图15-3是该系统的验证流程。
图15-3 验证流程
现在对图15-3做简要的梳理:
1) 用户(Subject)想要访问应用(RP),在本例中假设他通过浏览器发送请求。这一步用户被告知需要填写什么信息、RP支持哪一种IP、什么样的安全通信协议会被使用。
2) 客户选择一种合适的IP,请求令牌。在这个过程中,客户需要提供能被IP识别的凭据信息,这些信息被保存在Policy中。
3) IP接到并开始处理请求。如果IP认为请求合乎规范,它接收声明信息,然后发送一个安全令牌到客户端。
4) RP接收并检查客户端传递过来的安全令牌,如果通过,RP对客户进行授权。
虽然这个简单系统和传统的验证模型有很多不同,但是需要从类似的或者更复杂的场景中抽象出一个简单易用的模型。WIF提供了这样的模型,其他细节可参考《.NET安全揭秘》一书中的相关章节。
-------------------------注:本文部分内容改编自《.NET 安全揭秘》