下面,我们为authenticationProcessingFilter注入AuthenticationManager,后者将完成具体的身份认证工作:
代码清单 5 applicationContext-acegi-plugin.xml
认证管理器的配置
<beans> … <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> <property name="filterProcessesUrl" value="/j_acegi_security_check"/> <property name="defaultTargetUrl" value="/main.jsp"/> <property name="authenticationFailureUrl" value="/index.jsp?login_error=1"/> ①注入认证管理器 <property name="authenticationManager" ref="authenticationManager" /> </bean> ②认证管理器 <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> ②-1使用基于DAO的认证提供者提供认证服务 <ref local="daoAuthenticationProvider" /> </list> </property> </bean> <bean id="daoAuthenticationProvider" ③基于DAO的认证提供者 class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> ③-1根据用户名获取系统中真实UserDetails 对象的服务类 <property name="userDetailsService" ref="userDetailsService" /> </bean> ④ 该服务类根据缓存在内存中的用户信息列表获取userDetails对象 <bean id="userDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"> <property name="userMap"> ④-1用户信息 <value> john=john,PRIV_COMMON,PRIV_1 tom=tom,PRIV_COMMON,PRIV_1,PRIV_2 peter=peter,disabled,PRIV_COMMON,PRIV_1 </value> </property> </bean> </beans>
在①处,我们为authenticationProcessingFilter注入了一个AuthenticationManager Bean。AuthenticationManager有两个实现类:
l MockAuthenticationManager:这个实现类用于开发时的测试环境,它将所有待认证的用户标志为有效有户;
l ProviderManager:该实现类将用户身份认证的工作委托给多个提供者来完成。
这里,我们使用ProviderManager来定义一个AuthenticationManager Bean,如②所示。可以通过ProviderManager的providers属性配置多个认证提供者,认证提供者必须实现org.acegisecurity.providers.AuthenticationProvider接口。身份认证的多样性通过AuthenticationProvider接口实现类的多样性体现出来,Acegi针对不同的安全系统提供了10多个不同的认证提供者。这些不同的认证提供者在表 1中进行说明: 
这里,我们使用DaoAuthenticationProvider,在②-1所示。它负责从数据库或其它保存用户信息的媒介中获取用户信息进行认证。
DaoAuthenticationProvider首先从SecurityContextHolder的Authentication中得到待认证的用户名,并根据该用户名获取保存在数据库或其它媒介中代表真正系统用户的UserDetails对象。紧接着比较Authentication和UserDetails的匹配关系(如看密码是否相等),如果两者匹配,认证成功,并将UserDetails的权限信息将拷贝到Authentication中。如果不匹配,认证将失败。
DaoAuthenticationProvider通过UserDetailsService完成UserDetails的获取工作,根据存储用户信息媒介的不同,Acegi提供了两个UserDetailsService的实现类:
l InMemoryDaoImpl:该实现类负责从内存中获取用户的信息,它允许通过UserMap或Properties直接在Spring配置文件中定义系统用户信息。
l JdbcDaoImpl:该实现类从数据库中获取用户的信息。我们将在下一节讨论该实现类的使用方法。
在这里,我们使用InMemoryDaoImpl获取UserDetails,如③-1所示并通过userMap属性定义系统用户信息,如④-1所示。userMap属性的类型为UserMap,Acegi已经向Spring注册了对应的编辑器,所以我们可以通过格式化的字符串为UserMap类型的属性提供配置值。在④-1处,我们提供了几行键值对的配置值,每一行代表一个用户及其对应的权限信息。下面我们通过图 2来了解代表一个用户的字符串的格式: 
8 用户配置信息
我们通过④-1定义了三个用户,john、tom和peter,它们对应的密码和用户名相同。其中john和tom是激活的用户,而peter用户处于非激活状态。john和peter都拥有PRIV_COMMON,PRIV_1权限,而tom额外拥有PRIV_2的权限。
如果用户数比较多,在Spring中直接进行配置未免不太雅观,这时,你可以将用户信息转移到一个属性文件中,并通过userProperties进行加载:
<bean id="userDetailsService"
class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userProperties">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="/WEB-INF/users.properties" />
</bean>
</property>
</bean>
users.properties文件内容的格式和刚才我们介绍的方式相同,既每一个用户对应一行格式化配置串。
通过以上分析后,我们可以理出参与用户身份认证所涉及到4个Bean,它们之间的关系可通过图 9来描述:

图 9 基于内存用户信息认证的主要Bean之间的关系
我们知道,大部分应用系统的用户信息并非一个小小的属性文件可以应付的,它们一般保存在数据库中。InMemoryDaoImpl存在的更多意义在于方便程序测试——你可以不依赖外部数据库环境完成测试。在生产环境下,我们应该使用JdbcDaoImpl这个UserDetailsService实现类从数据库中获取UserDetails。