[WCF权限控制]WCF自定义授权体系详解[原理篇]

到目前为止,我么介绍的授权策略都是围绕着安全主体进行的,基本上都是基于角色的授权。虽然角色是定义权限最为常用的形式,但是它解决不了授权的所有问题。基于角色的授权策略一般是这样的:需要进行访问控制的操作或者资源关联到某个角色上,那么只要访问者被分配了该角色,就被授予了相应的权限。那么假设我们的授权策略是这样的:访问权限和两个角色进行关联,访问者需要同时被分配了这两个角色才能被授权。这是一个很常见的授权策略,但是典型的基于单一角色的授权解决不了这个问题(除非为两个角色的交集创建新的角色)。而这仅仅是一种简单的授权策略,有时候授权需要通过一个复杂的表达式来表示,而且其中会涉及诸多元素,比如身份、角色和组织等。

我之所以说这么多,主要是为说明一个问题:授权策略有时候需要手工定制。而作为一种基于可扩展性的通信框架,WCF在授权方面提供了扩展点,使你可以根据的你实际需要定制相应的授权策略。WCF为了创建了一个基于“声明”的授权系统,为了让读者对该系统的内部原理有全面的了解,我们不妨先来讨论一下这里指的声明是如何定义的。

目录: 一、Claim和ClaimSet 二、DispatchRuntime中的AuthorizationPolicy和ServiceAuthorizationManager 三、通过自定义AuthorizationPolicy创建基于自定义授权策略的声明 四、通过自定义ServiceAuthorizationManager根据声明作出最后的授权判断

一、Claim和ClaimSet

声明描述了与系统中某个实体关联的功能,该实体通常为该系统中的某个用户。通过对访问给定的受保护资源所需的声明和与试图进行访问的实体关联的声明进行比较,便可确定该资源的访问权限。声明是针对特定值的权限表达式。权限可以是读取、写入或拥有。值可以是数据库、文件、邮箱或属性。声明还具有声明类型。声明类型和权限的组合提供了用于针对该值指定的功能的机制。在WCF安全应用编程接口中,生命通过类型Claim表示。从下面给出的关于Claim定义的代码片断中,我们可以认识到:一个通过Claim对象表示的声明具有如下三要素:声明类型(ClaimType)、声明关联的资源(Resource)和声明代表的权限类型(Right)。

   1: public class Claim
   2: {
   3:     //其他成员
   4:     public string ClaimType { get; }
   5:     public object Resource { get; }
   6:     public string Right { get; }
   7: }

通过Right属性表示的权限类型一般通过一个统一资源标识符(URI)来表示,而静态类Rights定义了两个预定义的权限类型:Identity和PossessProperty。前者表示声明用于身份标识,后者则表示声明关联的实体具有的属性。Rights类定义如下。

   1: public static class Rights
   2: {
   3:     public static string Identity { get; }
   4:     public static string PossessProperty { get; }
   5: }

二、DispatchRuntime中的AuthorizationPolicy和ServiceAuthorizationManager

在上面我们已经提到过了,借助于WCF的扩展,我们通过自定义AuthorizationPolicy和ServiceAuthorizationManager来让WCF按照我们自定义的授权策略进行访问控制。那么。这两个对象是如何参与到WCF的授权执行流程中的呢?

我们首先需要了解的是:自定义的AuthorizationPolicy和ServiceAuthorizationManager通过服务行为ServiceAuthorizationBehavior成为WCF运行时的一部分。具体来说,在ServiceAuthorizationBehavior的ApplyDispatchBehavior方法被调用的时候,定义在ExternalAuthorizationPolicies属性中的AuthorizationPolicy列表和ServiceAuthorizationManager被赋值给所有终结点的分发运行时。在DispatchRuntime类型中,具有两个同名的属性。

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

三、通过自定义AuthorizationPolicy创建基于自定义授权策略的声明

