在《模拟(Impersonation)与委托(Delegation)》一文中,我们对模拟和委托这两个概念以及相关编程实现进行了详细说明。如果将模拟使用在WCF上面,就意味着WCF可以模拟客户端身份(而不是启动寄宿进程的Windows帐号)执行服务操作。这篇文章主要介绍WCF关于模拟的编程。
一、命令式模拟编程
如果我们有一个具有模拟级别为Impersonation或者Delegation的WindowsIdentity,我们就可以通过调用其Impersonate对其进行身份模拟。在采用WCF认证的情况下,我们可以通过当前ServiceSecurityContext的WindowsIdentity或者PrimaryIdentity获取到代码认证客户端的WindowsIdentity对象,那么在服务操作中模拟客户端身份就和简单了。这种在服务操作实现中通过编程的方式实现身份模式可以将服务操作的部分逻辑在模拟的客户端身份下执行。
1: using (WindowsImpersonationContext context = ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
2: {
3: //在模拟上下文中执行的操作
4: }
二、声明式模拟编程
如果你希望整个操作都在模拟上下文中执行,你可以采用声明式的模拟编程。具体来说,你只需要在需要进行模拟的服务操作方法上应用OperationBehaviorAttribute特性,并指定相应的模拟选项即可。
1: [AttributeUsage(AttributeTargets.Method)]
2: public sealed class OperationBehaviorAttribute : Attribute, IOperationBehavior
3: {
4: //其他成员
5: public ImpersonationOption Impersonation { get; set; }
6: }
7: public enum ImpersonationOption
8: {
9: NotAllowed,
10: Allowed,
11: Required
12: }
通过OperationBehaviorAttribute的Impersonation属性指定模拟选项通过枚举ImpersonationOption表示。定义在ImpersonationOption的三个枚举项NotAllowed、Allowed和Required分别表示的含义如下。
- NotAllowed:不允许模拟客户端身份,这是默认值;
- Allowed:在身份模拟条件满足条件允许模拟客户端身份;
- Required:强制模拟客户端身份。这要求强制采用Windows认证,如果采用非Windows认证,会抛出异常。
此外,如果你要求服务的所有操作均强制采用身份模拟,你可以通过编程或者配置将ServiceAuthorizationBehavior的ImpersonateCallerForAllOperations属性设置成true。但是在这种情况下,如果该服务具有任何模拟选项被设置成NotAllowed的服务操作,服务在寄宿过程中会抛出InvalidationOperationException异常。
1: public sealed class ServiceAuthorizationBehavior : IServiceBehavior
2: {
3: //其他成员
4: public bool ImpersonateCallerForAllOperations { get; set; }
5: }
三、设置模拟级别
在采用Windows认证的情况下,服务可以在成功认证后可以获取代表客户端身份的WindowsIdentity对象。但是服务端是否可以根据WindowsIdentity获取客户端身份信息,是否可以模拟客户端身份访问某些安全资源,这取决于该WindowsIdentity的模拟级别。
身份模拟密切地关系到被模拟身份代表的用户的安全,所以模拟级别应该通过客户端自行控制。在WCF安全体系中,该模拟级别是在客户端提供的Windows凭证中指定。如下面的代码所示,表示客户端Windows凭证的WindowsClientCredential类型中,具有一个类型为TokenImpersonationLevel枚举的AllowedImpersonationLevel属性,用以指定模拟级别。该属性的默认值为None,实际上代表的等级是Identification。
1: public sealed class WindowsClientCredential
2: {
3: //其他成员
4: public TokenImpersonationLevel AllowedImpersonationLevel { get; set; }
5: }