淺談Acegi配置

xuniji123發表於2007-03-04
Acegi是基於Spring的一個開源的安全認證框架,現在的最新版本是1.04。Acegi的特點就是有很多的過濾器:不過我們也用不到這麼多的過濾器,只是可以把它們看作為一個個的模組,在用的時候加上自己用的著的即可,由於認證的流程的方面比較複雜導致它的配置很複雜,如果能摸清它的工作原理還是不太難.下面用比較順著人思維的流程過一遍
這裡只列出常用的過濾器和攔載器
1. 過濾器:HttpSessionContextIntegrationFilter,authenticationProcessingFilter,BasicProcessingFilter,RememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter
2. 攔截器:filterSecurityInterceptor(其實它是過濾器,不過把它放在這裡更能說明它的功能),methodSecurityInterceptor
看著上面的用紅色標出的過濾器是用來認證(表單和HTTP基本認證,當然還有別的不過這兩個比較長用)它們是資源訪問的入口.其它的過濾器是用來輔助的:HttpSessionContextIntegrationFilter是用來把認證資訊記錄到Session中的RememberMeProcessingFilter是以cookie的形式來儲存認證資訊的. anonymousProcessingFilter是在匿名的時候(這時候是沒有認證資訊的)給這個使用者分配一個匿名的認證資訊,exceptionTranslationFilter總結一下異常並處理.在實際中選擇適合程式的即可.
上面只是資源訪問的入口,真正保護資源的是這兩個攔截器:filterSecurityInterceptor,攔截URL的類(它是個過濾器)
metohdSecurityInterceptor,攔截類中方法的呼叫,它們為什麼要攔截呢?就是想在訪問或呼叫這些方法之前來判斷一下使用者是否有訪問或呼叫的許可權,有就透過,沒有就踢出.
除此之外,Acegi專門做了兩個管理器(實際上就是兩個類,為什麼會用做這兩個管理器,因為認證和授權都有一些的操作,這就需要專門做兩個管理器了):authenticationManager(class= org.acegisecurity.providers.ProviderManager),授權管理器accessDecisionManager(class=org.acegisecurity.vote.AffirmativeBased)
說白了一個用於認證使用者,一個是用於許可權的授於的
先來說認證使用者,認證管理器有什麼東西呢?只內建了一些提供者:這些提供者呢又是什麼呢,他們是提供使用者的驗證身份資訊的,比如從資料庫或配置檔案裡讀出使用者名稱和密碼,在使用者的cookie裡讀出身份資訊(rememberMeProcessingFilter用到的[前面講了的,有印象吧]),或在Session裡讀出身份驗證資訊(HttpSessionContextIntegrationFilter起作用的),這裡我們只說一下從資料庫或配置檔案裡讀出使用者名稱密碼來裝配驗證資訊的,其它的配置類似可以找一下對應api在Spring裡配置即可,daoAuthenticationProvider是資料庫的提供者class=org.acegisecurity.providers.dao.DaoAuthenticationProvider,而它提供的服務呢又有幾種,資料庫和配置檔案(這是Acegi的兩個預設的實現)當然也可以自己實現(實現userDetailsService介面就行)
程式碼
  1. <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
  2. <property name="providers">
  3. <list>
  4. <ref local="daoAuthenticationProvider"/>
  5. list>
  6. property>
  7. bean>
  8. <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
  9. <!-- --&gt<!-- 這裡有兩種選擇 --&gt
  10. <property name="userDetailsService"><ref local="jdbcDaoImpl"/>property>
  11. bean>

如果使用者名稱和密碼在配置檔案裡可以用InMemoryDaoImpl,class=org.acegisecurity.userdetails.memory.InMemoryDaoImpl,在這個類的userMap裡配置即可:javafish=java,ROLE_USER,配置了一個使用者名稱為javafish,密碼為java,使用者組為ROLE_USER的使用者,不過最常用的還是資料庫的JDBC實現(兩個二選一)org.acegisecurity.userdetails.jdbc.JdbcDaoImpl裡面需要usersByUsernameQuery和authoritiesByUsernameQuery還有資料來源dataSource(有人問為什麼呢,userByUsernameQuery是用來透過使用者名稱來查密碼的,authoritiesByUsernameQuery是用來透過使用者名稱來查許可權的,查詢資料庫肯定的用資料來源吧這個裡是用的SpringFrameWork的DataSource)它們查詢的sql語句是有講究的,就是查密碼的時候查三個第一個是username,第二個是password,第三個是是否可用,查許可權的時候查兩個:username和authorities(具體看例子)
程式碼
  1. <bean id="InMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
  2. <property name="userMap">
  3. <value>
  4. javajavafish=java,ROLE_USER
  5. value>
  6. property>
  7. bean>
  8. <bean id="jdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
  9. <property name="usersByUsernameQuery">
  10. <value>select username,password,enabled from users where username=?value>
  11. property>
  12. <property name="authoritiesByUsernameQuery">
  13. <value>select username,authority from authorities where username=?value>
  14. property>
  15. <property name="dataSource">
  16. <ref local="dataSource"/>
  17. property>
  18. bean>
  19. <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  20. <property name="driverClassName">
  21. <value>com.mysql.jdbc.Drivervalue>
  22. property>
  23. <property name="url">
  24. <value>jdbc:mysql://localhost:3306/testvalue>
  25. property>
  26. <property name="username">
  27. <value>rootvalue>
  28. property>
  29. <property name="password">
  30. <value>javafishvalue>
  31. property>
  32. bean>

