Acegi 学习笔记

Acegi Security System 是一种功能强大并易于使用的替代性方案,使您不必再为 Java 企业应用程序编写大量的安全代码。虽然它专门针对使用 Spring 框架编写的应用程序,但是任何类型的 Java 应用程序都没有理由不去使用 Acegi。

本文的主要目的是希望能够说明如何在基于Spring构架的Web应用中使用Acegi,而不是详细介绍其中的每个接口、每个类。注意,即使对已经存在的Spring应用,通过下面介绍的步骤,也可以马上享受到Acegi提供的认证和授权。
 

 

Acegi Security System 使用安全过滤器来提供企业应用程序的身份验证和授权服务。该框架提供了不同类型的过滤器,可以根据应用程序的需求进行配置。您将在本文后面了解到 安全过滤器的不同类型 ;现在,只需注意可以为如下任务配置 Acegi 安全过滤器:

  1. 在访问一个安全资源之前提示用户登录。

  2. 通过检查安全标记(如密码),对用户进行身份验证。

  3. 检查经过身份验证的用户是否具有访问某个安全资源的特权。

  4. 将成功进行身份验证和授权的用户重定向到所请求的安全资源。

  5. 对不具备访问安全资源特权的用户显示 Access Denied 页面。

  6. 在服务器上记录成功进行身份验证的用户,并在用户的客户机上设置安全 cookie。使用该 cookie 执行下一次身份验证,而无需要求用户登录。

  7. 将身份验证信息存储在服务器端的会话对象中,从而安全地进行对资源的后续请求。

  8. 在服务器端对象中构建并保存安全信息的缓存,从而优化性能。

  9. 当用户退出时,删除为用户安全会话而保存的服务器端对象。

  10. 与大量后端数据存储服务(如目录服务或关系数据库)进行通信,这些服务用于存储用户的安全信息和 ECM 的访问控制策略。

正如这个列表显示的那样,Acegi 的安全过滤器允许您执行保护企业应用程序所需的几乎任何事情。

 

 

[基础工作] 
在你的Web应用的lib中添加Acegi下载包中的acegi-security.jar

 

[web.xml]

在web.xml配置

Xml代码   收藏代码
  1. <filter>  
  2.         <filter-name>Acegi Filter Chain Proxy</filter-name>  
  3.         <filter-class>  
  4.             org.acegisecurity.util.FilterToBeanProxy  
  5.         </filter-class>  
  6.         <init-param>  
  7.             <param-name>targetClass</param-name>  
  8.             <param-value>  
  9.                 org.acegisecurity.util.FilterChainProxy  
  10.             </param-value>  
  11.         </init-param>  
  12.     </filter>  

 

<filter-mapping>限定了FilterToBeanProxy URL匹配模式 ,

