您的位置:首页 > 运维架构

AOP 下的权限控制实现

2016-02-25 17:08 274 查看
摘要

面向方面的编程(AOP)是一种新的编程技术,它弥补了面向对象的编程(OOP)在跨越模块行为上的不足。AOP 引进了 Aspect,它将影响多个类的行为封装到一个可重用模块中,它允许程序员对横切关注点进行模块化,从而消除了 OOP 引起的代码混乱和分散问题,增强了系统的可维护性和代码的重用性。本文分析传统权限控制的实现方法,并研究了在 AOP 下权限控制的实现方法。

OOP 应用开发面临的问题

面向对象技术很好地解决了软件系统中角色划分的问题。借助于面向对象的分析、设计和实现技术,开发者可以将问题领域的“名词”转换成软件系统中的对象,从而很自然地完成从问题到软件的转换。

但是,问题领域的某些需求却偏偏不是用这样的“名词”来描述的。比如遇到这样的问题:需要对系统中的某些方法进行权限检验,这种需要权限检验的方法散布在 40 多个类中。面对这种需求,应该怎么办呢?最直接的办法就是:创建一个起类(或接口),将权限检验的功能放在其中,并让所有需要权限检验的类继承这个起类(或接口).如果这个需求是后期提出的.需要修改的地方就会分散在 40 多个文件中。这样大的修改量,无疑会增加出错的几率,并且加大系统维护的难度。

人们认识到,传统的程序经常表现出一些不能自然地适合单个程序模块或者几个紧密相关的程序模块的行为例如权限检验、日志记录、对上下文敏感的错误处理、性能优化以及设计模式等等、我们将这种行为称为“横切关注点(crosscuttingconcern)”,因为它跨越了给定编程模型中的典型职责界限。如果使用过用于横切关注点的代码,您就会知道缺乏模块性所带来的问题。因为横切行为的实现是分散的,开发人员发现这种行为难以作逻辑思维、实现和更改。

AOP 的基本思想

AOP 是 Aspect Oriented Programming 的缩写,意思是面向方面编程,一种新兴的编程技术。AOP 实际是 GoF 设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP 可以说也是这种目标的一种实现。它可以解决 OOP 和过程化方法不能够很好解决的横切(crosscut)问题,如:事务、安全、日志等横切关注。当未来系统变得越来越复杂,横切关注点就成为一个大问题的时候,AOP 就可以很轻松的解决横切关注点这个问题。



图1 把模块作为一批关注点来实现

通常,为满足整个企业应用某方面得需求,开发者(架构师)需要整理出系统得关注点。图 1 形象地描述了关注点,它能够从 AOP Aspect 角度看待系统。比如,持久化、日志、应用的业务逻辑通常被认为是应用需要解决的问题。因此,他们通常作为关注点看待。从整个系统角度考虑,它往往是由大量的关注点构成的。
我们把 AOP 看作是 OOP 的延续,而不是竞争对手。OOP 在通常的场合下工作得很好,但在特定的领域里却有所欠缺:举例来说,如果我们必须为多个对象和方法应用相同的事务行为,我们需要将同样的代码剪切/粘贴到每一个方法里。AOP 让我们可以把这类问题封装到方面(aspect)中,从而更好地实现模块化。AOP 定义了“切入点”(pointcut)的概念,让开发者可以从另一个角度来思考程序的结构,从而弥补了 OOP 的某些缺陷:如果需要对一组方法施加横切的行为,就应该拦截这些方法。

在 J2EE 应用开发中,我们主要用到 AOP 的拦截(interception)能力,它为我们提供了“在任何对象的方法调用前/后加入自定义行为”的能力,这使得我们可以处理企业应用中的横切(crosscutting)关注点(即:同时作用于多个对象的关注点),并且仍然保持强类型(不需要改变方法签名)。

权限控制的应用程序实现

对于权限管理的做法,在 WEB 实现上,有以下几种:

1、利用 Filter,对所有进入的 URI 进行解析,并取得当时 Session 中的 User 信息,然后通过 RBAC 的机制,将此链接需要的权限与用户拥有的权限进行比较,然后进行相应的处理。这种做法有很多好处:简单,容易实现,并且对系统侵入性也不强。这里 URL 就是 RBAC 中的资源了。这样做的缺点是所有对数据的操作必须通过 URL 来体现,这一点在现代的程序中不太好实现。如果采用 Struts,XWork 或者 Tapestry,采用同一个 URL(浏览器看来)进行处理多项任务已不是什么稀奇的事。
2、利用一个 BaseServlet(Servlet + Jsp 经典模式)或者 BaseAction(Struts 模式)或者 BasePage(Tapestry 模式)或者 BaseController(SpringMVC 模式),对所有的请求先进行过滤进行权限操作,然后再处理。稍微看一下就知道这种模式跟 Filter 并无本质不同。优缺点同上。

那么,如果要实现更为细致的权限操作,精确到某个方法的权限,典型的做法如下:

[java] view plain copy

print?

