Acegi相关类别之间的依赖关系,可以藉由IoC容器来协助建立,在这边您可以使用 Spring 的IoC容器功能,您可以在下载的Acegi档案中,找到acegi-security-sample-tutorial.war,将之使用解压缩软体解 开,可以在WEB-INF\lib下,找到所需的Spring程式库,在Acegi 1.0.3中的acegi-security-sample-tutorial.war所搭配的是Spring 1.2.8。
要完成您第一个Acegi程式,您需要以下的程式库:
- acegi-security-1.0.3.jar
- spring-1.2.8.jar
为了在Web应用程式中,使用Acegi搭配Spring的Context资讯,来完成Acegi的依赖关系配置,您要在web.xml中设定 org.springframework.web.context.ContextLoaderListener,并在Context参数中,指定设定档 案位置与名称,而Acegi主要透过Filter Chain来达到请求的检查、验证、授权、登出等动作,您可以在web.xml中如下设定:
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Acegi 范例</display-name>
<!-- 指定Acegi资讯的设定档-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/acegi-config.xml</param-value>
</context-param>
<!-- Acegi 的 Filter Chain 代理 -->
<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>
<!-- 取得Spring的Context -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
org.acegisecurity.util.FilterToBeanProxy建立 org.acegisecurity.util.FilterChainProxy实例,并将请求转交给FilterChainProxy来处理,接下来 就是配置acegi-config.xml的内容,在这边先假设一个需求情境,您想要对Web应用程式中的/protected/下所有资源进行保护,如 果使用者试图存取/protected/下的资源,就先将其送至/acegilogin.jsp进行登入,登入成功后显示使用者所请求的资源,使用者也可 以直接连接/acegilogin.jsp,登入成功后,预设显示/loginsuccess.jsp,您可以在acegi-config.xml中加入 以下的设定:
<!-- 验证处理,使用表单 -->
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<!-- 验证管理员,处理验证资讯提供者 -->
<property name="authenticationManager" ref="authenticationManager"/>
<!-- 验证失败URL -->
<property name="authenticationFailureUrl" value="/acegilogin.jsp"/>
<!-- 验证成功预设URL -->
<property name="defaultTargetUrl" value="/protected/loginsuccess.jsp"/>
<!-- 验证处理的提交位址 -->
<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
</bean>
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<!-- 验证管理员,处理验证资讯提供者 -->
<property name="authenticationManager" ref="authenticationManager"/>
<!-- 验证失败URL -->
<property name="authenticationFailureUrl" value="/acegilogin.jsp"/>
<!-- 验证成功预设URL -->
<property name="defaultTargetUrl" value="/protected/loginsuccess.jsp"/>
<!-- 验证处理的提交位址 -->
<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
</bean>
可以看到在这边使用了AuthenticationProcessingFilter来处理验证,实际对使用者的验证是交给验证管理员,也就是authenticationManager属性中所设定的实例,可以在acegi-config.xml中加入:
<!-- 验证管理员,管理验证资讯提供者 -->
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers"><!-- 可有多个提供者,其中一个验证通过即可以了 -->
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers"><!-- 可有多个提供者,其中一个验证通过即可以了 -->
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>
验证管理员管理验证提供者,也就是实际提供使用者名称、密码、角色资讯的物件,来源可以是资料库或设定文件中的讯息,基于来源的不同,您可以使用 org.acegisecurity.providers.dao.DaoAuthenticationProvider,并指定其 userDetailsService属性,设定验证讯息来源:
<!-- 验证提供者,指定使用记忆体来源中的验证资讯 -->
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
</bean>
<bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
caterpillar=123456,ROLE_SUPERVISOR
user1=user1pwd,ROLE_USER
user2=user2pwd,disabled,ROLE_USER
</value>
</property>
</bean>
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
</bean>
<bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
caterpillar=123456,ROLE_SUPERVISOR
user1=user1pwd,ROLE_USER
user2=user2pwd,disabled,ROLE_USER
</value>
</property>
</bean>
在这边使用userMap属性指定可以登入的使用者名称、密码、是否启用、角色等资讯。
当验证失败会发生例外,这时需要将之送至/acegilogin.jsp,权限不符而试图取得资源时则将之送至/accessDenied.jsp,这可以使用org.acegisecurity.ui.ExceptionTranslationFilter来达到:
<!-- 发生验证错误或权限错误时的处理 -->
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/acegilogin.jsp"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/protected/accessDenied.jsp"/>
</bean>
</property>
</bean>
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/acegilogin.jsp"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/protected/accessDenied.jsp"/>
</bean>
</property>
</bean>
接下来要定义可以存取的资源,由于在这边是针对URL进行规范,可以使用org.acegisecurity.intercept.web.FilterSecurityInterceptor:
<!-- FilterSecurityInterceptor 对 URI 进行保护 -->
<bean id="filterSecurityInterceptor"
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_SUPERVISOR,ROLE_USER
</value>
</property>
</bean>
<bean id="filterSecurityInterceptor"
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_SUPERVISOR,ROLE_USER
</value>
</property>
</bean>
FilterSecurityInteceprot需要参考至验证管理员取得验证讯息,以对使用者进行验证,而是否授权则必须以投票的方式来决定,Acegi的解决方案有几种:
- 如果有一个同意就通过
- 如果都同意就通过
- 如果都不反对就通过
例如若希望/protected/下可以被使用者(USER)或管理者(SUPERVISOR)存取,采第一个方案的话则使用AffirmativeBased实作,只要有一票通过,就允许存取:
<!-- 授权管理员 -->
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<!-- 是否全部弃权时视为通过 -->
<property name="allowIfAllAbstainDecisions" value="false" />
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter" />
</list>
</property>
</bean>
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<!-- 是否全部弃权时视为通过 -->
<property name="allowIfAllAbstainDecisions" value="false" />
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter" />
</list>
</property>
</bean>
RoleVoter对使用者群组中,有ROLE_作为前置文件的进投票,您也可以在设定RoleVoter实例时,使用其rolePrefix来改变前置文字;DecisionManager收集票数,然后决定结果。
在所有的Filter被呼叫之前,通常会有个HttpSessionContextIntegrationFilter,它会建立Security Context物件,后续的Filter会将安全相关讯息储存于其中,也可以取得当中的安全相关讯息:
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />
Acegi是基于Filter Chain来完成请求的检查、验证、授权等动作,所以要将以上的Filter串在一起了:
<!-- Filter Chain -->
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,
exceptionTranslationFilter,filterSecurityInterceptor
</value>
</property>
</bean>
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,
exceptionTranslationFilter,filterSecurityInterceptor
</value>
</property>
</bean>
由于排版的关系,将Filter的串连作了换行,实际上authenticationProcessingFilter,exceptionTranslationFilter是连在一起不能换行的,请在设定时特别注意。
所完成的acegi-config.xml完整档案如下所示:
- acegi-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 验证处理,使用表单 -->
<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<!-- 验证管理员,处理验证资讯提供者 -->
<property name="authenticationManager" ref="authenticationManager"/>
<!-- 验证失败URL -->
<property name="authenticationFailureUrl" value="/acegilogin.jsp"/>
<!-- 验证成功预设URL -->
<property name="defaultTargetUrl" value="/protected/loginsuccess.jsp"/>
<!-- 验证处理的提交位址 -->
<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
</bean>
<!-- 验证管理员,管理验证资讯提供者 -->
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers"><!-- 可有多个提供者,其中一个验证通过即可以了 -->
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<!-- 验证提供者,指定使用记忆体来源中的验证资讯 -->
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
</bean>
<bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
caterpillar=123456,ROLE_SUPERVISOR
user1=user1pwd,ROLE_USER
user2=user2pwd,disabled,ROLE_USER
</value>
</property>
</bean>
<!-- 发生验证错误或权限错误时的处理 -->
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/acegilogin.jsp"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/protected/accessDenied.jsp"/>
</bean>
</property>
</bean>
<!-- FilterSecurityInterceptor 对 URI 进行保护 -->
<bean id="filterSecurityInterceptor"
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_SUPERVISOR,ROLE_USER
</value>
</property>
</bean>
<!-- 授权管理员 -->
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<!-- 是否全部弃权时视为通过 -->
<property name="allowIfAllAbstainDecisions" value="false" />
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter" />
</list>
</property>
</bean>
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />
<!-- Filter Chain -->
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,
exceptionTranslationFilter,filterSecurityInterceptor
</value>
</property>
</bean>
</beans>

