Acegi Security的技术概述
运行环境 (Runtime Environment)
Acegi Security将运行在J2SE 1.3以上的环境中。当然它也支持Java 5.0,涉及到Java 5.0的特殊的类被独立打包,并封装在以“tiger”作为后缀的jar文件中,比如acegi-security-tiger-1.0.3.jar。Acegi Security强调独立性,不需在JRE环境中添加任何配置文件。需要特别指出的是,虽然Acegi Security作为验证及授权的框架,也不需要配置JAAS(Java Authentication and Authorization Service)的安全策略文件,也不需要将Acegi Security加入JRE环境的classpath中。<o:p></o:p>
同样地,即使您使用EJB容器或者Servlet容器,也无需添加任何配置文件或将Acegi Security加入服务器的类加载器中。<o:p></o:p>
上述的设计是为了在部署的时候能有最大限度的灵活性,您只需要将最终的工件(可能是jar文件、war文件或者ear文件)拷贝到目标系统中,便可以立即工作。公共组件 (Shared Components)<o:p></o:p>
接下来,我们来将共同探讨Acegi Security框架中最重要的公共组件。首先,让我们理解什么是“公共”组件。通常地说,那些作为框架的核心并且在框架中不可或缺的组件被认为是“公共组件”。这些Java类相互合作构成了整个框架的基石,它们在框架中相当重要,甚至您不应该直接和它们进行交互。<o:p></o:p>
在Acegi Security框架中最基础的组件应该算是SecurityContextHolder(org.acegisecurity.context.SecurityContextHolder)。SecurityContextHolder是存放系统安全信息(包括正在使用系统的主体“Principal”的详细信息)上下文的环境。默认情况下,SecurityContextHolder使用SecurityContextHolder.MODE_THREADLOCAL方式来存储信息,即通过java.lang.ThreadLocal来保存信息,这意味着Acegi Security提供的是一个线程安全环境,您不会再被线程安全的问题所困扰。<o:p></o:p>
当然并不是所有应用都适合使用ThreadLocal,它们用些特殊的方式来处理线程。比如一个Swing客户端希望JVM中的所有线程都共用一个上下文。在这种情况下,您应该使用SecurityContextHolder.MODE_GLOBAL方式。有些应用中的线程是由一个安全线程中派生出来的,那么您将使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL方式来管理这些线程。您可以通过两种方式来改变SecurityContextHolder的存储策略:通过设置系统属性或调用setStrategyName方法。
- 1、 通过设置系统属性来改变SecurityContextHolder存储策略:
- System.setProperty(SecurityContextHolder.SYSTEM_PROPERTY, SecurityContextHolder.MODE_GLOBAL);
- 2、 通过调用setStrategyName方法:
- SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_GLOBAL);
在SecurityContextHolder内部保存着使用系统的主体当前与系统交互的信息,Acegi Securit框架用Authentication -- 一个Acegi Security风格的主体表现 -- 来表示这些信息。您通常不需要自行创建Authentication对象,框架提供了一个通用的方法去获取Authentication对象。您可以在应用程序的任何地方使用以下代码来获取Authentication对象。
- Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
- if (obj instanceof UserDetails) {
- String username = ((UserDetails)obj).getUsername();
- } else {
- String username = obj.toString();
- }
上述代码中,我们可以发现一些关键对象及它们之间的关系。首先引起我们注意的应该是通过SecurityContextHolder.getContext()方法获取到的介于SecurityContextHolder和Authentication之间的org.acegisecurity.context.SecurityContext对象。在Acegi Security框架中存在多种不同的SecurityContext实现。<o:p></o:p>
在上述代码中另一个值得关注的地方是通过Authentication对象中获取主体对象(principal object),而这个principal对象实际上是一个java.lang.Object对象。大多数情况下,可以将这个principle对象转换成org.acegisecurity.userdetails.UserDetails.UserDetails对象。UserDetails是Acegi Security框架中的一个核心接口,它通常扮演一个principal对象。在实际运用中,可以把UserDetails看作是Acegi Security与数据库记录之间的适配器,也可以把它看作是现有系统中主体对象与Acegi Security的适配器。<o:p></o:p>
您现在或许会问,那么我如何获取一个UserDetails对象?有一个简短的答案通过org.acegisecurity.userdetails.UserDetailsService接口获得。UserDetailsService仅提供一个通过用户名获取UserDetails对象的方法。Acegi Security框架中的大部分验证提供者(authentication provider)委托UserDetailsService处理验证过程的一部分,用来创建存储在SecurityContextHolder中的Authentication对象。有个好消息,Acegi Security框架中提供了多种UserDetailsService的实现,其中包括内存数据库和JDBC的实现方式。虽然大部分程序员倾向于在现有的DAO实现的基础上编写自己的实现,但是请记住,无论你的UserDetailsService返回的是什么对象,都可以通过SecurityContextHolder中获取,即都将被保存在SecurityContextHolder中。<o:p></o:p>
除了能返回主体对象以外,Authentication对象还提供了另一个重要的方法getAuthorities(),这个方法将返回一个org.acegisecurity.GrantedAuthority对象数组。GrantedAuthority对象就是使用系统的主体被授予的权限。[有部分没有翻译]<o:p></o:p>
有的时候,您需要将SecurityContext保存在不同的Http请求中。有的时候,您又希望使用系统的主体在每次请求时,都要重新验证。Acegi Security提供了org.acegisecurity.context. HttpSessionContextIntegrationFilter在不同的Http请求之间保存SecurityContext对象。正如这个类的名称所暗示的,它将采用javax.servlet.HttpSession来保存。出于安全的考虑,您不应绕过Acegi Security框架,直接通过HttpSession获取信息。实际上,我想您没有足够的理由这么做,您应该只与SecurityContextHolder交互。<o:p></o:p>
让我们一起回顾Acegi Security中最主要的公共组件:
- SecurityContextHolder,可以通过SecurityContext获取任何类型的主体对象。
- SecurityContext,保存Authentication对象及一些特殊的请求的安全信息。
- HttpSessionContextIntegrationFilter,在不同的Http请求之间用HttpSession保存SecurityContext对象。
- Authentication, Acegi Security风格的系统主体的代言。
- GrantedAuthority,系统主体所获得的应用程序级的许可。
- UserDetails,从现有的DAO系统中获取必要的信息,来创建一个Authentication对象。
- UserDetailsService,通过用户名或身份标识来创建一个UserDetails对象。
我想您现在对Acegi Security的这些公共组件有了初步的认识,接下去我们将深入了解Acegi Security框架的验证过程。