此节将要了解的是AnonymousProcessingFilter、RememberMeProcessingFilter和LogoutFilter三个过滤器
1、AnonymousProcessingFilter
在大部分企业应用中,存在许多不需要用户登录就可以访问的资源,比如登录页面、退出页面、主页等。鉴于此,Acegi提供了匿名认证服务。这样能够使所有的Web资源得到保护,而不是某些资源不设权限控制,让任何人访问,这样Acegi使Web安全策略模型应用的非常完美。与此同时,SecurityContextHolder(SecurityContext)将始终持有Authentication对象,因此代码的健壮性、可读性也会得到增强。
AnonymousProcessingFilter,该过滤器是用来处理匿名用户的。如果用户尚未登录,将生成一个匿名用户的Authentication存放到ContextHolder中。即当不存在任何授权信息时,自动为Authentication对象添加userAttribute中定义的匿名用户权限。
<bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter"> <property name="key" value="changeThis" /> <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS" /> </bean>
说明:
key:用于指定用户的名称,其实这个属性指定的值只是一个简单的标识符,可以自己取值。
userAttribute:用于指定匿名用户的密码(anonymousUser)、权限信息(ROLE_ANONYMOUS)和启用状态(enabled/disabled),这里anonymousUser实际上是用户名。
另外,和AuthenticationProcessingFilter的一样,AnonymousProcessingFilter也有自己的AuthenticationProvider,即AnonymousProcessingFilter。
<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> <property name="key" value="changeThis" /> </bean>
此处的key一般与AnonymousProcessingFilter中的key保持一致,用于保证Authentication对象的真实性,当然这只是Acegi内部的一个设计细节,开发者直接提供自身的key属性取值即可。同样将该Provider加到authenticationManager的providers属性列表中。(如果不提供AnonymousAuthenticationProvider,同样能实现匿名认证的功能,或者key不相同也没影响,这一点暂时不太明白,如果有谁清楚的请留言给我,让我增加见识。)
2、RememberMeProcessingFilter
该Filter会在用户登录后,在本地机器上记录用户cookies信息,这样下次访问就不用再登录了。它还负责对所有HTTP请求进行拦截,当发现SecurityContextHolder中没有包含有效的Authentication对象时,自动调用RememberMeServices#autoLogin()方法从Cookie中获取用户名/密码的编码串进行自动登录,所以rememberMeProcessingFilter首先要注入一个RememberMeServices Bean。
rememberMeProcessingFilter通过rememberMeServices获取对应Cookie中用户的UserDetails后,就必须进行用户身份认证。这项工作依然委托给authenticationManager完成,所以我们给rememberMeProcessingFilter注入了authenticationManager Bean。
authenticationManager如何对基于Cookie的用户凭证进行认证呢?显然,不能采用原来的daoAuthenticationProvider所用的方法,因为Cookie所提供的用户凭证和登录表单提供的用户凭证在格式上存在很大的差异。基于Remember-Me的用户名/密码信息是经过特殊编码的字符串,Acegi通过RememberMeAuthenticationProvider负责对基于Cookie的用户凭证信息进行认证。所以你必须将该认证提供者添加到authenticationManager中。
<bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter"> <property name="authenticationManager" ref="authenticationManager" /> <property name="rememberMeServices" ref="rememberMeServices" /><!-- 增加 --> </bean> <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref local="daoAuthenticationProvider" /> <!-- 增加 --> <bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> <property name="key" value="changeThis" /> </bean> <!-- 增加 --> <bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> <property name="key" value="foobar" /><!-- key必须和rememberMeServices中的key一致 --> </bean> </list> </property> </bean> <!-- 增加, 默认tokenValiditySeconds = 1209600L, 即保留两周 --> <bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"> <property name="userDetailsService" ref="inMemDaoImpl" /> <!-- cookie中的键值, 防止保存到客户端的cookie中的加密串被恶意篡改 --> <property name="key" value="foobar" /> <!-- cookie有效时间, 单位为秒, 这里设定为5天内不用再登陆 --> <property name="tokenValiditySeconds" value="432000" /> </bean> <!--另外,必须在AuthenticationProcessingFilter中加入rememberMeServices。这样,当用户勾选了记住密码并登录后,rememberMeServices会将用户信息保存到Cookie中--> <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> …… <property name="rememberMeServices" ref="rememberMeServices" /><!-- 增加, 可别忘了此处 --> </bean>
3、LogoutFilter
该Filter负责处理退出登录后所需要的清理工作。包括:
1) 销毁session
2) 清空 ContextHolder
3) 把rememberMeServices从cookies中清除掉
4) 最后重定向到指定的退出登陆页面。
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"> <constructor-arg value="/index.jsp"/> <!-- URL redirected to after logout 退出页面url --> <constructor-arg> <list> <ref bean="rememberMeServices"/><!-- 用于清空cookies --> <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> </list> </constructor-arg> </bean>
最后,请注意,Acegi默认的自动登陆设定参数名为_acegi_security_remember_me,注销链接为/j_acegi_logout。
登录时,在页面添加以下代码:
<input type="checkbox" name="_acegi_security_remember_me">
退出时,在页面添加以下代码:
<a href="<%=request.getContextPath() %>/j_acegi_logout">Logout</a>