Acegi 学习笔记

系统 1856 0

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,  true true true ,  
  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. }  
 

Acegi 学习笔记


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论