acegi在appfuse中的应用

转自:http://pipboy.group.iteye.com/group/topic/2262

 

Acegi是Spring Framework 下最成熟的安全系统,它提供了强大灵活的企业级安全服务,如完善的认证和授权机制,Http资源访问控制,Method 调用访问控制,Access Control List (ACL) 基于对象实例的访问控制,Yale Central Authentication Service (CAS) 耶鲁单点登陆,X509 认证,当前所有流行容器的认证适配器,Channel Security频道安全管理等功能。
我着重说明一下appfuse是怎么使用acegi的:
1)web.xml:

  1. <filter>  
  2.   <filter-name>securityFilter</filter-name>  
  3.     <filter-class >org.acegisecurity.util.FilterToBeanProxy</filter- class >  
  4.     <init-param>  
  5.       <param-name>targetClass</param-name>  
  6.       <param-value>org.acegisecurity.util.FilterChainProxy</param-value>  
  7.      </init-param>  
  8. </filter>  
<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匹配模式,有如下的请求才会受到权限控制。
  1. <filter-mapping>  
  2.         <filter-name>securityFilter</filter-name>  
  3.         <url-pattern>/j_security_check</url-pattern>  
  4.     </filter-mapping>  
  5.     <filter-mapping>  
  6.         <filter-name>securityFilter</filter-name>  
  7.         <url-pattern>/dwr/*</url-pattern>  
  8.     </filter-mapping>  
  9.     <filter-mapping>  
  10.         <filter-name>securityFilter</filter-name>  
  11.         <url-pattern>*.html</url-pattern>  
  12.     </filter-mapping>  
  13.     <filter-mapping>  
  14.         <filter-name>securityFilter</filter-name>  
  15.         <url-pattern>*.jsp</url-pattern>  
  16.     </filter-mapping>    
<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。
  1. <context-param>  
  2.   <param-name>contextConfigLocation</param-name>  
  3.   <param-value>/WEB-INF/applicationContext-*.xml,/WEB-INF/security.xml</param-value>  
  4. </context-param>  
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/applicationContext-*.xml,/WEB-INF/security.xml</param-value>
</context-param>



2)过滤的代理:
在security中我们首先看到的是过滤代理:

  1. <!-- ======================== FILTER CHAIN ======================= -->  
  2. <bean id="filterChainProxy"   class = "org.acegisecurity.util.FilterChainProxy" >  
  3.   <property name="filterInvocationDefinitionSource" >  
  4.     <value>  
  5.       CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON  
  6.       PATTERN_TYPE_APACHE_ANT  
  7.       /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor  
  8.     </value>   
  9.   </property>  
  10. </bean>  
<!-- ======================== 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)基本验证管理:
我们先看一下它是怎么进行验证管理的。

  1. <!-- 验证管理 -->  
  2. <bean id="authenticationManager"   class = "org.acegisecurity.providers.ProviderManager" >  
  3.   <property name="providers" >  
  4.     <list>  
  5.       <!-- 从数据库中读取用户信息验证身份 -->  
  6.       <ref local="daoAuthenticationProvider" />  
  7.       <!-- 匿名用户身份认证 -->  
  8.       <ref local="anonymousAuthenticationProvider" />  
  9.       <!-- 已存cookie中的用户信息身份认证 -->  
  10.       <ref local="rememberMeAuthenticationProvider" />  
  11.     </list>  
  12.   </property>  
  13. </bean>  
  14. <!-- 从数据库中读取用户信息验证身份 -->  
  15. <bean id="daoAuthenticationProvider"   class = "org.acegisecurity.providers.dao.DaoAuthenticationProvider" >  
  16.          <property name="userDetailsService"  ref= "userDao" />  
  17.          <property name="userCache"  ref= "userCache" />  
  18.          <property name="passwordEncoder"  ref= "passwordEncoder" />  
  19. </bean>  
  20. <!-- 匿名用户身份认证 -->  
  21. <bean id="anonymousAuthenticationProvider"   class = "org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider" >  
  22.         <property name="key"  value= "anonymous" />  
  23. </bean>  
  24. <!-- 已存cookie中的用户信息身份认证 -->  
  25. <bean id="rememberMeAuthenticationProvider"   class = "org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider" >  
  26.         <property name="key"  value= "appfuseRocks" />  
  27. </bean>  
<!-- 验证管理 -->
<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)加密

  1. <bean id= "passwordEncoder"   class = "org.acegisecurity.providers.encoding.ShaPasswordEncoder" />  
<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所定义登 陆失败页面。
配置如下:
  1.   <property name="authenticationManager"  ref= "authenticationManager" />  
  2.   <property name="authenticationFailureUrl"  value= "/login.jsp?error=true" />  
  3.   <property name="defaultTargetUrl"  value= "/" />  
  4.   <property name="filterProcessesUrl"  value= "/j_security_check" />  
  5.   <property name="rememberMeServices"  ref= "rememberMeServices" />  
  6. </bean>  
<bean id= "authenticationProcessingFilter"   class = "org.acegisecurity.ui.webapp.AuthenticationProcessingFilter" >  
<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资源。

  1. <bean id= "filterInvocationInterceptor"   class = "org.acegisecurity.intercept.web.FilterSecurityInterceptor" >  
  2.   <property name="authenticationManager"  ref= "authenticationManager" />  
  3.   <property name="accessDecisionManager"  ref= "accessDecisionManager" />  
  4.   <property name="objectDefinitionSource" >  
  5.     <value>  
  6.                 PATTERN_TYPE_APACHE_ANT  
  7.                 /clickstreams.jsp*=admin  
  8.                 /flushCache.*=admin  
  9.                 /passwordHint.html*=ROLE_ANONYMOUS,admin,user  
  10.                 /reload.*=admin  
  11.                 /signup.html*=ROLE_ANONYMOUS,admin,user  
  12.                 /users.html*=admin  
  13.                 /**/ *.html*=admin,user  
  14.     </value>  
  15.   </property>  
  16. </bean>  
<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。
  1.     <property name="allowIfAllAbstainDecisions"  value= "false" />  
  2.     <property name="decisionVoters" >  
  3.         <list>  
  4.             <bean class = "org.acegisecurity.vote.RoleVoter" >  
  5.                 <property name="rolePrefix"  value= "" />  
  6.             </bean>  
  7.         </list>  
  8.     </property>  
  9. </bean>  
<bean id= "accessDecisionManager"   class = "org.acegisecurity.vote.AffirmativeBased" >  
<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)方法执行前权限验证

  1. <!-- 类代理 -->  
  2. <aop:config>  
  3.   <aop:advisor id="managerSecurity"  advice-ref= "methodSecurityInterceptor"  pointcut= "execution(* cn.scs.service.UserManager.*(..))" />  
  4.   <aop:advisor id="personManagerSecurity"  advice-ref= "methodSecurityInterceptor"  pointcut= "execution(* cn.scs.service.PersonManager.*(..))" />  
  5. </aop:config>  
  6. <!-- 在执行方法前进行拦截,检查用户权限信息 -->  
  7. <bean id="methodSecurityInterceptor"   class = "org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor" >  
  8.     <property name="authenticationManager"  ref= "authenticationManager" />  
  9.     <property name="accessDecisionManager"  ref= "accessDecisionManager" />  
  10.     <property name="objectDefinitionSource" >  
  11.              <value>  
  12.                  cn.scs.service.UserManager.getUsers=admin  
  13.                  cn.scs.service.UserManager.removeUser=admin  
  14.                  cn.scs.service.PersonManager.getPeople=ROLE_ANONYMOUS  
  15.              </value>  
  16.     </property>  
  17.     </bean>  
<!-- 类代理 -->
<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个)
Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://pipboy.group.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%3Cauthz%3Aauthorize%3E%0A" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. <authz:authorize>  
<authz:authorize>

这个标签来控制页面元素的显示与否。
主要有如下属性
ifAnyGranted 只要有任意权限相当于或者(||)这个经常使用
ifAllGranted 需要有所列出的全部权限 相当于并且(&&)
ifNotGranted 不能有任意权限 相当于非(!)和第一个配合使用
用这些就可以控制页面元素的显示与否了
例:
Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://pipboy.group.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%3Cauthz%3Aauthorize%20ifAnyGranted%3D%22ROLE_ADMIN%22%3E%0A%20%20%20%20%3Cdisplay%3Acolumn%20property%3D%22infoId%22%20sortable%3D%22true%22%20href%3D%22infoContentform.html%3FtypeId%3D%24%7BtypeId%7D%22%20media%3D%22html%22%0A%20%20%20%20%20%20%20%20paramId%3D%22infoId%22%20paramProperty%3D%22infoId%22%20titleKey%3D%22infoContent.infoId%22%2F%3E%0A%3C%2Fauthz%3Aauthorize%3E%0A%3Cauthz%3Aauthorize%20ifNotGranted%3D%22ROLE_ADMIN%22%3E%0A%20%20%20%20%3Cdisplay%3Acolumn%20property%3D%22infoId%22%20sortable%3D%22true%22%20titleKey%3D%22infoContent.infoId%22%2F%3E%0A%3C%2Fauthz%3Aauthorize%3E%0A" quality="high" allowscriptaccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
  1. <authz:authorize ifAnyGranted= "ROLE_ADMIN" >  
  2.     <display:column property="infoId"  sortable= "true"  href= "infoContentform.html?typeId=${typeId}"  media= "html"   
  3.         paramId="infoId"  paramProperty= "infoId"  titleKey= "infoContent.infoId" />  
  4. </authz:authorize>  
  5. <authz:authorize ifNotGranted="ROLE_ADMIN" >  
  6.     <display:column property="infoId"  sortable= "true"  titleKey= "infoContent.infoId" />  
  7. </authz:authorize>  
<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标签的显示。

你可能感兴趣的:(应用服务器,bean,企业应用,Appfuse,Acegi)