Xml代码   收藏代码
  1. <filter-mapping>  
  2.         <filter-name>Acegi Filter Chain Proxy</filter-name>  
  3.         <url-pattern>/*</url-pattern>  
  4.     </filter-mapping>  

 

<listener>的HttpSessionEventPublisher 用于发布HttpSessionApplicationEvents 和HttpSessionDestroyedEvent 事件给spring的applicationcontext 。

 

Xml代码   收藏代码
  1. <listener>  
  2.         <listener-class>  
  3.             org.acegisecurity.ui.session.HttpSessionEventPublisher  
  4.         </listener-class>  
  5.     </listener>  

 

[applicationContext-acegi-security.xml]

 

 applicationContext-acegi-security.xml文件配置

 

FilterChainProxy 会按顺序来调用这些filter,使这些filter能享用Spring ioc的功能 , CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义了url比较前先转为小写, PATTERN_TYPE_APACHE_ANT定义了使用Apache ant的匹配模式

Xml代码   收藏代码
  1. <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">  
  2.      <property name="filterInvocationDefinitionSource">  
  3.         <value>  
  4.         CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON  
  5.         PATTERN_TYPE_APACHE_ANT  
  6.            /**=httpSessionContextIntegrationFilter, logoutFilter, authenticationProcessingFilter,  
  7.               basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,  
  8.             switchUserProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor  
  9.         </value>  
  10.      </property>  
  11.    </bean>  

 

定义数据源为调用tomcat容器数据源 登入验证时需要获取数据源连接数据库

Xml代码   收藏代码
  1. <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">  
  2.     <property name="jndiName"><value>java:/comp/env/jdbc/test</value></property>  
  3. </bean>  

 

认证管理 ,从数据库中读取用户信息验证身份

Xml代码   收藏代码
  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>  

 

 

 

 

daoAuthenticationProvider 
  进行简单的基于数据库的身份验证。DaoAuthenticationProvider 获取数据库中的账号密码并进行匹配,若成功则在通过用户身份的同时返回一个包含授权信息的Authentication对象 ,否则身份验证失败,抛出一个AuthenticatiionException 。

Xml代码   收藏代码
  1.  <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">  
  2.       <property name="userDetailsService"><ref local="jdbcDaoImpl"/></property>  
  3.       <property name="passwordEncoder"><ref local="passwordEncoder"/></property>  
  4.       <property name="userCache"><ref local="userCache"/></property>  
  5.  </bean>  

 

jdbcDaoImpl

用于在数据中获取用户信息

Xml代码   收藏代码
  1. <bean id="jdbcDaoImpl" class="com.milesup.acegi.userdetails.jdbc.JdbcDaoImpl">  
  2.       <property name="dataSource"><ref bean="dataSource"/></property>  
  3.       <property name="rolePrefix"><value>ROLE_</value></property>  
  4.    </bean>  

 

passwordEncoder

  使用加密器对用户输入的明文进行加密。Acegi提供了三种加密器:

Xml代码   收藏代码
  1. 1 :   PlaintextPasswordEncoder—默认,不加密,返回明文.  
  2. 2 :   ShaPasswordEncoder—哈希算法(SHA)加密  
  3. 3 :   Md5PasswordEncoder—消息摘要(MD5)加密  
 

使用加密器对用户输入的明文进行加密 为MD5加密方式

Xml代码   收藏代码
  1. <bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>  

 

缓存用户和资源相对应的权限信息。每当请求一个受保护资源时,daoAuthenticationProvider 就会被调用以获取用户授权信息 。如果每次都从数据库获取的话,那代价很高,对于不常改变的用户和资源信息来说,最好是把相关授权信息缓存起来。
userCache提供了两种实现: NullUserCache 和EhCacheBasedUserCache , NullUserCache 实际上就是不进行任何缓存,EhCacheBasedUserCache 是使用Ehcache来实现缓功能。

Xml代码   收藏代码
  1. <!-- 缓存管理 -->  
  2.    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>  
  3.     <!-- 缓存用户和资源相对应的权限信息 -->  
  4.    <bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">  
  5.       <property name="cacheManager">  
  6.          <ref local="cacheManager"/>  
  7.       </property>  
  8.       <property name="cacheName">  
  9.          <value>userCache</value>  
  10.       </property>  
  11.    </bean>  
  12.      
  13.    <bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">  
  14.       <property name="cache"><ref local="userCacheBackend"/></property>  
  15.    </bean>  

 

该过滤器用来处理在系统认证授权过程中抛出的异常

Xml代码   收藏代码
  1. <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">  
  2.      <property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/></property>  
  3.   </bean>  

 

 

一个没有进行身份验证的用户试图访问受保护的资源验证是否授权  Exception Translation Filter(ETF)

Xml代码   收藏代码
  1. <bean id="authenticationProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">  
  2.       <property name="loginFormUrl"><value>/login.jsp</value></property>  
  3.       <property name="forceHttps"><value>false</value></property>  
  4.    </bean>  

 

登入验证

成功进入main.jsp页面  ,失败跳转到/login.jsp?login_error=1  ,登入url为 /j_acegi_security_check, Authentication Processing Filter(APF)

        authenticationFailureUrl 定义登陆失败时转向的页面
         defaultTargetUrl 定义登陆成功时转向的页面
         filterProcessesUrl 定义登陆请求的页面
         rememberMeServices 用于在验证成功后添加cookie信息

Xml代码   收藏代码
  1. <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">  
  2.       <property name="authenticationManager"><ref bean="authenticationManager"/></property>  
  3.       <property name="authenticationFailureUrl"><value>/login.jsp?login_error=1</value></property>  
  4.       <property name="defaultTargetUrl"><value>/main.jsp</value></property>  
  5.       <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>  
  6.      <property name="rememberMeServices"><ref local="rememberMeServices"/></property>  
  7.  </bean>  

 

 

Xml代码   收藏代码
  1. 用于处理HTTP头的认证信息,如从<span style="color: #0000ff;"><strong>Spring远程协议</strong>  
  2. </span>  
  3. (如Hessian和Burlap)或<span style="color: #0000ff;"><strong>普通的浏览器如IE,Navigator的HTTP头</strong>  
  4. </span>  
  5. 中获取用户  
  6. 信息,将他们转交给通过<span style="text-decoration: underline;">authenticationManager</span>  
  7. 属性装配的认证管理器。如果认证成功,会<span style="text-decoration: underline;">将一个Authentication对象放到会话中</span>  
  8.   
  9. ,否则,如果认证失败,会将控制<span style="text-decoration: underline;">转交给认证入口点</span>  
  10. (通过authenticationEntryPoint属性装配)  
  11. <!-- 用于处理HTTP头的认证信息 -->  
  12.    <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">  
  13.       <property name="authenticationManager"><ref local="authenticationManager"/></property>  
  14.       <property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>  
  15.    </bean>  
  16.      
  17.    <!-- 通过向浏览器发送一个HTTP401(未授权)消息,提示用户登录 -->  
  18.    <bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">  
  19.       <property name="realmName"><value>Milesup Realm</value></property>  
  20.    </bean>  
 

注销 退出验证 跳转到/login.jsp

Xml代码   收藏代码
  1. <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">  
  2.       <constructor-arg value="/login.jsp"/><constructor-arg>  
  3.          <list>  
  4.               <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>  
  5.          </list>  
  6.       </constructor-arg>  
  7.    </bean>  

 

经过投票 机制来决定是否可以访问某一资源(URL 方法 )。allowIfAllAbstainDecisions为false时如果有一个或以上的decisionVoters投票通过,则授权通过。可选的决策机制有ConsensusBased和UnanimousBased

 

roleVoter 
  必须是以rolePrefix设定的value开头的权限才能进行投票,如 ROLE_

Xml代码   收藏代码
  1. <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">  
  2.       <property name="rolePrefix">  
  3.          <value>ROLE_</value>  
  4.       </property>  
  5.    </bean>  
  6.   
  7.   <!-- 组件管理授权过程  决策管理器-->  
  8.    <bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">  
  9.       <property name="allowIfAllAbstainDecisions"><value>false</value></property>  
  10.       <property name="decisionVoters">  
  11.          <list>  
  12.             <ref bean="roleVoter"/>  
  13.          </list>  
  14.       </property>  
  15.    </bean>  
 

 

过滤器安全拦截器  是否认证,是否有权限访问受保护的资源

 在执行转向url前检查objectDefinitionSource 中设定的用户权限信息。首先,objectDefinitionSource 中定义了访问URL需要的属性信息(这里的属性信息仅仅是标志,告诉accessDecisionManager 要用哪些voter来投票)。然后,authenticationManager 掉用自己的provider来对用户的认证信息进行校验。最后,有投票者根据用户持有认证和访问url需要的属性,调用自己的voter来投票,决定是否允许访问。

Xml代码   收藏代码
  1. <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">  
  2.       <property name="authenticationManager"><ref bean="authenticationManager"/></property>  
  3.       <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>  
  4.       <property name="objectDefinitionSource">  
  5.          <value>  
  6.             PATTERN_TYPE_APACHE_ANT  
  7.             /user.jsp=ROLE_ADMIN  
  8.             /admin=ROLE_ADMIN  
  9.          </value>  
  10.       </property>  
  11.    </bean>  
 

 

[login.jsp]

 

Html代码   收藏代码
  1. <form action="/j_acegi_security_check" method="post" >  
  2.     <table>  
  3.                   <tr>  
  4.                     <td width="140" height="22" align="right">用户名</td>  
  5.                     <td width="47%" align="left"><input value="${lastUserName}" name="j_username" type="text" class="inputstyle" id="j_username"></td>  
  6.                     <td align="left" nowrap><div class="loginmeon"> </div></td>  
  7.                   </tr>  
  8.                   <tr>  
  9.                     <td width="140" height="22" align="right">密 码</td>  
  10.                     <td width="47%" align="left"><input name="j_password" type="password" class="inputstyle" id="j_password" size="21"></td>  
  11.                   </tr>  
  12.  <tr>  
  13.                     <td height="60" colspan="3" align="center" valign="middle">  
  14.                  <input type="submit" name="imageField2" value="登入"></td>  
  15.                   </tr>  
  16.                 </table>  
  17.                 </td>  
  18.               </tr>  
  19.            </table>  
  20.          </td>  
  21.       </tr>  
  22.     </table>  
  23.  </form>     
 

[JdbcDaoImpl.java]

 

Java代码   收藏代码
  1. public class JdbcDaoImpl extends org.acegisecurity.userdetails.jdbc.JdbcDaoImpl {  
  2.   
  3.     private String anonymousRoleName = "ROLE_ANONYMOUS";  
  4.   
  5.     private Log logger = LogFactory.getLog(JdbcDaoImpl.class);  
  6.   
  7.     private PreparedStatement userPstmt;  
  8.   
  9.     private PreparedStatement rolePstmt;  
  10.   
  11.     public UserDetails loadUserByUsername(String userName)  
  12.             throws UsernameNotFoundException, DataAccessException {  
  13.         UserDetails user = findUserByName(userName);  
  14.   
  15.         if (user == null) {  
  16.             throw new UsernameNotFoundException("User not found");  
  17.         }  
  18.   
  19.         return user;  
  20.     }  
  21.   
  22.     private UserDetails findUserByName(String userName) {  
  23.         Connection connection = null;  
  24.         ResultSet rsUser = null;  
  25.         ResultSet rsRole = null;  
  26.         UserDetails user = null;  
  27.         String logonName = null;  
  28.         String password = null;  
  29.         String roleName = null;  
  30.         int status = -1;  
  31.         boolean enabled = false;  
  32.         Vector roles = null;  
  33.         GrantedAuthority[] rolesArray = null;  
  34.   
  35.         try {  
  36.             connection = getDataSource().getConnection();  
  37.             userPstmt = connection  
  38.                     .prepareStatement("select * from users where user_NAME=?");  
  39.             userPstmt.setString(1, userName);  
  40.             rsUser = userPstmt.executeQuery();  
  41.             if (rsUser.next()) {  
  42.                 logonName = rsUser.getString("USER_NAME");  
  43.                 password = rsUser.getString("PASSWORD");  
  44.                 status = rsUser.getInt("STATUS");  
  45.                 if (status == 1)  
  46.                     enabled = true;  
  47.             } else {  
  48.                 return null;  
  49.             }  
  50.             rolePstmt = connection  
  51.                     .prepareStatement("SELECT ROLE.NAME Role FROM ROLE,  users_ROLES,  users WHERE ROLE.ID= user_ROLES.FK_ROLES  and users.user_NAME=?");  
  52.             rolePstmt.setString(1, userName);  
  53.             rsRole = rolePstmt.executeQuery();  
  54.             roles = new Vector();  
  55.             while (rsRole.next()) {  
  56.                 roleName = getRolePrefix() + rsRole.getString("Role");  
  57.                 roles.add(new GrantedAuthorityImpl(roleName));  
  58.             }  
  59.             rolesArray = new GrantedAuthority[roles.size() + 1];  
  60.             int index = 0;  
  61.             for (index = 0; index < roles.size(); index++)  
  62.                 rolesArray[index] = (GrantedAuthority) roles.get(index);  
  63.             rolesArray[index] = new GrantedAuthorityImpl(anonymousRoleName);  
  64.             user = new User(logonName, password, enabled, truetruetrue,  
  65.                     rolesArray);  
  66.         } catch (SQLException e) {  
  67.             logger.fatal("", e);  
  68.         } finally {  
  69.             try {  
  70.                 rsRole.close();  
  71.                 rsUser.close();  
  72.                 userPstmt.close();  
  73.                 rolePstmt.close();  
  74.                 connection.close();  
  75.             } catch (SQLException sqlx) {  
  76.                 logger.fatal("", sqlx);  
  77.             } catch (NullPointerException x) {  
  78.                 logger.fatal("", x);  
  79.             }  
  80.         }  
  81.         return user;  
  82.     }  
  83.   
  84. }  
 

猜你喜欢

转载自langzhiwang888.iteye.com/blog/1544944