public someFunciton() {

 //权限判断

 User user = context.getUser();

 if (user.canExecuteThisFunction()) {

  // do the business method

  // ...

 } else {

  throw new PermissionDeniedException();

 }

}

这种做法能够将权限的粒度控制到具体的业务方法,因此它的控制能力应该是强大的。可以看到,权限判断部分对于每个方法几乎是独立的。
这种在具体功能前加入权限操作检验的实现方式有很多缺点:
1、每个功能类都需要相应的权限检验代码,将程序功能和权限检验混淆在一起,存在紧密的耦合性,扩展修改难度大。
2、以代理模式为每个功能类实现一个相应的代理类,虽然解耦了程序功能和权限检验,但是,从某个角色的权限检验这个切面考虑,涉及具体 Proxy 类太多,扩展修改难度大。
权限控制的 J2EE 容器实现

在 AOP 概念没有诞生前,J2EE 规范已经提供了关于权限控制的容器实现标准,这种变迁结果如下图所示:



图2 权限控制的J2EE容器实现

原来需要每个应用程序实现的权限 Proxy 转为整个容器的 Proxy 实现,其中 JDK1.3 以后的动态代理 API 为这种转换实现提供了技术保证。
非常明显,通过容器实现权限控制验证可以大大简化应用程序的设计,分离了应用系统的权限关注,将权限控制变成了对 J2EE 容器服务器的配置工作。
其实,容器的权限实现也是一种从一个侧面来解决问题方式,AOP 概念诞生后,权限控制实现由此也带来了两个方向的变化:
1、J2EE 容器级别的权限实现,也就是容器自身的权限实现。
2、J2EE 应用程序级别的权限实现。
权限控制在容器级别实现似乎使得 J2EE 开发者感觉没有灵活性和可扩展性,其实象 JBoss 4.0 这样的 J2EE 容器,由于引入了 AOP 概念,使得 J2EE 开发者在自己的应用系统中能够直接操纵容器的一些行为。容器和应用系统由于 AOP 引入的 Aspect 切面,变得可以成为一体了。
对于 J2EE 应用系统开发者,能够做到上述境界,必须的条件是对 JBoss 之类 J2EE 容器必须有足够的了解,因为这些方式并不是 J2EE 标准,有可能在移植到新的 J2EE 容器,这些知识和投入变得无用(也有可能将来 J2EE 扩展其标准)。

很显然,使用 AOP 实现 J2EE 应用系统级别的权限控制,是解决上述移植风险的一个主要方法,但是带来的缺点是必须亲自从零开始做起,耗费时间不会很短。

AOP 下的权限控制实现

有了 AOP,新的业务方法可以这样写:

[java] view plain copy

print?

public someFunciton() {

 // do the business method

 // ...

}

没有了额外的权限操作,这个业务方法看起来那么清晰自然。

将对权限的操作作为一个 Advice,并将 Advisor 关注到所有的业务方法(可能有某一个特定 package),然后,剩下的事情就由 RBAC 以及 AOP 来完成了。通过这样的分离,纵向的一个业务方法被分割为一个更为自然的业务方法和一个关注点。这个关注点写法可能如下:

[java] view plain copy

print?

public class PermissionCheckAdvice implements MethodBeforeAdvice {

 public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {

  //权限判断

  if (!this.getContext().getUser().canExcute(this, arg0)) {

   throws new PermissionDeniedException();

  }

 }

}

可能有个问题:如何取得 context 或者当时上下文环境的 User 呢?答案是使用IoC(或称 Dependency Injection),将上下文环境或者 User 作为参数反向传入到逻辑方法中。当然,在传入之前,这些变量是需要初始化的。这个初始化工作可以在 SuperServlet 中进行,并且以 Session 单例的形式保存在应用程序中。下面是 Spring 配置文件的例子:

<beans>
<!-- Bean configuration -->
<bean id="businesslogicbean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>IBusinessLogic</value>
</property>
<property name="target">
<ref local="beanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>thePermissionCheckBeforeAdvisor</value>
</list>
</property>
</bean>
<!-- Bean Classes -->
<bean id="beanTarget" class="BusinessLogic">
<property name="user"><<YOUR USER OBJECT>> </property>
</bean>
<!-- Advisor pointcut definition for before advice -->
<bean id="thePermissionCheckBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="thePermissionCheckBeforeAdvice"/>
</property>
<property name="pattern">
<value>.*</value>
</property>
</bean>
<!-- Advice classes -->
<bean id="thePermissionCheckBeforeAdvice" class="PermissionCheckBeforeAdvice"/>
</beans>


结束语

AOP 引进了 Aspect,它将影响多个类的行为封装到一个可重用模块中,它允许程序员对横切关注点进行模块化,从而消除了 OOP 引起的代码混乱和分散问题,增强了系统的可维护性和代码的重用性。利用 AOP 的拦截(interception)能力,它为我们提供了“在任何对象的方法调用前/后加入自定义行为”的能力,这使得我们可以处理企业应用中的权限控制这一横切关注点,并且仍然保持强类型(不需要改变方法签名),解耦了程序功能和权限检验。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: