当前位置 博文首页 > 适己而忘人者,人之所弃;克己而立人者,众之所戴。:知识积累(

    适己而忘人者,人之所弃;克己而立人者,众之所戴。:知识积累(

    作者:[db:作者] 时间:2021-07-11 12:34

    第二章. 技术概览

    2.1. 运行时环境

    Acegi Security可以在JRE1.3中运行。这个发行版本中支持也Java 5.0,尽管对应的Java类型被分开打包到一个后缀是“tiger”的包中。因为Acegi Security致力于以一种自包含的方式运行,因此不需要在JRE中放置任何特殊的配置文件。特别无需配置Java Authentication and Authorization Service (JAAS)策略文件或者将Acegi Security放置到通用的classpath路径中。

    ?

    同样的,如果你使用EJB容器或者Servlet容器,同样无需放置任何特别的配置文件或者将Acegi Security包含在服务器的类加载器(classloader)中。

    ?

    上述的设计提供了最大的部署灵活性,你可以直接把目标工件(JAR, WAR 或者 EAR))直接从一个系统copy到另一个系统,它马上就可以运行起来。

    ?

    2.2. 共享组件

    让我们来看看Acegi Security中最重要的一些共享组件。所谓共享组件是指在框架中处于核心地位,系统脱离了它们之后就不能运行。这些Java类型代表了系统中其他部分的构建单元,因此理解它们是非常重要的,即使你不需要直接和它们打交道。

    ?

    最基础的对象是SecurityContextHolder。在这里存储了当前应用的安全上下文(security context),包括正在使用应用程序的principal的详细信息。SecurityContextHolder默认使用ThreadLocal来存储这些详细信息,这意味着即便安全上下文(security context)没有被作为一个参数显式传入,它仍然是可用的。如果在当前principal的请求处理后清理线程,那么用这种方式使用ThreadLocal是非常安全的。当然, Acegi Security自动为你处理这些,所以你无需担心。

    ?

    有些应用程序由于使用线程的方式而并不是完全适用ThreadLocal。例如,Swing客户端可能需要一个Java Virtual Machine中的所有线程都使用同样的安全上下文(security context)。在这种情况下你要使用SecurityContextHolder.MODE_GLOBAL模式。另外一些应用程序可能需要安全线程产生的线程拥有同样的安全标识符(security identity)。这可以通过SecurityContextHolder.MODE_INHERITABLETHREADLOCAL来实现。你可以通过两种方法来修改默认的SecurityContextHolder.MODE_THREADLOCAL。第一种是设置一个系统属性。或者,调用SecurityContextHolder的一个静态方法。大部分的应用程序不需要修改默认值,不过如果你需要,那么请查看SecurityContextHolderJavaDocs获取更多信息。

    ?

    我们在SecurityContextHolder中存储当前和应用程序交互的principal的详细信息。Acegi Security使用一个Authentication对象来代表这个信息。尽管你通常不需要自行创建一个Authentication对象,用户还是经常会查询Authentication对象。

    ?

    你可以在你的应用程序中的任何地方使用下述的代码块:

    java 代码

    ?

    1. Object?obj?=?SecurityContextHolder.getContext().getAuthentication().getPrincipal();??
    2. if?(obj?instanceof?UserDetails)?{??
    3. String?username?=?((UserDetails)obj).getUsername();??
    4. }?else?{??
    5. String?username?=?obj.toString();??
    6. }??

    ?

    上述的代码展示了一些有趣的联系和关键的对象。首先,你会注意到在SecurityContextHolderAuthentication之间有一个媒介对象。SecurityContextHolder.getContext() 方法实际上返回一个SecurityContextAcegi Security使用若干个不同的SecurityContext实现,以备我们需要存储一些和principal无关的特殊信息。一个很好的例子就是我们的Jcaptcha集成,它需要知道一个需求是否是由人发起的。这样的判断和principal是否通过认证完全没有关系,因此我们将它保存在SecurityContext中。

    ?

    从上述的代码片段可以看出的另一个问题是你可以从一个Authentication对象中获取一个principalPrincipal只是一个对象。通常可以把它cast为一个UserDetails对象。UserDetailsAcegi Security中是一个核心接口,它以一种扩展以及应用相关的方式来展现一个principal。可以把UserDetails看作是你的用户数据库和Acegi SecuritySecurityContextHolder所需要的东西之间的一个适配器(adapter)。作为你自己用户数据库的一个展现,你可能经常要把它cast到你应用程序提供的原始对象,这样你就可以调用业务相关的方法(例如 getEmail(), getEmployeeNumber())

    ?

    现在你可能已经开始疑惑,那我什么时候提供UserDetails对象呢?我要如何提供呢?

    我想你告诉过我这个东西是声明式的,我不需要写任何Java代码-那怎么做到呢?对此的简短回答就是有一个叫做UserDetailsService的特殊接口。这个接口只有一个方法,接受一个Sring类型的参数并返回一个UserDetailsAcegi Security提供的大多数认证提供器将部分认证过程委派给UserDetailsServiceUserDetailsService用来构建保存在SecurityContextHolder中的Authentication对象。好消息是我们提供若干个UserDetailsService的实现,包括一个使用in-memory map和另一个使用JDBC的。

    大多数用户还是倾向于写自己的实现,这样的实现经常就是简单的构建于已有的Data Access Object (DAO)上,这些DAO展现了他们的雇员、客户、或者其他企业应用程序中的用户。要记得这样做的好处,不论你的UserDetailsService返回什么,它总是可以从SecurityContextHolder中获取,象上面的代码显示的那样。

    ?

    除了principalAuthentication提供的另一个重要方法就是getAuthorities()。这个方法返回一个GrantedAuthority对象数组。GrantedAuthority,毫无疑问,就是授予principal的权限。这些权限通常是角色,例如ROLE_ADMINISTRATOR 或者ROLE_HR_SUPERVISOR。这些角色稍后配置到web授权,方法授权和领域对象授权。Acegi Security的其他部分能够处理这些权限,并且期待他们被提供。通常你会从UserDetailsService中返回GrantedAuthority对象。

    ?

    通常GrantedAuthority对象都是应用范围的权限。它们都不对应特定的领域对象。因此,你应该不会有一个代表54号员工对象的GrantedAuthority,因为这样会有数以千计的authority,你马上就会用光所有内存(或者,至少会让系统花太长时间来认证一个用户)。当然,Acegi Security会高效的处理这种普遍的需求,但是你不会使用领域对象安全功能来实现这个目的。

    ?

    最后,但不是不重要,你有时候需要在HTTP 请求之间存储SecurityContext。另外有些时候你在每次请求的时候都会重新认证principal,不过大部分时候你会存储SecurityContextHttpSessionContextIntegrationFilterHTTP之间存储SecurityContext。正如类名字显示的那样,它使用HttpSession来进行存储。基于安全原因,你永远都不要直接和HttpSession交互。没有理由这么做,所以记得使用SecurityContextHolder来代替。

    ?

    让我们回忆一下,Acegi Security的基本组成构件是:

    ? SecurityContextHolder,提供对SecurityContext的所有访问方式。

    ? SecurityContext, 存储Authentication以及可能的请求相关的安全信息。

    ? HttpSessionContextIntegrationFilter, web请求之间把SecurityContext存储在HttpSession中。

    ? Authentication, Acegi Security的方式表现principal

    ? GrantedAuthority, 表示赋予一个principal的应用范围的权限。

    ? UserDetails, 为从你的应用程序DAO中获取必要的信息来构建一个Authentication 对象。

    ? UserDetailsService,用传入的String类型的username(或者认证ID,或类似)来创建一个UserDetails

    ?

    现在你已经理解了这些重复使用的组件,让我们仔细看看认证过程吧。

    ?

    2.3. 认证