您的位置:首页 > 其它

Acegi Security System学习

2012-02-06 14:25 190 查看
一 可以为如下任务配置ACEGI安全过滤器:

在访问一个安全资源之前提示用户登录。

通过检查安全标记(如密码),对用户进行身份验证。

检查经过身份验证的用户是否具有访问某个安全资源的特权。

将成功进行身份验证和授权的用户重定向到所请求的安全资源。

对不具备访问安全资源特权的用户显示 Access Denied 页面。

在服务器上记录成功进行身份验证的用户,并在用户的客户机上设置安全 cookie。使用该 cookie 执行下一次身份验证,而无需要求用户登录。

将身份验证信息存储在服务器端的会话对象中,从而安全地进行对资源的后续请求。

在服务器端对象中构建并保存安全信息的缓存,从而优化性能。

当用户退出时,删除为用户安全会话而保存的服务器端对象。

与大量后端数据存储服务(如目录服务或关系数据库)进行通信,这些服务用于存储用户的安全信息和 ECM 的访问控制策略。

acegi Security System 由四种主要类型的组件组成:过滤器、管理器、提供者和处理程序。

过滤器这种最高级的组件提供了常见的安全服务,如身份验证、会话处理以及注销。我将在 本文后面的部分 深入讨论过滤器。管理器过滤器仅是安全相关功能的高级抽象:实际上要使用管理器和提供者实现身份验证处理和注销服务。管理器管理由不同提供者提供的较低级的安全服务。提供者有大量的提供者可用于和不同类型的数据存储服务通信,例如目录服务、关系数据库或简单的内存中的对象。这意味着您可以将用户库和访问控制协议存储在任何一种这样的数据存储服务中,并且 Acegi 的管理器将在运行时选择合适的提供者。处理程序有时任务可能会被分解为多个步骤,其中每个步骤由一个特定的处理程序执行。比方说,Acegi 的 注销过滤器 使用两个处理程序来退出一个 HTTP 客户机。其中一个处理程序使用户的 HTTP 会话无效,而另一个处理程序则删除用户的 cookie。当根据应用程序需求配置 Acegi 时,多个处理程序能够提供很好的灵活性。您可以使用自己选择的处理程序来执行保护应用程序所需的步骤。

清单 1. Spring 配置文件的结构

<beans>
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value> value here </value>
</property>
</bean>

<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFitler">
<property name="authenticationManager" ref="authManager"/>
<!-- Other properties -->
</bean>

<bean id="authManager"
class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<!--  List of providers here -->
</property>
</bean>
<!-- Other bean tags -->
</beans>

如您所见,Acegi 使用的 Spring XML 配置文件包含一个
<beans>
标记,它封装了一些其他的
<bean>
标记。所有的 Acegi 组件(即过滤器、管理器、提供者等)实际上都是 JavaBean。XML 配置文件中的每个
<bean>
标记都代表一个 Acegi 组件。

进一步解释 XML 配置文件

首先将注意到的是每个
<bean>
标记都包含一个 class 属性,这个属性标识组件所使用的类。
<bean>
标记还具有一个 id 属性,它标识作为 Acegi 组件工作的实例(Java 对象)。

比方说,清单 1 的第一个
<bean>
标记标识了名为
filterChainProxy
的组件实例,它是名为
org.acegisecurity.util.FilterChainProxy
的类的实例。

使用
<bean>
标记的子标记来表示 bean 的依赖关系。比如,注意第一个
<bean>
标记的
<property>
子标记。
<property>
子标记定义了
<bean>
标记依赖的其他 bean 或值。

所以在 清单 1 中,第一个
<bean>
标记的
<property>
子标记具有一个 name 属性和一个
<value>
子标记,分别定义了这个 bean 依赖的属性的名称和值。

同样,清单 1 中的第二个和第三个
<bean>
标记定义了一个过滤器 bean 依赖于一个管理器 bean。第二个
<bean>
标记表示过滤器 bean,而第三个
<bean>
标记表示管理器 bean。

