这里,需要注意以下两点:
1) 这几个filter的顺序是不能更改的,顺序不对将无法正常工作;
2) 如果你的应用不需要安全传输,如https,则将"Acegi Channel Processing Filter"相关内容注释掉即可;
3) 如果你的应用不需要Spring提供的远程访问机制,如Hessian and Burlap,将"Acegi HTTP BASIC Authorization
Filter"相关内容注释掉即可。
[applicationContext.xml]
接下来就是要添加applicationContext.xml中的内容了,从刚才FilterToBeanFactory的解释可以看出,真正的filter都
在Spring的applicationContext中管理:
1) 首先,你的数据库中必须具有保存用户名和密码的table,Acegi要求table的schema必须如下:
CREATE TABLE users (
username VARCHAR(50) NOT NULL PRIMARY KEY,
password VARCHAR(50) NOT NULL,
enabled BIT NOT NULL
);
CREATE TABLE authorities (
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL
);
CREATE UNIQUE INDEX ix_auth_username ON authorities ( username, authority );
ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users foreign key (username) REFERENCES users
(username);
2) 添加访问你的数据库的datasource和Acegi的jdbcDao,如下:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 1配置FilterChainProxy --> <!-- FilterChainProxy会按顺序来调用这些filter,使这些filter能享用Spring ioc的功能 --> <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource"> <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON <!-- 定义了url比较前先转为小写--> PATTERN_TYPE_APACHE_ANT <!-- 定义了使用Apache ant的匹配模式--> /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor </value> </property> </bean> <!-- 2配置8个filter过滤器 --> <!--这个bean负责每次请求从HttpSession中获取Authentication对象,然后把Authentication存于一个新的 ContextHolder对象 --> <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/> <!-- 注销处理filter,这个bean负责处理退出登录后所需要的清理工作。它会把session销毁,把ContextHolder清空, 把rememberMeServices从cookies中清除掉,然后重定向到指定的退出登陆页面 --> <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"> <constructor-arg value="/shop/index.do"/> <!-- URL redirected to after logout --> <constructor-arg> <list> <ref bean="rememberMeServices"/> <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> </list> </constructor-arg> </bean> <!-- 表单认证处理filter,这个bean处理一个认证表单,登陆用的表单必须提交用户名和密码这两个参数给这个filter.由用户名和密码构造一个 UsernamePasswordAuthenticationToken,将传给AuthenticationManager的 authenticate方法进行认证处理。该filter默认处理filterProcessesUrl属性指定的URL,认证失败会转到 authenticationFailureUrl,认证成功会转到defaultTargetUrl页面。 --> <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/><!-- 定义登陆失败时转向的页面 --> <property name="defaultTargetUrl" value="/admin/index.jsp"/><!-- 义登陆成功时转向的页面 --> <property name="filterProcessesUrl" value="/j_acegi_security_check"/><!-- 定义登陆请求的页面 --> <property name="rememberMeServices" ref="rememberMeServices"/><!--用于在验证成功后添加cookie信息--> </bean> <!--这个bean保存当前的请求到SavedRequest,并存入Session,然后转到登录页。 --> <bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/> <!-- 这个bean以cookie的形式来保存认证信息。负责在用户登录后在本地机上记录用户cookies信息,免除下次再次登陆。检查 AuthenticationManager 中是否已存在Authentication对象,如果不存在则会调用RememberMeServices的aotoLogin方法来从cookies中获取Authentication对象。 --> <bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="rememberMeServices" ref="rememberMeServices"/> </bean> <!-- 这个bean负责为当不存在任何授权信息时,自动为Authentication对象添加userAttribute中定义的匿名用户权限 --> <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter"> <property name="key" value="changeThis"/> <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/> </bean> <!-- 异常转换过滤器,主要是处理AccessDeniedException和AuthenticationException,将给每个异常找到合适的"去向" --> <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="/accessDenied.jsp"/> </bean> </property> </bean> <!-- 这个bean会首先调用AuthenticationManager判断用户是否已登陆认证,如还没认证成功,则重定向到登陆界面.认证成功,则从 Authentication中获取用户的权限.然后从objectDefinitionSource属性获取各种URL资源所对应的权限.最后调用 AccessDecisionManager来判断用户所拥有的权限与当前受保护的URL资源所对应的权限是否相匹配.如果匹配失败,则返回403错误给用户.匹配成功则用户可以访问受保护的URL资源 --> <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/> <property name="objectDefinitionSource"> <value> PATTERN_TYPE_APACHE_ANT /system/*.jsp*=admin /login.jsp*=ROLE_ANONYMOUS,admin,user /index.jsp*=ROLE_ANONYMOUS,admin,user </value> </property> </bean> <!-- 3配置rememberMeServices记录cookie--> <!-- 这个bean负责通过以cookie的形式保存先前的用户登录信息。在Authentication对象不存在时, rememberMeProcessingFilter会调用rememberMeServices的autoLogin()方法,尝试在cookie中获取用户登录信息,如果存在则并返回Authentication对象。在每次用户登录时,如果设置了RememberMe功能,在验证用户身份成功后,则会调用loginSuccess()方法记录用户信息在cookie中,否则调用loginFail()方法清除cookie。 --> <bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"> <property name="userDetailsService" ref="userJdbcDaoImpl"/> <property name="key" value="changeThis"/> </bean> <!-- 用于在数据中获取用户信息。 acegi提供了用户及授权的表结构,但是您也可以自己来实现。通过usersByUsernameQuery这个SQL得到你的(用户ID,密码,状态信息);通过authoritiesByUsernameQuery这个SQL得到你的(用户ID,授权信息) --> <bean id="userJdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl"> <property name="dataSource" ref="dataSource"/> <property name="usersByUsernameQuery"> <value> select loginid,passwd,1 from ss_users where status='1' and loginid = ? </value> </property> <property name="authoritiesByUsernameQuery"> <value> select u.loginid,r.name from ss_users u,ss_roles r,ss_user_role ur where u.id=ur.user_id and r.id=ur.role_id and u.status='1' and u.loginid=? </value> </property> </bean> <!-- 4配置authenticationManager--> <!-- AuthenticationManager的其中一个实现是ProviderManager,它负责把身份验证的工作委托给一个或多个Provider(认证提供者). --> <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref local="daoAuthenticationProvider"/> <ref local="anonymousAuthenticationProvider"/> <ref local="rememberMeAuthenticationProvider"/> </list> </property> </bean> <!-- 通过Providers提供认证者列表,如果一个认证提供者失败可以尝试另外一个认证提供者,以保证获取不同来源的身份认证,如 DaoAuthenticationProvider 从数据库中读取用户信息验证身份 AnonymousAuthenticationProvider 匿名用户身份认证 RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证 其它的还有 AuthByAdapterProvider 使用容器的适配器验证身份 CasAuthenticationProvider 根据Yale中心认证服务验证身份, 用于实现单点登录 JaasAuthenticationProvider 从JASS登录配置中获取用户信息验证身份 RemoteAuthenticationProvider 根据远程服务验证用户身份 RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证 X509AuthenticationProvider 从X509认证中获取用户信息验证身份 TestingAuthenticationProvider 单元测试时使用 每个认证者会对自己指定的证明信息进行认证,如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationToken这个证明信息进行认证。 --> <!-- daoAuthenticationProvider负责提供用户信息,包括用户名和密码。其中取用户名密码的工作就交给 userDetailsService来做。通过userCache来缓存用户信息,减少查询数据库次数。用passwordEncoder来使用加密密码。userDetailsService的接口实现有jdbcDaoImpl和inMemoryDaoImpl。jdbcDaoImpl通过数据库获取用户名和密码,而inMemoryDaoImpl则只是通过xml定义的方式来获取。 --> <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> <property name="userDetailsService" ref="userJdbcDaoImpl"/><!-- 认证数据访问对象,用于获取用户信息,包括:用户名,用户密码,用户状态和用户权限 --> <property name="userCache" ref="userCache"/><!-- 缓存user信息 --> <property name="passwordEncoder" ref="passwordEncoder"/><!-- 对密码进行私钥加密 --> </bean> <!-- userCache提供了两种实现: NullUserCache和EhCacheBasedUserCache, NullUserCache实际上就是不进行任何缓存,EhCacheBasedUserCache是使用Ehcache来实现缓功能 --> <bean name="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"> <property name="cache"> <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean" autowire="byName"> <property name="cacheManager" ref="cacheManager"/> <property name="cacheName" value="userCache"/> </bean> </property> </bean> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>classpath:org/springside/bookstoredemo/components/acegi/ehcache.xml</value> </property> </bean> <bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/> <!-- anonymousAuthenticationProvider--> <bean id="anonymousAuthenticationProvider" class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> <property name="key" value="anonymous"/> </bean> <!-- rememberMeAuthenticationProvider--> <bean id="rememberMeAuthenticationProvider" class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> <property name="key" value="appfuseRocks"/> </bean> <!-- 5配置accessDecisionManager--> <!-- authenticationManager,AccessDecisionManager来判断用户所拥有的权限与当前受保护的URL资源所对应的权限是否相匹配.如果匹配失败,则返回403错误给用户.匹配成功则用户可以访问受保护的URL资源--> <bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions" value="false"/><!-- allowIfAllAbstainDecisions为false时如果有一个或以上的decisionVoters投票通过,则授权通过。可选的决策机制有ConsensusBased和UnanimousBased --> <property name="decisionVoters"> <list> <bean class="org.acegisecurity.vote.RoleVoter"> <property name="rolePrefix" value="AUTH_"/> <!-- 必须是以rolePrefix设定的value开头的权限才能进行投票,如AUTH_ , ROLE_ --> </bean> </list> </property> </bean> <!-- 5配置authenticationProcessingFilterEntryPoint--> <!-- 当抛出AccessDeniedException时,将用户重定向到登录界面。属性loginFormUrl配置了一个登录表单的URL,当需要用户登录时,authenticationProcessingFilterEntryPoint会将用户重定向到该URL --> <bean id="authenticationProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl"> <value>/security/login.jsp</value> </property> <property name="forceHttps" value="false"/> </bean> <!-- 6配置MethodSecurityInterceptor--> <!-- MethodSecurityInterceptor实现了org.aopalliance.intercept.MethodInterceptor接口.在方法被调用之前,拦截器会先调用AuthenticationManager判断用户身份是否已验证,然后从 objectDefinitionSource中获取方法所应用的权限,再调用AccessDecisionManager来匹配用户权限和方法对应的权限.如果用户没有足够权限调用当前方法,则抛出AccessDeniedException是方法不能被调用.调用runAsManager,使在调用方法前动态改变authentication中获取用户权限. --> <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/> <property name="objectDefinitionSource"> <value> <!-- sample.contact.ContactManager.create=ROLE_USER sample.contact.ContactManager.getAllRecipients=ROLE_ADMIN --> </value> </property> </bean> <!-- 7配置channelProcessingFilter--> <!-- 如果需要一些页面通过安全通道的话,如https --> <bean id="channelProcessingFilter" class="org.acegisecurity.securechannel.ChannelProcessingFilter"> <property name="channelDecisionManager" ref="channelDecisionManager"/> <property name="filterInvocationDefinitionSource"> <value> PATTERN_TYPE_APACHE_ANT /admin/**=REQUIRES_SECURE_CHANNEL /login*=REQUIRES_SECURE_CHANNEL /j_security_check*=REQUIRES_SECURE_CHANNEL /editProfile.html*=REQUIRES_SECURE_CHANNEL /signup.html*=REQUIRES_SECURE_CHANNEL /saveUser.html*=REQUIRES_SECURE_CHANNEL /**=REQUIRES_INSECURE_CHANNEL </value> </property> </bean> <bean id="channelDecisionManager" class="org.acegisecurity.securechannel.ChannelDecisionManagerImpl"> <property name="channelProcessors"> <list> <bean class="org.acegisecurity.securechannel.SecureChannelProcessor"/> <bean class="org.acegisecurity.securechannel.InsecureChannelProcessor"/> </list> </property> </bean> <!-- 7配置basicProcessingFilter --> <!-- 如果需要一些页面通过安全通道的话,如https --> <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint"/> </bean> <bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint"> <property name="realmName" value="SpringSide Realm"/> </bean> <!-- 添加loggerListener--> <bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener"/> </beans>