acegi在appfuse中的应用
snowind9
2007-07-19
Acegi是Spring Framework 下最成熟的安全系统,它提供了强大灵活的企业级安全服务,如完善的认证和授权机制,Http资源访问控制,Method 调用访问控制,Access Control List (ACL) 基于对象实例的访问控制,Yale Central Authentication Service (CAS) 耶鲁单点登陆,X509 认证,当前所有流行容器的认证适配器,Channel Security频道安全管理等功能。
我着重说明一下appfuse是怎么使用acegi的: 1)web.xml: <filter> <filter-name>securityFilter</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> Acegi通过实现了Filter接口的FilterToBeanProxy提供一种特殊的使用Servlet Filter的方式,它委托Spring中的Bean -- FilterChainProxy来完成过滤功能,这好处是简化了web.xml的配置,并且充分利用了Spring IOC的优势。FilterChainProxy包含了处理认证过程的filter列表,每个filter都有各自的功能。 <filter-mapping>限定了FilterToBeanProxy的URL匹配模式,有如下的请求才会受到权限控制。 <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>/j_security_check</url-pattern> </filter-mapping> <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>/dwr/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping> <filter-mapping> <filter-name>securityFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> 这里配置了其他的xml文件我们主要看/WEB-INF/security.xml。 <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext-*.xml,/WEB-INF/security.xml</param-value> </context-param> 2)过滤的代理: 在security中我们首先看到的是过滤代理: <!-- ======================== 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,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor </value> </property> </bean> 其中CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON是 将请求转变成为小写。 PATTERN_TYPE_APACHE_ANT是按照ant的方式进行匹配模式 FilterChainProxy会按顺序来调用这些filter,使这些filter能享用Spring ioc的功能。 3)基本验证管理: 我们先看一下它是怎么进行验证管理的。 <!-- 验证管理 --> <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <!-- 从数据库中读取用户信息验证身份 --> <ref local="daoAuthenticationProvider"/> <!-- 匿名用户身份认证 --> <ref local="anonymousAuthenticationProvider"/> <!-- 已存cookie中的用户信息身份认证 --> <ref local="rememberMeAuthenticationProvider"/> </list> </property> </bean> <!-- 从数据库中读取用户信息验证身份 --> <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> <property name="userDetailsService" ref="userDao"/> <property name="userCache" ref="userCache"/> <property name="passwordEncoder" ref="passwordEncoder"/> </bean> <!-- 匿名用户身份认证 --> <bean id="anonymousAuthenticationProvider" class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> <property name="key" value="anonymous"/> </bean> <!-- 已存cookie中的用户信息身份认证 --> <bean id="rememberMeAuthenticationProvider" class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> <property name="key" value="appfuseRocks"/> </bean> 首先daoAuthenticationProvider是从数据库中读取数据进行验证。 userDetailsService属性使用了userDao进行的读取(userDao实现org.acegisecurity.userdetails.UserDetailsService接口,而user类实现org.acegisecurity.userdetails.UserDetails才能userDetailsService里使用),还可以使用其他的方式比如直接链接数据源等等。 userCache属性注明使用缓存,对于不经常改变的user放到缓存里可以大大减少数据库的负担。也可以选择不使用,如果不使用则每次都登录都从数据中查询用户信息。 passwordEncoder属性则注明密码加密的方法, 使用加密器对用户输入的明文进行加密。Acegi提供了三种加密器: PlaintextPasswordEncoder—默认,不加密,返回明文. ShaPasswordEncoder—哈希算法(SHA)加密 Md5PasswordEncoder—消息摘要(MD5)加密 <bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.ShaPasswordEncoder"/> 这里使用的哈希算法(SHA)加密。 其次,anonymousAuthenticationProvider是给与匿名用户一个匿名的权限anonymous。 最后,rememberMeAuthenticationProvider则是把登录的权限存储在cookie里,不loginout时关闭浏览器,再度打开浏览器依旧保留权限(密码修改时有特殊的限制)。 验证管理者(authenticationManager)包含三个验证提供者(daoAuthenticationProvider, anonymousAuthenticationProvider,rememberMeAuthenticationProvider),当然可以适当的简略。 怎么进行验证的呢? 3)过滤: 1.HttpSessionContextIntegrationFilter 每次request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象,在request完后, 又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi filter前使用,使之能跨越多个请求。 2. logoutFilter 该Filter负责处理退出登录后所需要的清理工作。它会把session销毁,把ContextHolder清空, 把rememberMeServices从cookies中清除掉,然后重定向到指定的退出登陆页面。 3. authenticationProcessingFilter 该Filter负责处理登陆身份验证。当接受到与filterProcessesUrl所定义相同的请求时,它会首先通过AuthenticationManager来验证用户身份。如果验证成功,则重定向到defaultTargetUrl所定义的成功登陆页面。如果验证失败,则再从rememberMeServices中获取用户身份,若再获取失败,则重定向到authenticationFailureUrl所定义登陆失败页面。 配置如下: <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationFailureUrl" value="/login.jsp?error=true"/> <property name="defaultTargetUrl" value="/"/> <property name="filterProcessesUrl" value="/j_security_check"/> <property name="rememberMeServices" ref="rememberMeServices"/> </bean> authenticationFailureUrl定义登陆失败时转向的页面 defaultTargetUrl定义登陆成功时转向的页面 filterProcessesUrl定义登陆请求的页面 rememberMeServices用于在验证成功后添加cookie信息 4.securityContextHolderAwareRequestFilter 该Filter负责通过Decorate Model(装饰模式),装饰的HttpServletRequest对象。其Wapper是ServletRequest包装类HttpServletRequestWrapper的子类(SavedRequestAwareWrapper或SecurityContextHolderAwareRequestWrapper),附上获取用户权限信息,request参数,headers, Date headers 和 cookies 的方法。 p.s. 如果把channelProcessingFilter放到securityContextHolderAwareRequestFilter前面则进行SSL的过滤。 5.rememberMeProcessingFilter 该Filter负责在用户登录后在本地机上记录用户cookies信息,免除下次再次登陆。检查AuthenticationManager 中是否已存在Authentication对象,如果不存在则会调用RememberMeServices的aotoLogin方法来从cookies中获取Authentication对象 6.anonymousProcessingFilter 该Filter负责为当不存在任何授权信息时,自动为Authentication对象添加userAttribute中定义的匿名用户权限 7.exceptionTranslationFilter 该过滤器负责处理各种异常,然后重定向到相应的页面中。 8.filterInvocationInterceptor 该过滤器会首先调用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 /clickstreams.jsp*=admin /flushCache.*=admin /passwordHint.html*=ROLE_ANONYMOUS,admin,user /reload.*=admin /signup.html*=ROLE_ANONYMOUS,admin,user /users.html*=admin /**/*.html*=admin,user </value> </property> </bean> 其中使用了accessDecisionManager,他是经过投票机制来决定是否可以访问某一资源(URL或方法)。allowIfAllAbstainDecisions为false时如果有一个或以上的decisionVoters投票通过,则授权通过。可选的决策机制有ConsensusBased和UnanimousBased。 <bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions" value="false"/> <property name="decisionVoters"> <list> <bean class="org.acegisecurity.vote.RoleVoter"> <property name="rolePrefix" value=""/> </bean> </list> </property> </bean> roleVoter表示必须是以rolePrefix设定的value开头的权限才能进行投票,如AUTH_ , ROLE_等,实现用户分组。 4)方法执行前权限验证 <!-- 类代理 --> <aop:config> <aop:advisor id="managerSecurity" advice-ref="methodSecurityInterceptor" pointcut="execution(* cn.scs.service.UserManager.*(..))"/> <aop:advisor id="personManagerSecurity" advice-ref="methodSecurityInterceptor" pointcut="execution(* cn.scs.service.PersonManager.*(..))"/> </aop:config> <!-- 在执行方法前进行拦截,检查用户权限信息 --> <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/> <property name="objectDefinitionSource"> <value> cn.scs.service.UserManager.getUsers=admin cn.scs.service.UserManager.removeUser=admin cn.scs.service.PersonManager.getPeople=ROLE_ANONYMOUS </value> </property> </bean> 可以把类MethodSecurityInterceptor注入到代理了的类中,实现在方法执行之前进行验证。 大概就是这样了。有什么问题才讨论。 |
|
snowind9
2007-08-11
再介绍一个aecgi的常用标签(一共只有4个)
<authz:authorize> 这个标签来控制页面元素的显示与否。 主要有如下属性 ifAnyGranted 只要有任意权限相当于或者(||)这个经常使用 ifAllGranted 需要有所列出的全部权限 相当于并且(&&) ifNotGranted 不能有任意权限 相当于非(!)和第一个配合使用 用这些就可以控制页面元素的显示与否了 例: <authz:authorize ifAnyGranted="ROLE_ADMIN"> <display:column property="infoId" sortable="true" href="infoContentform.html?typeId=${typeId}" media="html" paramId="infoId" paramProperty="infoId" titleKey="infoContent.infoId"/> </authz:authorize> <authz:authorize ifNotGranted="ROLE_ADMIN"> <display:column property="infoId" sortable="true" titleKey="infoContent.infoId"/> </authz:authorize> 结合了<authz:authorize>控制displayTag标签的显示。 |