过滤器的
<bean>
标记包含一个
<property>
子标记,该子标记具有两个属性:
name
ref
name
属性定义了过滤器 bean 的属性,而
ref
属性引用了管理器 bean 的实例(名称)。

Session Integration Filter

Acegi 的 Session Integration Filter(SIF)通常是您将要配置的第一个过滤器。SIF 创建了一个安全上下文对象,这是一个与安全相关的信息的占位符。其他 Acegi 过滤器将安全信息保存在安全上下文中,也会使用安全上下文中可用的安全信息。

SIF 创建安全上下文并调用过滤器链中的其他过滤器。然后其他过滤器检索安全上下文并对其进行更改。比如,Authentication Processing Filter(我将稍后进行介绍)将用户信息(如用户名、密码和电子邮件地址)存储在安全上下文中。

当所有的处理程序完成处理后,SIF 检查安全上下文是否更新。如果任何一个过滤器对安全上下文做出了更改,SIF 将把更改保存到服务器端的会话对象中。如果安全上下文中没有发现任何更改,那么 SIF 将删除它。

在 XML 配置文件中对 SIF 进行了配置,如清单 2 所示:

清单 2. 配置 SIF

<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>

Authentication Processing Filter

Acegi 使用 Authentication Processing Filter(APF)进行身份验证。APF 使用一个身份验证(或登录)表单,用户在其中输入用户名和密码,并触发身份验证。

APF 执行所有的后端身份验证处理任务,比如从客户机请求中提取用户名和密码,从后端用户库中读取用户参数,以及使用这些信息对用户进行身份验证。

在配置 APF 时,您必须提供如下参数:

Authentication manager 指定了用来管理身份验证提供者的身份验证管理器。

Filter processes URL 指定了客户在登录窗口中按下 Sign In 按钮时要访问的 URL。收到这个 URL 的请求后,Acegi 立即调用 APF。

Default target URL 指定了成功进行身份验证和授权后呈现给用户的页面。

Authentication failure URL 指定了身份验证失败情况下用户看到的页面。

APF 从用户的请求对象中得到用户名、密码和其他信息。它将这些信息传送给身份验证管理器。身份验证管理器使用适当的提供者从后端用户库中读取详细的用户信息(如用户名、密码、电子邮件地址和用户访问权利或特权),对用户进行身份验证,并将信息存储在一个
Authentication
对象中。

最后,APF 将
Authentication
对象保存在 SIF 之前创建的安全上下文中。存储在安全上下文中的
Authentication
对象将用于做出授权决策。

APF 的配置如清单 3 所示:

清单 3. 配置 APF

<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager"
ref="authenticationManager" />
<property name="filterProcessesUrl"
value="/j_acegi_security_check" />
<property name="defaultTargetUrl"
value="/protected/protected1.jsp" />
<property name="authenticationFailureUrl"
value="/login.jsp?login_error=1" />
</bean>

Logout Processing Filter

Acegi 使用一个 Logout Processing Filer(LPF)管理注销处理。当客户机发来注销请求时,将使用 LPF 进行处理。它标识了来自由客户机所调用 URL 的注销请求。

LPF 的配置如清单 4 所示:

清单 4. 配置 LPF

<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="/logoutSuccess.jsp"/>
<constructor-arg>
<list>
<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>

Exception Translation Filter

Exception Translation Filter(ETF)处理身份验证和授权过程中的异常情况,比如授权失败。在这些异常情况中,ETF 将决定如何进行操作。

比如,如果一个没有进行身份验证的用户试图访问受保护的资源,ETF 将显示一个登录页面要求用户进行身份验证。类似地,在授权失败的情况下,可以配置 ETF 来呈现一个 Access Denied 页面。

ETF 的配置如清单 5 所示:

清单 5. 配置 ETF

<bean id="exceptionTranslationFilter"
class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.jsp" />
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.jsp" />
</bean>
</property>
</bean>

拦截过滤器

