百度首页 | 百度空间 | 登录
启明之笔
若隐若现乃相思,雪落无痕心神驰.亦喜亦忧怅寥廓,寒来暑往渐成诗。
主页博客相册|个人档案 |好友
查看文章
Springside之开发bookstore心得二
2007-07-17 20:31
这个bean以cookie的形式来保存认证信息。负责在用户登录后在本地机上记录用户cookies信息,免除下次再次登陆。检查 AuthenticationManager 中是否已存在Authentication对象,如果不存在则会调用RememberMeServices的aotoLogin方法来从cookies中获取Authentication对象。
1.9顺序添加nonymousProcessingFilter
<bean id="anonymousProcessingFilter"
class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key" value="changeThis"/>
<property name="userAttribute"
value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>
这个bean负责为当不存在任何授权信息时,自动为Authentication对象添加userAttribute中定义的匿名用户权限
1.10顺序添加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="/accessDenied.jsp"/>
</bean>
</property>
</bean>
这个bean负责处理各种异常,然后重定向到相应的页面中。
1.11顺序添加filterInvocationInterceptor
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"
ref="authenticationManager"/>
<property name="accessDecisionManager">
<bean class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"
value="false"/>
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter"/>
<bean
class="org.acegisecurity.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>
</property>
<property name="objectDefinitionSource"
ref="filterDefinitionSource"/>
</bean>
这个bean会首先调用AuthenticationManager判断用户是否已登陆认证,如还没认证成功,则重定向到登陆界面.认证成功,则从 Authentication中获取用户的权限.然后从objectDefinitionSource属性获取各种URL资源所对应的权限.最后调用 AccessDecisionManager来判断用户所拥有的权限与当前受保护的URL资源所对应的权限是否相匹配.如果匹配失败,则返回403错误给用户.匹配成功则用户可以访问受保护的URL资源
1.12至此,我们在1.3中所声明的8个Filter全部装载完成。试运行,会提示找不到rememberMeServices,这个rememberMeServices在1.6和1.8中声明,添加rememberMeServices:
<bean id="rememberMeServices"
class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="changeThis"/>
</bean>
这个bean负责通过以cookie的形式保存先前的用户登录信息。在Authentication对象不存在时, rememberMeProcessingFilter会调用rememberMeServices的autoLogin()方法,尝试在cookie中获取用户登录信息,如果存在则并返回Authentication对象。在每次用户登录时,如果设置了RememberMe功能,在验证用户身份成功后,则会调用loginSuccess()方法记录用户信息在cookie中,否则调用loginFail()方法清除cookie。
1.13为1.12添加userDetailsService:
<bean id="userDetailsService"
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>
1.14为1.11添加authenticationManager:
<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="changeThis"/>
</bean>
</list>
</property>
</bean>
AuthenticationManager的其中一个实现是ProviderManager,它负责把身份验证的工作委托给一个或多个Provider(认证提供者).
Provider都是实现AuthenticationProvider接口,该接口有两个方法authenticate()和support(). authenticate()方法会尝试验证用户身份,若验证成功则返回一个Authentication对象,否则抛出一个 AuthenticationException.
support()方法会评估当前Authentication对象是否适合这个Provider来进行进一步的处理,而不是指已经通过.
Provir有多个实现.例如daoAuthenticationProvider,anonymousAuthenticationProvider,rememberMeAuthenticationProvider.
1.15添加daoAuthenticationProvide:
<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="userCache" ref="userCache"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>
daoAuthenticationProvider负责提供用户信息,包括用户名和密码。其中取用户名密码的工作就交给 userDetailsService来做。通过userCache来缓存用户信息,减少查询数据库次数。用passwordEncoder来使用加密密码。userDetailsService的接口实现有jdbcDaoImpl和inMemoryDaoImpl。jdbcDaoImpl通过数据库获取用户名和密码,而inMemoryDaoImpl则只是通过xml定义的方式来获取。
userCache的接口实现有EhCacheBasedUserCache和NullUserCache。NullUserCache实际上就是不进行缓存。EhCacheBasedUserCache是基于ehcache的开源缓存项目来实现的。
passwordEncoder是使用加密器对用户输入的明文进行加密。Acegi提供了三种加密器:
PlaintextPasswordEncoder---默认,不加密,返回明文.
ShaPasswordEncoder---哈希算法(SHA)加密
d5PasswordEncoder---消息摘要(MD5)加密
1.16添加userCache:
<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>
1.17添加cacheManager:
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:org/springside/bookstoredemo/components/acegi/ehcache.xml</value>
</property>
</bean>
1.18添加bookstoredemo/arc/java/org/springside/bookstoredemo/components/acegi/ehcache.xml,内容参照源代码。
1.19添加passwordEncoder:
<!-- 使用Md5算法加密 -->
<bean id="passwordEncoder"
class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>
1.20添加filterDefinitionSource
<bean id="filterDefinitionSource"
class="org.springside.bookstoredemo.components.acegi.intercept.web.DBFilterInvocationDefinitionSource">
<property name="convertUrlToLowercaseBeforeComparison"
value="true"/>
<property name="useAntPath" value="true"/>
<property name="acegiCacheManager" ref="acegiCacheManager"/>
</bean>
1.21添加
org.springside.bookstoredemo.components.acegi.domain.Resource;
org.springside.bookstoredemo.components.acegi.domain.Role;
org.springside.bookstoredemo.components.acegi.domain.User;
org.springside.bookstoredemo.components.acegi.resourcedetails.Resourceimport org.springside.bookstoredemo.components.acegi.Constants;
org.springside.bookstoredemo.components.acegi.resourcedetails.ResourceCache;
org.springside.bookstoredemo.components.acegi.cache.AcegiCacheManager;
org.springside.bookstoredemo.components.acegi.resourcedetails.ResourceDetails;
org.springside.bookstoredemo.components.acegi.intercept.web.DBFilterInvocationDefinitionSource.java;
添加
org.springside.bookstoredemo.components.acegi.domain.hbm.Resource.hbm.xml;
org.springside.bookstoredemo.components.acegi.domain.hbm.Role.hbm.xml;
org.springside.bookstoredemo.components.acegi.domain.hbm.Role.hbm.xml;
这几个文件稍稍改下class的映射地址
在dataAccessContext-hibernate.xml中合适地方添加
<property name="mappingDirectoryLocations">
<list>
<value>
classpath*:/org/springside/bookstoredemo/components/acegi/domain/hbm/
</value>
</list>
</property>
1.22添加acegiCacheManager
<bean id="acegiCacheManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="false" autowire="byName">
<property name="transactionManager" ref="transactionManager"/>
<property name="proxyTargetClass" value="true"/>
<property name="target">
<bean class="org.springside.bookstoredemo.components.acegi.cache.AcegiCacheManagerImpl">
<property name="resourceCache" ref="resourceCache"></property>
<property name="userCache" ref="userCache"></property>
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="modify*">PROPAGATION_REQUIRED</prop>
<prop key="auth*">PROPAGATION_REQUIRED</prop>
<prop key="init*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="refresh*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
1.23添加resourceCache
<!-- 自建一个resourceCache以存放resource对应的role关系 -->
<!--
resource缓存实际执行对象
-->
<bean id="resourceCache"
class="org.springside.bookstoredemo.components.acegi.resourcedetails.ResourceCache"
autowire="byName">
<property name="cache">
<bean id="resourceCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="cacheName" value="resourceCache"/>
</bean>
</property>
</bean>
1.24添加loggerListener
<!-- This bean is optional; it isn't used by any other bean as it only listens and logs -->
<bean id="loggerListener"
class="org.acegisecurity.event.authentication.LoggerListener"/>
1.25打开浏览器,输入http://localhost:8080/bookstoredemo/admin/,提示404错误。我们在webapp 目录下新建文件acegilogin.jsp,并把bookstore例子中的文件内容拷贝过来,再访问http://localhost: 8080/bookstoredemo/admin/,会打开acegilogin,jsp页面,这个页面用来供用户登录。我们先看看Acegi是如何转到这个登录页面的:首先浏览器发送http://localhost:8080/bookstoredemo/admin/请求,调用 httpSessionContextIntegrationFilter,从HttpSession中获取Authentication对象,然后把 Authentication存于一个新的ContextHolder对象。因为没有Authentication信息,Acegi转到异常处理 Filter(1.10声明),并转向到loginFormUrl。
1.26登录失败的流程
1.26.1用户不存在
用户输入错误的用户名或密码,Acegi会转到表单认证Filter(1.6中声明),转向到authenticationFailureUrl;
1.26.2用户没有权限
用户登录信息正确,Acegi通过系列数据库查询,会得出用户没有任何权限,Acegi会通过/j_acegi_security_check自动转向到/accessDenied.jsp。
1.26登录成功的流程
用户登录信息正确,Acegi通过系列数据库查询,得到用户admin对/admin/下的所有请求都有权限(可以自行分析数据库的数据)。Acegi转到表单认证,转向到defaultTargetUrl。
1.27登出流程
用户登出,如果没有声明注销处理Filter(1.5中声明),会清除登录信息,并最终重定向到/j_acegi_logout上,而如果声明了注销处理,则重定向到constructor-arg的Value上
1.28需要注意的几点
登录用户名和密码必须是j_username,j_password;/j_acegi_logout;/j_acegi_security_check.
另外,还有几个重要的拦截器和组件:
1.29
<bean id="securityInterceptor"
class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="validateConfigAttributes">
<value>false</value>
</property>
<property name="authenticationManager">
<ref local="authenticationManager" />
</property>
<property name="accessDecisionManager">
<ref local="accessDecisionManager" />
</property>
<property name="runAsManager">
<ref local="runAsManager" />
</property>
<property name="objectDefinitionSource">
<value>
sample.contact.ContactManager.create=ROLE_USER
sample.contact.ContactManager.getAllRecipients=ROLE_ADMIN
</value>
</property>
</bean>
MethodSecurityInterceptor实现了org.aopalliance.intercept.MethodInterceptor接口.在方法被调用之前,拦截器会先调用AuthenticationManager判断用户身份是否已验证,然后从 objectDefinitionSource中获取方法所应用的权限,再调用AccessDecisionManager来匹配用户权限和方法对应的权限.如果用户没有足够权限调用当前方法,则抛出AccessDeniedException是方法不能被调用.调用runAsManager,使在调用方法前动态改变authentication中获取用户权限.
1.30
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="proxyTargetClass" value="true" />
<property name="interceptorNames">
<list>
<idref local="securityInterceptor" />
</list>
</property>
<property name="beanNames">
<list>
<idref bean="userManager"/>
<idref bean="roleManager"/>
<idref bean="rescManager"/>
</list>
</property>
</bean>
1.31
BeanNameAutoProxyCreator
设置AOP代理的最简单方法就是用Spring的BeanNameAutoProxyCreator.在BeanNameAutoProxyCreator中选出你所需要的interceptor,和列出你所需要保护的Bean.
<bean id="accessDecisionManager" class="org.acegisecurity.vote.UnanimousBased">
<property name="allowIfAllAbstainDecisions">
<value>false</value>
</property>
<property name="decisionVoters">
<list>
<ref local="roleVoter" />
</list>
</property>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />
AccessDecisionManager接口有decide()和support()方法.decide()方法是进行决策是否批准通过,如果没有抛出AccessDeniedException则为允许访问资源,否则拒绝访问.support()方法是根据配置属性和受保护资源的类来判断是否需要对该资源作出决策判断.
AccessDecisionManager有三个实现类,功能各不相同:
AffirmativeBased:当至少有一个Voter投允许票才通过
UnanimousBased:没有Voter投反对票时才通过
ConsensusBased:当所有Voter都投允许票时才通过
1.32
<bean id="runAsManager" class="org.acegisecurity.runas.RunAsManagerImpl">
<property name="key">
<value>my_run_as_password</value>
</property>
</bean>
runAsmanager提供了动态替换ContextHolder中Ahthentication对象的功能
2.Book管理
按照前面所讲的Acegi登录成功以后,左边会出现 Book Manager和Shop Manager,意味着我们可以在权限内对Book和Shop进行管理了。
2.1Book和Pojo和Manager我们可以用刚开始建的,现在只要建立Book的Controller:
新建org.springside.bookstoredemo.web.admin.BookManagerAction.java,内容如下:
public class BookManagerAction extends StrutsEntityAction<Book, BookManager> {
private BookManager bookManager;
public void setBookManager(BookManager bookManager) {
this.bookManager = bookManager;
}
}
当然必要的import和package不能省。
2.2 在/WEB-INF/web.xml中的struts Action Mapping部分添加struts-congfig-admin.xml路径;
2.3在struts-config-admin.xml中添加
<form-beans>
<form-bean name="bookForm" type="org.apache.struts.validator.LazyValidatorForm"/>
</form-beans>
以及
<action path="/admin/book" name="bookForm" parameter="method" scope="request" validate="false">
<forward name="list" path="/WEB-INF/pages/admin/bookList.jsp"/>
<forward name="edit" path="/WEB-INF/pages/admin/bookForm.jsp"/>
<forward name="success" path="/admin/book.do" redirect="true"/>
</action>
2.4新建
/WEB-INF/pages/admin/bookList.jsp
/WEB-INF/pages/admin/bookForm.jsp
2.5在/WEB-INF/modules/spring-config-admin.xml中添加
<bean name="/admin/book" class="org.springside.bookstoredemo.web.admin.BookManagerAction"/>
启明之笔
若隐若现乃相思,雪落无痕心神驰.亦喜亦忧怅寥廓,寒来暑往渐成诗。
主页博客相册|个人档案 |好友
查看文章
Springside之开发bookstore心得二
2007-07-17 20:31
这个bean以cookie的形式来保存认证信息。负责在用户登录后在本地机上记录用户cookies信息,免除下次再次登陆。检查 AuthenticationManager 中是否已存在Authentication对象,如果不存在则会调用RememberMeServices的aotoLogin方法来从cookies中获取Authentication对象。
1.9顺序添加nonymousProcessingFilter
<bean id="anonymousProcessingFilter"
class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key" value="changeThis"/>
<property name="userAttribute"
value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>
这个bean负责为当不存在任何授权信息时,自动为Authentication对象添加userAttribute中定义的匿名用户权限
1.10顺序添加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="/accessDenied.jsp"/>
</bean>
</property>
</bean>
这个bean负责处理各种异常,然后重定向到相应的页面中。
1.11顺序添加filterInvocationInterceptor
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"
ref="authenticationManager"/>
<property name="accessDecisionManager">
<bean class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"
value="false"/>
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter"/>
<bean
class="org.acegisecurity.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>
</property>
<property name="objectDefinitionSource"
ref="filterDefinitionSource"/>
</bean>
这个bean会首先调用AuthenticationManager判断用户是否已登陆认证,如还没认证成功,则重定向到登陆界面.认证成功,则从 Authentication中获取用户的权限.然后从objectDefinitionSource属性获取各种URL资源所对应的权限.最后调用 AccessDecisionManager来判断用户所拥有的权限与当前受保护的URL资源所对应的权限是否相匹配.如果匹配失败,则返回403错误给用户.匹配成功则用户可以访问受保护的URL资源
1.12至此,我们在1.3中所声明的8个Filter全部装载完成。试运行,会提示找不到rememberMeServices,这个rememberMeServices在1.6和1.8中声明,添加rememberMeServices:
<bean id="rememberMeServices"
class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="changeThis"/>
</bean>
这个bean负责通过以cookie的形式保存先前的用户登录信息。在Authentication对象不存在时, rememberMeProcessingFilter会调用rememberMeServices的autoLogin()方法,尝试在cookie中获取用户登录信息,如果存在则并返回Authentication对象。在每次用户登录时,如果设置了RememberMe功能,在验证用户身份成功后,则会调用loginSuccess()方法记录用户信息在cookie中,否则调用loginFail()方法清除cookie。
1.13为1.12添加userDetailsService:
<bean id="userDetailsService"
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>
1.14为1.11添加authenticationManager:
<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="changeThis"/>
</bean>
</list>
</property>
</bean>
AuthenticationManager的其中一个实现是ProviderManager,它负责把身份验证的工作委托给一个或多个Provider(认证提供者).
Provider都是实现AuthenticationProvider接口,该接口有两个方法authenticate()和support(). authenticate()方法会尝试验证用户身份,若验证成功则返回一个Authentication对象,否则抛出一个 AuthenticationException.
support()方法会评估当前Authentication对象是否适合这个Provider来进行进一步的处理,而不是指已经通过.
Provir有多个实现.例如daoAuthenticationProvider,anonymousAuthenticationProvider,rememberMeAuthenticationProvider.
1.15添加daoAuthenticationProvide:
<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="userCache" ref="userCache"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>
daoAuthenticationProvider负责提供用户信息,包括用户名和密码。其中取用户名密码的工作就交给 userDetailsService来做。通过userCache来缓存用户信息,减少查询数据库次数。用passwordEncoder来使用加密密码。userDetailsService的接口实现有jdbcDaoImpl和inMemoryDaoImpl。jdbcDaoImpl通过数据库获取用户名和密码,而inMemoryDaoImpl则只是通过xml定义的方式来获取。
userCache的接口实现有EhCacheBasedUserCache和NullUserCache。NullUserCache实际上就是不进行缓存。EhCacheBasedUserCache是基于ehcache的开源缓存项目来实现的。
passwordEncoder是使用加密器对用户输入的明文进行加密。Acegi提供了三种加密器:
PlaintextPasswordEncoder---默认,不加密,返回明文.
ShaPasswordEncoder---哈希算法(SHA)加密
d5PasswordEncoder---消息摘要(MD5)加密
1.16添加userCache:
<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>
1.17添加cacheManager:
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:org/springside/bookstoredemo/components/acegi/ehcache.xml</value>
</property>
</bean>
1.18添加bookstoredemo/arc/java/org/springside/bookstoredemo/components/acegi/ehcache.xml,内容参照源代码。
1.19添加passwordEncoder:
<!-- 使用Md5算法加密 -->
<bean id="passwordEncoder"
class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>
1.20添加filterDefinitionSource
<bean id="filterDefinitionSource"
class="org.springside.bookstoredemo.components.acegi.intercept.web.DBFilterInvocationDefinitionSource">
<property name="convertUrlToLowercaseBeforeComparison"
value="true"/>
<property name="useAntPath" value="true"/>
<property name="acegiCacheManager" ref="acegiCacheManager"/>
</bean>
1.21添加
org.springside.bookstoredemo.components.acegi.domain.Resource;
org.springside.bookstoredemo.components.acegi.domain.Role;
org.springside.bookstoredemo.components.acegi.domain.User;
org.springside.bookstoredemo.components.acegi.resourcedetails.Resourceimport org.springside.bookstoredemo.components.acegi.Constants;
org.springside.bookstoredemo.components.acegi.resourcedetails.ResourceCache;
org.springside.bookstoredemo.components.acegi.cache.AcegiCacheManager;
org.springside.bookstoredemo.components.acegi.resourcedetails.ResourceDetails;
org.springside.bookstoredemo.components.acegi.intercept.web.DBFilterInvocationDefinitionSource.java;
添加
org.springside.bookstoredemo.components.acegi.domain.hbm.Resource.hbm.xml;
org.springside.bookstoredemo.components.acegi.domain.hbm.Role.hbm.xml;
org.springside.bookstoredemo.components.acegi.domain.hbm.Role.hbm.xml;
这几个文件稍稍改下class的映射地址
在dataAccessContext-hibernate.xml中合适地方添加
<property name="mappingDirectoryLocations">
<list>
<value>
classpath*:/org/springside/bookstoredemo/components/acegi/domain/hbm/
</value>
</list>
</property>
1.22添加acegiCacheManager
<bean id="acegiCacheManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="false" autowire="byName">
<property name="transactionManager" ref="transactionManager"/>
<property name="proxyTargetClass" value="true"/>
<property name="target">
<bean class="org.springside.bookstoredemo.components.acegi.cache.AcegiCacheManagerImpl">
<property name="resourceCache" ref="resourceCache"></property>
<property name="userCache" ref="userCache"></property>
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="modify*">PROPAGATION_REQUIRED</prop>
<prop key="auth*">PROPAGATION_REQUIRED</prop>
<prop key="init*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="refresh*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
1.23添加resourceCache
<!-- 自建一个resourceCache以存放resource对应的role关系 -->
<!--
resource缓存实际执行对象
-->
<bean id="resourceCache"
class="org.springside.bookstoredemo.components.acegi.resourcedetails.ResourceCache"
autowire="byName">
<property name="cache">
<bean id="resourceCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="cacheName" value="resourceCache"/>
</bean>
</property>
</bean>
1.24添加loggerListener
<!-- This bean is optional; it isn't used by any other bean as it only listens and logs -->
<bean id="loggerListener"
class="org.acegisecurity.event.authentication.LoggerListener"/>
1.25打开浏览器,输入http://localhost:8080/bookstoredemo/admin/,提示404错误。我们在webapp 目录下新建文件acegilogin.jsp,并把bookstore例子中的文件内容拷贝过来,再访问http://localhost: 8080/bookstoredemo/admin/,会打开acegilogin,jsp页面,这个页面用来供用户登录。我们先看看Acegi是如何转到这个登录页面的:首先浏览器发送http://localhost:8080/bookstoredemo/admin/请求,调用 httpSessionContextIntegrationFilter,从HttpSession中获取Authentication对象,然后把 Authentication存于一个新的ContextHolder对象。因为没有Authentication信息,Acegi转到异常处理 Filter(1.10声明),并转向到loginFormUrl。
1.26登录失败的流程
1.26.1用户不存在
用户输入错误的用户名或密码,Acegi会转到表单认证Filter(1.6中声明),转向到authenticationFailureUrl;
1.26.2用户没有权限
用户登录信息正确,Acegi通过系列数据库查询,会得出用户没有任何权限,Acegi会通过/j_acegi_security_check自动转向到/accessDenied.jsp。
1.26登录成功的流程
用户登录信息正确,Acegi通过系列数据库查询,得到用户admin对/admin/下的所有请求都有权限(可以自行分析数据库的数据)。Acegi转到表单认证,转向到defaultTargetUrl。
1.27登出流程
用户登出,如果没有声明注销处理Filter(1.5中声明),会清除登录信息,并最终重定向到/j_acegi_logout上,而如果声明了注销处理,则重定向到constructor-arg的Value上
1.28需要注意的几点
登录用户名和密码必须是j_username,j_password;/j_acegi_logout;/j_acegi_security_check.
另外,还有几个重要的拦截器和组件:
1.29
<bean id="securityInterceptor"
class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="validateConfigAttributes">
<value>false</value>
</property>
<property name="authenticationManager">
<ref local="authenticationManager" />
</property>
<property name="accessDecisionManager">
<ref local="accessDecisionManager" />
</property>
<property name="runAsManager">
<ref local="runAsManager" />
</property>
<property name="objectDefinitionSource">
<value>
sample.contact.ContactManager.create=ROLE_USER
sample.contact.ContactManager.getAllRecipients=ROLE_ADMIN
</value>
</property>
</bean>
MethodSecurityInterceptor实现了org.aopalliance.intercept.MethodInterceptor接口.在方法被调用之前,拦截器会先调用AuthenticationManager判断用户身份是否已验证,然后从 objectDefinitionSource中获取方法所应用的权限,再调用AccessDecisionManager来匹配用户权限和方法对应的权限.如果用户没有足够权限调用当前方法,则抛出AccessDeniedException是方法不能被调用.调用runAsManager,使在调用方法前动态改变authentication中获取用户权限.
1.30
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="proxyTargetClass" value="true" />
<property name="interceptorNames">
<list>
<idref local="securityInterceptor" />
</list>
</property>
<property name="beanNames">
<list>
<idref bean="userManager"/>
<idref bean="roleManager"/>
<idref bean="rescManager"/>
</list>
</property>
</bean>
1.31
BeanNameAutoProxyCreator
设置AOP代理的最简单方法就是用Spring的BeanNameAutoProxyCreator.在BeanNameAutoProxyCreator中选出你所需要的interceptor,和列出你所需要保护的Bean.
<bean id="accessDecisionManager" class="org.acegisecurity.vote.UnanimousBased">
<property name="allowIfAllAbstainDecisions">
<value>false</value>
</property>
<property name="decisionVoters">
<list>
<ref local="roleVoter" />
</list>
</property>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />
AccessDecisionManager接口有decide()和support()方法.decide()方法是进行决策是否批准通过,如果没有抛出AccessDeniedException则为允许访问资源,否则拒绝访问.support()方法是根据配置属性和受保护资源的类来判断是否需要对该资源作出决策判断.
AccessDecisionManager有三个实现类,功能各不相同:
AffirmativeBased:当至少有一个Voter投允许票才通过
UnanimousBased:没有Voter投反对票时才通过
ConsensusBased:当所有Voter都投允许票时才通过
1.32
<bean id="runAsManager" class="org.acegisecurity.runas.RunAsManagerImpl">
<property name="key">
<value>my_run_as_password</value>
</property>
</bean>
runAsmanager提供了动态替换ContextHolder中Ahthentication对象的功能
2.Book管理
按照前面所讲的Acegi登录成功以后,左边会出现 Book Manager和Shop Manager,意味着我们可以在权限内对Book和Shop进行管理了。
2.1Book和Pojo和Manager我们可以用刚开始建的,现在只要建立Book的Controller:
新建org.springside.bookstoredemo.web.admin.BookManagerAction.java,内容如下:
public class BookManagerAction extends StrutsEntityAction<Book, BookManager> {
private BookManager bookManager;
public void setBookManager(BookManager bookManager) {
this.bookManager = bookManager;
}
}
当然必要的import和package不能省。
2.2 在/WEB-INF/web.xml中的struts Action Mapping部分添加struts-congfig-admin.xml路径;
2.3在struts-config-admin.xml中添加
<form-beans>
<form-bean name="bookForm" type="org.apache.struts.validator.LazyValidatorForm"/>
</form-beans>
以及
<action path="/admin/book" name="bookForm" parameter="method" scope="request" validate="false">
<forward name="list" path="/WEB-INF/pages/admin/bookList.jsp"/>
<forward name="edit" path="/WEB-INF/pages/admin/bookForm.jsp"/>
<forward name="success" path="/admin/book.do" redirect="true"/>
</action>
2.4新建
/WEB-INF/pages/admin/bookList.jsp
/WEB-INF/pages/admin/bookForm.jsp
2.5在/WEB-INF/modules/spring-config-admin.xml中添加
<bean name="/admin/book" class="org.springside.bookstoredemo.web.admin.BookManagerAction"/>