[WCF权限控制]利用WCF自定义授权模式提供当前Principal[原理篇]

在《通过扩展自行实现服务授权》一文中,我通过自定义CallContextInitializer的方式在操作方法之前之前根据认证用户设置了当前线程的安全主体,从而实现授权的目的。实际上,WCF的安全体系本就提供相应的扩展,使你能够自由地实现安全主体的提供方式。具体来说,安全主体的提供可以通过自定AuthorizationPolicy或者ServiceAuthorizationManager来实现。

一、AuthorizationPolicy

在WCF安全应用编程接口中,所有的AuthorizationPolicy实现了IAuthorizationPolicy接口。如下面的代码所示,IAuthorizationPolicy继承自IAuthorizationComponent接口,本身具有一个ClaimSet类型的Issuer属性和一个Evaluate方法。关于ClaimSet,我们会在后续的部分继续介绍,这里我们只需要关注Evaluate方法。该方法的第一个参数的类型为System.IdentityModel.Policy.EvaluationContext,它具有一个字典类型的只读属性Properties。

   1: public interface IAuthorizationPolicy : IAuthorizationComponent
   2: {
   3:     bool Evaluate(EvaluationContext evaluationContext, ref object state);
   4:     ClaimSet Issuer { get; }
   5: }
   6: public abstract class EvaluationContext
   7: {
   8:     //其他成员
   9:     public abstract IDictionary<string, object> Properties { get; }
  10: }

如果我们需要通过自定义的方式来提供安全主体,我们只需要通过实现IAuthorizationPolicy接口创建自定义的AuthorizationPolicy,并在Evaluate方法中将创建安全主体对象添加到EvaluationContext的Properties字典中即可。在该字典中,用于存放安全主体条目对应的键值为“Principal”。

   1: Public class CustomAuthorizationPolicy:IAuthorizationPolicy
   2: {
   3:    //其他成员
   4:    bool Evaluate(EvaluationContext evaluationContext, ref object state)
   5:    {
   6:      //其他操作
   7:       evaluationContext. Properties[“Principal”] = customPrincipal;
   8:       return true;
   9:    }
  10: }

那么自定义的AuthorizationPolicy通过怎样的方式被应用到WCF的授权运行时呢?这还是要借助于我们已经很熟悉的服务行为ServiceAuthorizationBehavior。如下面给出的代码片断所示,ServiceAuthorizationBehavior具有一个类型为ReadOnlyCollection<IAuthorizationPolicy> 的ExternalAuthorizationPolicies属性,表示自定义AuthorizationPolicy的集合。

   1: public sealed class ServiceAuthorizationBehavior : IServiceBehavior
   2: {
   3:     //其他成员
   4:     public ReadOnlyCollection<IAuthorizationPolicy> ExternalAuthorizationPolicies { get; set; }
   5: }

你可以通过编程的方式将自定义的AuthorizationPolicy添加到ServiceAuthorizationBehavior的ExternalAuthorizationPolicies集合中,也可以通过配置指定自定义AuthorizationPolicy的类型。如下面给出的配置片断所示,ServiceAuthorizationBehavior的ExternalAuthorizationPolicies集合对应的配置节点为<serviceAuthorization>/<authorizationPolicies>。

   1: <configuration>
   2:   <system.serviceModel>
   3:     <behaviors>
   4:       <serviceBehaviors>
   5:         <behavior  name="useCustomAuthorization">         
   6:           <serviceAuthorization principalPermissionMode="Custom">
   7:             <authorizationPolicies >
   8:               <add policyType="AuthorizationPolicyType1" />
   9:               <add policyType="AuthorizationPolicyType2" />
  10:               ...
  11:             </authorizationPolicies>
  12:           </serviceAuthorization>
  13:           <serviceDebug includeExceptionDetailInFaults="true"/>
  14:         </behavior>
  15:       </serviceBehaviors>
  16:     </behaviors>
  17:   </system.serviceModel>
  18: </configuration>

二、ServiceAuthorizationManager

在ServiceAuthorizationBehavior选择Custom安全主体权限模式的情况下,除了自定义AuthorizationPolicy,你还可以通过自定义ServiceAuthorizationManager来提供当前的安全主体。下面给出了ServiceAuthorizationManager的定义,从中我们可以看出它具有两个CheckAccess方法用于实现授权。方法的返回值表示当前请求的服务操作是否被授权指定。实际上最终的授权判断实现在受保护方法CheckAccessCore中,并且在ServiceAuthorizationManager中该方法直接返回True。

   1: public class ServiceAuthorizationManager
   2: {
   3:     //其他成员
   4:     public virtual bool CheckAccess(OperationContext operationContext);
   5:     public virtual bool CheckAccess(OperationContext operationContext, ref Message message);
   6:     protected virtual bool CheckAccessCore(OperationContext operationContext);
   7: }

当ServiceAuthorizationBehavior的PrincipalPermissionMode被设置成Custom的情况下,被设置的当前安全主体实际上是通过当前服务安全上下文(ServiceSecurityContext)获取的。具体来说,ServiceSecurityContext具有一个表示授权信息的AuthorizationContext对象。和EvaluationContext一样,AuthorizationContext也具有一个字典类型的Properties属性。实际上,通过AuthorizationPolicy添加到EvaluationContext中的属性,最终都会被转移到当前AuthorizationContext的Properties属性中。

   1: public class ServiceSecurityContext
   2: {
   3:     //其他成员
   4:     public AuthorizationContext AuthorizationContext { get; }
   5: }
   6: public abstract class AuthorizationContext : IAuthorizationComponent
   7: {  
   8:     //其他成员
   9:     public abstract IDictionary<string, object> Properties { get; }
  10: }

所以只要我们能够在WCF从当前AuthorizationContext获取安全主体之前对其进行初始化,整个基于安全主体的授权体系就能正常运作,而这个工作可以通过自定义ServiceAuthorizationManager来实现。一般来讲,我们只需通过继承ServiceAuthorizationManager,重写虚方法CheckAccessCore进行安全主体的初始化。

   1: public class CustomServiceAuthorizationManager : ServiceAuthorizationManager
   2: {
   3:     protected override bool CheckAccessCore(OperationContext operationContext)
   4: {
   5:     //其他操作
   6:         AuthorizationContext authorizationContext = operationContext.ServiceSecurityContext.AuthorizationContext;
   7:         authorizationContext.Properties["Principal"] = customPrincipal;
   8:         return true;
   9:     }
  10: }

自定义的ServiceAuthorizationManager最终还是通过ServiceAuthorizationBehavior这个服务行为应用到WCF授权框架体系中。如下面给出的代码片断所示,在ServiceAuthorizationBehavior中依然具有相应属性定义的。而在ServiceAuthorizationBehavior的配置节中,ServiceAuthorizationManager对应的配置属性为serviceAuthorizationManager,你可以通过该配置属性将设置自定义ServiceAuthorizationManager的类型。

   1: public sealed class ServiceAuthorizationBehavior: IServiceBehavior
   2: {
   3:     //其他成员
   4:     public ServiceAuthorizationManager ServiceAuthorizationManager { get; set; }
   5: }

如果两种默认的安全主体权限模式(UseWindowsGroup和UseAspNetRoles)不能满足你的要求,你需要自定义安全主体提供方式,自定义AuthorizationPolicy或者ServiceAuthorizationManager不失为一个很好的解决方案。为了让你对此有个深刻的认识,在《下篇》中我们提供一个完整的实例。

[WCF权限控制]利用WCF自定义授权模式提供当前安全主体[原理篇] [WCF权限控制]利用WCF自定义授权模式提供当前安全主体[实例篇]

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个会写诗的程序员的博客

《Kotin 极简教程》第9章 轻量级线程:协程(2)《Kotlin极简教程》正式上架:

如果需要依次调用它们, 我们只需要使用正常的顺序调用, 因为协同中的代码 (就像在常规代码中一样) 是默认的顺序执行。下面的示例通过测量执行两个挂起函数所需的总...

1172
来自专栏技术点滴

程序异常分析指南一、非法内存访问二、资源访问冲突三、程序异常解决方法总结参考资料

在Linux上编写运行C语言程序,经常会遇到程序崩溃、卡死等异常的情况。程序崩溃时最常见的就是程序运行终止,报告Segmentation fault (core...

3993
来自专栏iOSDevLog

pygit:足够的Git客户端创建一个repo,commit,并将自己推送到GitHub

Git因其非常简单的对象模型而闻名(其中包括) - 并且有充分的理由。学习时git我发现本地对象数据库只是目录中的一堆普通文件.git。除了index(.git...

1302
来自专栏Vamei实验室

Linux并发与同步

典型的UNIX系统都支持一个进程创建多个线程(thread)。在Linux进程基础中提到,Linux以进程为单位组织操作,Linux中的线程也都基于进程。尽管实...

2499
来自专栏高性能服务器开发

经典面试题(一)之服务器内存碎片

年前去过上海掌门集团(做无线wifi万能钥匙的那一家)和百度面试过一次,前者问了linux下gcc的malloc函数如何分配内存的,后者在二面时通过一个链表的数...

8898
来自专栏DannyHoo的专栏

iOS中解决后台返回的null导致的崩溃问题--NullSafe

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

1523
来自专栏后端技术探索

利用 PHP 名称空间编写可读且可维护的代码

“Conan 是我榜样。” 如果我在餐桌上说这句话,我儿子会以为我说的是游戏 “野蛮人柯南”,而我妻子会以为我说的是脱口秀主持人 Conan O'Brien。这...

1272
来自专栏后端技术探索

PHP 使用协同程序实现合作多任务(一)

PHP 5.5 一个比较好的新功能是实现对生成器和协同程序的支持。对于生成器,PHP的文档和各种其他的博客文章已经有了非常详细的讲解。协同程序相对受到的关注就少...

991
来自专栏数据小魔方

左手用R右手Python系列——异常捕获与容错处理

一套稳健的代码体系,必须能够包容所有可能出现的错误情况并做出针对性处理,要想达到这个目标,务必要对异常捕获与容错处理有深入的了解和认识。 秉着初学者入门探索的...

29410
来自专栏Java技术分享

Redis特性和应用场景

Redis使用标准C编写实现,而且将所有数据加载到内存中,所以速度非常快。官方提供的数据表明,在一个普通的Linux机器上,Redis读写速度分别达到81000...

2567

扫码关注云+社区