Acegi 的拦截过滤器 用于做出授权决策。您需要在 APF 成功执行身份验证后对拦截过滤器进行配置,以使其发挥作用。拦截器使用应用程序的访问控制策略来做出授权决定。

本系列的下一篇文章将展示如何设计访问控制策略,如何将它们托管在目录服务中,以及如何配置 Acegi 以读取您的访问控制策略。但是,目前我将继续向您展示如何使用 Acegi 配置一个简单的访问控制策略。在本文后面的部分,您将看到使用简单的访问控制策略构建一个样例应用程序。

配置简单的访问控制策略可分为两个步骤:

编写访问控制策略。

根据策略配置 Acegi 的拦截过滤器。

步骤 1. 编写简单的访问控制策略

首先看一下 清单 6,它展示了如何定义一个用户及其用户角色:

清单 6. 为用户定义简单的访问控制策略

alice=123,ROLE_HEAD_OF_ENGINEERING

清单 6 所示的访问控制策略定义了用户名
alice
,它的密码是
123
,角色是
ROLE_HEAD_OF_ENGINEERING
。(下一节将说明如何在文件中定义任意数量的用户及其用户角色,然后配置拦截过滤器以使用这些文件。)

步骤 2. 配置 Acegi 的拦截过滤器

拦截过滤器使用三个组件来做出授权决策,我在清单 7 中对其进行了配置:

清单 7. 配置拦截过滤器