整个自定义授权先从AuthorizationPolicy开始。具体来说,WCF先创建一个EvaluationContext对象。我们之前只提到过EvaluationContext用于表示属性的Properties,实际上它的核心是通过ClaimSets属性表示的声明集(ClaimSet)的集合。下面给出的EvaluationContext的整个公用成员的定义,除了Properties和ClaimSets之前,EvaluationContext还具有一个额外的属性Generation表示声明集被添加到ClaimSets集合的次数。而声明集的添加通过方法AddClaimSet实现。EvaluationContext具有一个有效性,而失效的时间可以通过方法RecordExpirationTime来记录。

   1: public abstract class EvaluationContext
   2: {
   3:     public abstract void AddClaimSet(IAuthorizationPolicy policy, ClaimSet claimSet);
   4:     public abstract void RecordExpirationTime(DateTime expirationTime);
   5:  
   6:     public abstract ReadOnlyCollection<ClaimSet> ClaimSets { get; }
   7:     public abstract int Generation { get; }
   8:     public abstract IDictionary<string, object> Properties { get; }
   9: }

EvaluationContext是一个抽象类型,实际被创建的是一个被称为System.IdentityModel.Policy.DefaultEvaluationContext的内部类型的对象。当EvaluationContext被初始化之后,WCF会遍历定义在当前DispatchRuntime的ExternalAuthorizationPolicies属性中的所有AuthorizationPolicy。然后依次调用它们的Evaluate方法,而传入的参数就是之前初始化的这个EvaluationContext对象。一般地,我们通过自定义AuthorizationPolicy的目的在于通过在实现的Evaluate方法中将基于你自定义授权策略相关的声明集添加到EvaluationContext的ClaimSets中。

四、通过自定义ServiceAuthorizationManager根据声明作出最后的授权判断

在所有的自定义AuthorizationPolicy的Evaluate方法被调用之后,最终的EvaluationContext对象被用以初始化当前的授权上下文(AuthorizationContext)。下面给出了AuthorizationContext的所有公共属性的定义。一般来说,除了Id,其余三个属性直接来源于EvaluationContext。具体来说,EvaluationContext的ClaimSets和Properties作为AuthorizationContext的ClaimSets和Properties,而EvaluationContext通过RecordExpirationTime记录的过期时间反应在AuthorizationContext的ExpirationTime上。

   1: public abstract class AuthorizationContext : IAuthorizationComponent
   2: {
   3:     public abstract string Id { get; }
   4:  
   5:     public abstract ReadOnlyCollection<ClaimSet> ClaimSets { get; }
   6:     public abstract DateTime ExpirationTime { get; }
   7:     public abstract IDictionary<string, object> Properties { get; }
   8: }

和EvaluationContext一样,AuthorizationContext也是一个抽象类,默认被创建的是一个名称为System.IdentityModel.Policy.DefaultAuthorizationContext的内部类型对象。而通过EvaluationContext创建的AuthorizationContext最终服务于自定义的ServiceAuthorizationManager以实现最终授权的判断。具体来说,WCF通过调用ServiceAuthorizationManager的CheckAccess方法决定当前操作是否被授权访问。

为了让自定义授权有深刻的理解,在《实例篇》中我们将演示一个简单的实例为你展示如何通过自定义AuthorizationPolicy和ServiceAuthorizationManager实现非角色授权,敬请期待。

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起

我们一致在说 ASP.NET Core广泛地使用到了依赖注入,通过前面两个系列的介绍,相信读者朋友已经体会到了这一点。由于前面两章已经涵盖了依赖注入在管道构建过...

4187
来自专栏JAVA技术zhai

JVM虚拟机详解

  JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿...

1412
来自专栏增长技术

git对象模型

所有用来表示项目历史信息的文件,是通过一个40个字符的(40-digit)“对象名”来索引的,对象名看起来像这样:

1283
来自专栏Java技术分享

Redis特性和应用场景

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

1K7
来自专栏微服务那些事儿

java多线程(一)快速认识线程

答:继承Thread类,实现Runnable接口,为了展示自己学识的渊博,还可能还会说实现Callable接口通过FutureTask包装器来创建Thread线...

1534
来自专栏Java技术分享

50道Java线程题

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速。...

2437
来自专栏java一日一条

Java 编程要点之并发(Concurrency)详解

计算机用户想当然地认为他们的系统在一个时间可以做多件事。他们认为,他们可以工作在一个字处理器,而其他应用程序在下载文件,管理打印队列和音频流。即使是单一的应用程...

842
来自专栏Android群英传

Gradle函数复用的一点实践

1201
来自专栏后端技术探索

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

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

1322
来自专栏Java技术栈

高级 Java 面试通关知识点整理!

2093

扫码关注云+社区

领取腾讯云代金券