下面說一下授權,授權管理器又有什麼東西呢?accessDecisionManager,Acegi把授權方面弄的比較的形象化,把某個URL或方法是否可以被訪問按投票的形式來決定,

Acegi提出來了幾種方案:
1. 如果有一個贊成就同意(具體的說就是隻要你在那個URL對應的幾個使用者組中的一個就讓你訪問)
2. 如果都贊成就同意(具本的說就是那個URL對應的幾個使用者組裡都有你,你才能訪問)
3. 如果都不反對就同意(這個在下面講投票者的時候再說)

程式碼
  1. <bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
  2. <property name="allowIfAllAbstainDecisions"><!-- 是否讓全部棄權的透過 --&gt
  3. <value>falsevalue>
  4. property>
  5. <property name="decisionVoters"><!-- 投票者們 --&gt
  6. <ref bean="roleVoter"/>
  7. property>
  8. bean>

而投票者呢:Acegi自己實現了一個投票者的類RoleVoter:
現在我用第一種方案,RoleVoter只是在URL對應的使用者組裡有ROLE_為字首的才進行投票,否則的話棄權.(我們也可以在配置RoleVoter的時候把ROLE_配置成為別的字首如JAVA_),分別對URL對應的每個使用者組投票,如果使用者在這個使用者組裡就投贊成,不在投反對(在使用者組的字首是ROLE_的前提下)這樣就不難體會第三種方案的用途了吧
程式碼
  1. <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
  2. <property name="rolePrefix">
  3. <value>ROLE_value><!-- 可以改成別的 --&gt
  4. property>
  5. bean>

這樣認證管理器和授權管理器就ok了,別的無論是過濾器還是攔截器都會用到它們兩個,因為它們都要驗證而這兩個就是憑證.
那麼那兩個訪問過濾器呢,先說authenticationProcessingFilter是用於表單登陸的
程式碼
  1. <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
  2. <property name="authenticationManager"><ref bean="authenticationManager"/>property>
  3. <property name="authenticationFailureUrl"><value>/failure.htmlvalue>property><!--登陸失敗轉向的頁面 --&gt
  4. <property name="defaultTargetUrl"><value>/ok.htmlvalue>property><!-- 登陸成功轉向的頁面 --&gt
  5. <property name="filterProcessesUrl"><value>/checkvalue>property><!-- 要驗證的地址 --&gt
  6. bean>

這樣的話加上上面配置的認證管理器就已經可以處理登陸了(注意的是它沒有用到授權管理器,因為它只是個訪問入口還沒有許可權的授予)
再說一下HTTP基本認證:它比上面的略複雜一點
需要配置一個
程式碼
  1. <bean id="BasicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
  2. <property name="realmName"><value>javafishvalue>property><!-- 基本認證對話方塊上顯示的字 --&gt
  3. bean>
  4. 然後
  5. <bean id="BasicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
  6. <property name="authenticationManager">
  7. <ref bean="authenticationManager"/>
  8. property>
  9. <property name="authenticationEntryPoint">
  10. <ref bean="BasicProcessingFilterEntryPoint"/>
  11. property>
  12. bean>

即可.
不過在HTTP基本認證裡需要注意的地方是:好多人配置好了怎麼看不到效果啊,一開始我也是很鬱悶,看了BasicProcessingFilter的原始碼:
String header = httpRequest.getHeader("Authorization");//我們一般進入網頁測試的時候這裡的header始終是null的
程式碼
  1. if (logger.isDebugEnabled()) {
  2. logger.debug("Authorization header: " + header);
  3. }
  4. if ((header != null) && header.startsWith("Basic ")) {//從這裡可以看到一般的登陸基本認證是不起作用的
  5. .................

只有在伺服器上配置哪個目錄在訪問的時候用HTTP基本認證,它才會起作用(一開始還以為是Acegi的BUG呢)
下面說一下真正對URL資源的保護了filterSecurityInterceptor它的本質是個過濾器,有了前面*管理器的基礎了這就很容易了:
程式碼
  1. <bean id="filterSecurityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
  2. <property name="authenticationManager">
  3. <ref local="authenticationManager"/>
  4. property>
  5. <property name="accessDecisionManager">
  6. <ref local="accessDecisionManager"/>
  7. property>
  8. <property name="objectDefinitionSource"><!-- 把URL和可訪問的使用者組對應起來 --&gt
  9. <value>
  10. CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<!-- 把URL全部轉化為小寫 --&gt
  11. PATTERN_TYPE_APACHE_ANT<!-- 以ANT的形式來配置路徑 --&gt
  12. /ok.html=ROLE_USER
  13. value>
  14. property>
  15. bean>

光這樣配置還是不夠的,因為當授權失敗的時候會丟擲異常的,我們應該配置一個異常過濾器來捕獲它,exceptionTranslationFilter它是用來捕獲異常的,看一下配置吧:
程式碼
  1. <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
  2. <property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/>property>
  3. <property name="accessDeniedHandler">
  4. <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
  5. <property name="errorPage" value="/failure.html"/><!-- 發生異常轉向的網頁 --&gt
  6. bean>
  7. property>
  8. bean>
  9. <bean id="authenticationProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
  10. <property name="loginFormUrl"><value>/Login.htmlvalue>property><!-- 得到表單的資訊 --&gt
  11. <property name="forceHttps"><value>falsevalue>property><!-- 不用https --&gt
  12. bean>

這樣就OK了
最後說一下對類中方法的保護:
首先寫一個類並在spring中配置好:
程式碼
  1. package org.li.acegi;
  2. public class TestAcegi
  3. {
  4. public void Role()
  5. {
  6. System.out.println("javafish");
  7. }
  8. }
  9. "testAcegi" class="org.li.acegi.TestAcegi"/>

然看寫個servlet訪問一下它
程式碼
  1. package org.li.servlet;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import org.li.acegi.TestAcegi;
  9. import org.springframework.context.ApplicationContext;
  10. import org.springframework.web.context.support.WebApplicationContextUtils;
  11. public class TestServlet extends HttpServlet
  12. {
  13. private static final long serialVersionUID = -5610016980827214773L;
  14. public void doGet(HttpServletRequest request, HttpServletResponse response)
  15. throws ServletException, IOException
  16. {
  17. response.setContentType("text/html;charset=GBK");
  18. PrintWriter out = response.getWriter();
  19. ApplicationContext ctx =
  20. WebApplicationContextUtils.getRequiredWebApplicationContext(request.getSession().getServletContext());
  21. TestAcegi test = (TestAcegi)ctx.getBean("testAcegi");
  22. test.Role();//訪問TestAcegi類的Role方法
  23. out.println("呼叫成功");
  24. }
  25. public void doPost(HttpServletRequest request, HttpServletResponse response)
  26. throws ServletException, IOException
  27. {
  28. doGet(request,response);
  29. }
  30. }

準備工作做好了,開始配置Acegi
先在Spring裡給Acegi做個代理:
程式碼
  1. <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  2. <property name="beanNames">
  3. <list>
  4. <value>testAcegivalue><!-- 要代理的Bean的id --&gt
  5. list>
  6. property>
  7. <property name="interceptorNames">
  8. <list>
  9. <value>methodSecurityInterceptorvalue><!-- 代理為... --&gt
  10. list>
  11. property>
  12. bean>

裡面的methodSecurityInterceptor呢配置為:
程式碼
  1. <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
  2. <property name="authenticationManager">
  3. <ref bean="authenticationManager"/>
  4. property>
  5. <property name="accessDecisionManager">
  6. <ref bean="accessDecisionManager"/>
  7. property>
  8. <property name="objectDefinitionSource"><!-- 對代理的類的方法開始配置許可權 --&gt
  9. <value>org.li.acegi.TestAcegi.Role=ROLE_USERvalue>
  10. property>
  11. bean>

這樣當直接訪問的時候會發現不可訪問,控制元件臺也不輸出”javafish”,當輸入正確的使用者名稱和密碼之後便可以訪問.
這樣它就對類的方法呼叫起了保護的作用,這一點可以把Acegi應用到DWR上效果是很理想的.
對於Acegi有很多的過濾器不用全寫在web.xml裡,acegi提供了一個特殊的過濾器我們可以寫成這樣,在Web.xml裡:
程式碼
  1. <filter>
  2. <filter-name>Acegifilter-name>
  3. <filter-class>org.acegisecurity.util.FilterToBeanProxyfilter-class<

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8271432/viewspace-902547/,如需轉載,請註明出處,否則將追究法律責任。

淺談Acegi配置
請登入後發表評論 登入
全部評論