<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/protected/**=ROLE_HEAD_OF_ENGINEERING
/**=IS_AUTHENTICATED_ANONYMOUSLY
</value>
</property>
<!--  More properties of the interceptor filter -->
</bean>

如清单 7 所示,配置所需的三个组件是
authenticationManager
accessDecisionManager
objectDefinitionSource


authenticationManager
组件与我在介绍 Authentication Processing Filter 时讨论过的身份验证管理器相同。拦截过滤器可以在授权的过程中使用
authenticationManager
重新对客户机进行身份验证。

accessDecisionManager
组件管理授权过程,这部分内容将在本系列的下篇文章中详细讨论。

objectDefinitionSource
组件包含对应于将要发生的授权的访问控制定义。例如,清单 7 中的
objectDefinitionSource
属性值包含两个 URL(
/protected/*
/*
)。其值定义了这些 URL 的角色。
/protected/*
URL 的角色是
ROLE_HEAD_OF_ENGINEERING
。您可以根据应用程序的需要定义任何角色。

回想一下 清单 6,您为用户名
alice
定义了
ROLE_HEAD_OF_ENGINEERING
。这就是说
alice
将能够访问
/protected/*
URL。

回页首
过滤器工作原理

正如您已经了解到的一样,Acegi 的组件彼此依赖,从而对您的应用程序进行保护。在本文后面的部分,您将看到如何对 Acegi 进行配置,从而按照特定的顺序应用安全过滤器,因此需要创建过滤器链。出于这个目的,Acegi 保存了一个过滤器链对象,它封装了为保护应用程序而配置了的所有过滤器。图 1 展示了 Acegi 过滤器链的生命周期,该周期从客户机向您的应用程序发送 HTTP 请求开始。(图 1 显示了服务于浏览器客户机的容器。)

图 1. 托管 Acegi 过滤器链以安全地为浏览器客户机服务的容器



下面的步骤描述了过滤器链的生命周期:

浏览器客户机向您的应用程序发送 HTTP 请求。

容器接收到 HTTP 请求并创建一个请求对象,该对象将封装 HTTP 请求中包含的信息。容器还创建一个各种过滤器都可处理的响应对象,从而为发出请求的客户机准备好 HTTP 响应。容器然后调用 Acegi 的过滤器链代理,这是一个代理过滤器。该代理知道应用的过滤器的实际顺序。当容器调用代理时,它将向代理发送请求、响应以及过滤器链对象。

代理过滤器调用过滤器链中第一个过滤器,向其发送请求、响应和过滤器链对象。

链中的过滤器逐个执行其处理。一个过滤器可以通过调用过滤器链中下一个过滤器随时终止自身处理。有的过滤器甚至根本不执行任何处理(比如,如果 APF 发现一个到来的请求没有要求身份验证,它可能会立即终止其处理)。

当身份验证过滤器完成其处理时,这些过滤器将把请求和响应对象发送到应用程序中配置的拦截过滤器。

拦截器决定是否对发出请求的客户机进行授权,使它访问所请求的资源。

拦截器将控制权传输给应用程序(比如,成功进行了身份验证和授权的客户机请求的 JSP 页面)。

应用程序改写响应对象的内容。

响应对象已经准备好了,容器将响应对象转换为 HTTP 响应,并将响应发送到发出请求的客户机。

为帮助您进一步理解 Acegi 过滤器,我将详细探讨其中两个过滤器的操作:Session Integration Filter 和 Authentication Processing Filter。

SIF 如何创建一个安全上下文

图 2 展示了 SIF 创建安全上下文所涉及到的步骤:

图 2. SIF 创建安全上下文



现在详细地考虑下面这些步骤:

Acegi 的过滤器链代理调用 SIF 并向其发送请求、响应和过滤器链对象。注意:通常将 SIF 配置为过滤器链中第一个过滤器。

SIF 检查它是否已经对这个 Web 请求进行过处理。如果是的话,它将不再进一步进行处理,并将控制权传输给过滤器链中的下一个过滤器(参见下面的第 4 个步骤)。如果 SIF 发现这是第一次对这个 Web 请求调用 SIF,它将设置一个标记,将在下一次使用该标记,以表示曾经调用过 SIF。

SIF 将检查是否存在一个会话对象,以及它是否包含安全上下文。它从会话对象中检索安全上下文,并将其放置在名为 security context holder 的临时占位符中。如果不存在会话对象,SIF 将创建一个新的安全上下文,并将它放到 security context holder 中。注意:security context holder 位于应用程序的范围内,所以可以被其他的安全过滤器访问。

SIF 调用过滤器链中的下一个过滤器。

其他过滤器可以编辑安全上下文。

SIF 在过滤器链完成处理后接收控制权。

SIF 检查其他的过滤器是否在其处理过程中更改了安全上下文(比如,APF 可能将用户详细信息存储在安全上下文中)。如果是的话,它将更新会话对象中的安全上下文。就是说在过滤器链处理过程中,对安全上下文的任何更改现在都保存在会话对象中。

APF 如何对用户进行身份验证

图 3 展示了 APF 对用户进行身份验证所涉及到的步骤:

图 3. APF 对用户进行身份验证



现在仔细考虑以下这些步骤:

过滤器链中前面的过滤器向 APF 发送请求、响应和过滤链对象。

APF 使用从请求对象中获得的用户名、密码以及其他信息创建身份验证标记。

APF 将身份验证标记传递给身份验证管理器。

身份验证管理器可能包含一个或更多身份验证提供者。每个提供者恰好支持一种类型的身份验证。管理器检查哪一种提供者支持它从 APF 收到的身份验证标记。

身份验证管理器将身份验证标记发送到适合进行身份验证的提供者。

身份验证提供者支持从身份验证标记中提取用户名,并将它发送给名为 user cache service 的服务。Acegi 缓存了已经进行过身份验证的用户。该用户下次登录时,Acegi 可以从缓存中加载他或她的详细信息(比如用户名、密码和权限),而不是从后端数据存储中读取数据。这种方法使得性能得到了改善。

user cache service 检查用户的详细信息是否存在于缓存中。

user cache service 将用户的详细信息返回给身份验证提供者。如果缓存不包含用户详细信息,则返回 null。

身份验证提供者检查缓存服务返回的是用户的详细信息还是 null。

如果缓存返回 null,身份验证提供者将用户名(在步骤 6 中提取)发送给另一个名为 user details service 的服务。

user details service 与包含用户详细信息的后端数据存储通信(如目录服务)。

user details service 返回用户的详细信息,或者,如果找不到用户详细信息则抛出身份验证异常。

如果 user cache service 或者 user details service 返回有效的用户详细信息,身份验证提供者将使用 user cache service 或 user details service 返回的密码来匹配用户提供的安全标记(如密码)。如果找到一个匹配,身份验证提供者将用户的详细信息返回给身份验证管理器。否则的话,则抛出一个身份验证异常。

身份验证管理器将用户的详细信息返回给 APF。这样用户就成功地进行了身份验证。

APF 将用户详细信息保存在 图 2 所示由步骤 3 创建的安全上下文中。

APF 将控制权传输给过滤器链中的下一个过滤器。

回页首
一个简单的 Acegi 应用程序

在本文中,您已经了解了很多关于 Acegi 的知识,所以现在看一下利用您目前学到的知识能做些什么,从而结束本文。对于这个简单的演示,我设计了一个样例应用程序(参见 下载),并对 Acegi 进行了配置以保护它的一些资源。

样例应用程序包含 5 个 JSP 页面:index.jsp、protected1.jsp、protected2.jsp、login.jsp 和 accessDenied.jsp。

index.jsp 是应用程序的欢迎页面。它向用户显示了三个超链接,如图 4 所示:

图 4. 样例应用程序的欢迎页面:



图 4 所示的链接中,其中两个链接指向了被保护的资源(protected1.jsp 和 protected2.jsp),第三个链接指向登录页面(login.jsp)。只有在 Acegi 发现用户没有被授权访问受保护的资源时,才会显示 accessDenied.jsp 页面。

如果用户试图访问任何受保护的页面,样例应用程序将显示登录页面。当用户使用登录页面进入后,应用程序将自动重定向到被请求的受保护资源。

用户可以通过单击欢迎页面中的第三个链接直接请求登录页面。这种情况下,应用程序显示用户可以进入系统的登录页面。进入系统以后,应用程序将用户重定向到 protected1.jsp,它是用户进入系统而没有请求特定的受保护资源时显示的默认资源。

配置样例应用程序

为本文下载的源代码包含一个名为 acegi-config.xml 的 XML 配置文件,它包含 Acegi 过滤器的配置。根据 安全过滤器的讨论 中的示例,您应该很熟悉这些配置。

我还为样例应用程序编写了一个
web.xml
文件,如清单 8 所示:

清单 8. 样例应用程序的 web.xml 文件

<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/acegi-config.xml</param-value>
</context-param>
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>
org.acegisecurity.util.FilterToBeanProxy
</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>
org.acegisecurity.util.FilterChainProxy
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>

web.xml 文件配置如下:

acegi-config.xml
文件的 URL 位于
<context-param>
标记中。

Acegi 过滤器链代理类的名称位于
<filter>
标记中。

URL 到 Acegi 过滤器链代理的映射在
<filter-mapping>
标记中。注意:您可以简单地将应用程序的所有 URL(
/*
)映射到 Acegi 过滤器链代理。Acegi 将对映射到 Acegi 过滤器链代理上的所有 URL 应用安全性。

应用程序上下文加载程序位于
<listener>
标记中,它将加载 Spring 的 IOC 框架。

部署并运行应用程序

部署并运行样例应用程序非常的简单。只需要完成两件事情:

将 acegisample.war 文件从本教程下载的源代码中复制到安装 Tomcat 的
webapps
目录中。

从 Acegi Security System 主页 下载并解压缩 acegi-security-1.0.3.zip。您将找到一个名为 acegi-security-sample-tutorial.war 的样例应用程序。解压缩 war 文件并提取其 WEB-INF/lib 文件夹中所有的 jar 文件。将所有的 JAR 文件从 WEB-INF/lib 文件夹中复制到 theacegisample.war 应用程序的 WEB-INF/lib 文件夹。

现在,您已经为运行样例应用程序做好准备了。启动 Tomcat 并将浏览器指向
http://localhost:8080/acegisample/


您将看到 图 4 所示的欢迎页面,但是此时显示的页面是真实的。请继续运行程序,并查看在尝试访问欢迎页面显示的不同链接时会发生什么状况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: