limq

rainman
随笔 - 19, 文章 - 2, 评论 - 115, 引用 - 1
数据加载中……

Acegi+hibernate 动态实现基于角色的权限管理

 

    最近在做项目遇到了权限管理,用户要求可以自己建立不同的角色对系统的资源进行控制, 不同的用户有不同的角色,又恰恰框架中用到了struts+spring+hibernate,要求在web层调用 业务逻辑层 时不考虑权限,web层可以控制用户的显示界面,逻辑层处理用户权限问题。
想来想去好像只有spring 的aop 可以做到,在调用到 接口 中的方法时,首先检查用户的权限,如果检查通过则继续执行,否则抛出异常。但是新的问题又出现了,如何在逻辑层上来得到当前用户的id,以致用户的 角色,总不能每次都要从web中传来一个 httprequest,或者 session 这类的吧。在网上看了很多资料,发现了acegi,恰好解决了以上的难题,具体的实现原理这里就不多说了,网上有很多相关资料。
说正题,首先来看看acegi 的官方 example ,我下载的是acegi-security-1.0.0-RC1,解压缩后可以看到acegi-security-sample-contacts-filter.war,打开配置文件有这样几句

 1   <bean id="contactManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> 
 2       <property name="authenticationManager"><ref bean="authenticationManager"/></property> 
 3       <property name="accessDecisionManager"><ref local="businessAccessDecisionManager"/></property> 
 4       <property name="afterInvocationManager"><ref local="afterInvocationManager"/></property> 
 5       <property name="objectDefinitionSource"> 
 6          <value> 
 7             sample.contact.ContactManager.create=ROLE_USER 
 8             sample.contact.ContactManager.getAllRecipients=ROLE_USER 
 9             sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ 
10             sample.contact.ContactManager.getById=ROLE_USER,AFTER_ACL_READ 
11             sample.contact.ContactManager.delete=ACL_CONTACT_DELETE 
12             sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN 
13             sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN 
14          </value> 
15       </property> 
16    </bean> 
17 

可以看到它是通过读配置文件来判断执行某个方法所需要的角色的,再看这几句
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> 
      
<property name="authenticationManager"><ref bean="authenticationManager"/></property> 
      
<property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property> 
      
<property name="objectDefinitionSource"> 
         
<value> 
                            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 
                            PATTERN_TYPE_APACHE_ANT 
                            /index.jsp=ROLE_ANONYMOUS,ROLE_USER 
                            /hello.htm=ROLE_ANONYMOUS,ROLE_USER 
                            /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER 
                            /switchuser.jsp=ROLE_SUPERVISOR 
                            /j_acegi_switch_user=ROLE_SUPERVISOR 
                            /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER 
                                /**=ROLE_USER 
         
</value> 
      
</property> 
   
</bean> 
同样是将页面的访问权限写死在配置文件中,再来看看它的tag是如何处理的
<auth:authorize ifAnyGranted="ROLE_DELETE"> 
          
<href="">删除</a> 
</auth:authorize> 
可见它是要求我们对链接或者其他资源的保护时提供 用户角色,可是既然角色是用户自己添加的我们又如何来写死在这里呢?
还有就是它对用户验证默认使用的是jdbc,即 JdbcDaoImpl
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
                
<property name="dataSource"><ref local="dataSource"/></property> 
        
</bean> 
而我们希望基于Hibernate的Dao来实现。
可见仅仅使用现有的acegi 是 无法满足我们项目开发的需求的。
解决方法:

1: 开发基于数据库的保护资源。

看过acegi的源代码就会知道,对保护资源的定义是通过实现ObjectDefinitionSource这个接口来实现的,而且acegi为我们提供了默认实现的抽象类
public abstract class AbstractMethodDefinitionSource 
    
implements MethodDefinitionSource 
    
//~ Static fields/initializers ============================================= 

    
private static final Log logger = LogFactory.getLog(AbstractMethodDefinitionSource.class); 

    
//~ Methods ================================================================ 

    
public ConfigAttributeDefinition getAttributes(Object object) 
        
throws IllegalArgumentException 
        Assert.notNull(object, 
"Object cannot be null"); 

        
if (object instanceof MethodInvocation) 
            
return this.lookupAttributes(((MethodInvocation) object).getMethod()); 
        }
 

        
if (object instanceof JoinPoint) 
            JoinPoint jp 
= (JoinPoint) object; 
            Class targetClazz 
= jp.getTarget().getClass(); 
            String targetMethodName 
= jp.getStaticPart().getSignature().getName(); 
            Class[] types 
= ((CodeSignature) jp.getStaticPart().getSignature()) 
                    .getParameterTypes(); 

            
if (logger.isDebugEnabled()) 
                logger.debug(
"Target Class: " + targetClazz); 
                logger.debug(
"Target Method Name: " + targetMethodName); 

                
for (int i = 0; i < types.length; i++
                    
if (logger.isDebugEnabled()) 
                        logger.debug(
"Target Method Arg #" + i + "" 
                                
+ types[i]); 
                    }
 
                }
 
            }
 

            
try 
                
return this.lookupAttributes(targetClazz.getMethod(targetMethodName, types)); 
            }
 catch (NoSuchMethodException nsme) 
                
throw new IllegalArgumentException("Could not obtain target method from JoinPoint: " + jp); 
            }
 
        }
 

        
throw new IllegalArgumentException("Object must be a MethodInvocation or JoinPoint"); 
    }
 

    
public boolean supports(Class clazz) 
        
return (MethodInvocation.class.isAssignableFrom(clazz) 
        
|| JoinPoint.class.isAssignableFrom(clazz)); 
    }
 


    
protected abstract ConfigAttributeDefinition lookupAttributes(Method method); 
}
 

我们要做的就是实现它的
protected abstract ConfigAttributeDefinition lookupAttributes(Method method);方法,
以下是我的实现方法,大致思路是这样,通过由抽象类传来的Method 对象得到
调用这个方法的 包名,类名,方法名 也就是secureObjectName, 查询数据库并将结果映射为Function 也就是secureObject ,由于Function 与 Role 的多对多关系 可以得到 Function所对应的 Roles ,在将role 包装成GrantedAuthority (也就是acegi中的角色)。其中由于频繁的对数据库的查询 所以使用Ehcache 来作为缓存。
  1 package sample.auth; 
  2 
  3 import java.lang.reflect.Method; 
  4 import java.util.ArrayList; 
  5 import java.util.Arrays; 
  6 import java.util.Collection; 
  7 import java.util.Iterator; 
  8 import java.util.List; 
  9 import java.util.Set; 
 10 
 11 import org.acegisecurity.ConfigAttributeDefinition; 
 12 import org.acegisecurity.ConfigAttributeEditor; 
 13 import org.acegisecurity.GrantedAuthority; 
 14 import org.acegisecurity.GrantedAuthorityImpl; 
 15 import org.acegisecurity.intercept.method.AbstractMethodDefinitionSource; 
 16 import org.springframework.util.Assert; 
 17 
 18 import sample.auth.cache.AuthorityBasedFunctionCache; 
 19 import sample.auth.cache.info.FunctionByNameCache; 
 20 import sample.dao.IBaseDao; 
 21 import sample.mappings.function.Function; 
 22 import sample.mappings.role.Role; 
 23 
 24 public class DatabaseDrivenMethodDefinitionSourcew extends 
 25                 AbstractMethodDefinitionSource { 
 26         
 27         // baseDao 提供通过HIbenate对数据库操作的实现 
 28         private IBaseDao baseDao; 
 29         // AuthorityBasedFunctionCache 通过Function 查 Role 时缓存 
 30         private AuthorityBasedFunctionCache cache; 
 31         // FunctionByNameCache 由反射到的方法名查找 数据库对应的Function 时的缓存 
 32         private FunctionByNameCache functionCache; 
 33 
 34         public FunctionByNameCache getFunctionCache() { 
 35                 return functionCache; 
 36         } 
 37 
 38         public void setFunctionCache(FunctionByNameCache functionCache) { 
 39                 this.functionCache = functionCache; 
 40         } 
 41 
 42         protected ConfigAttributeDefinition lookupAttributes(Method mi) { 
 43         
 44                 Assert.notNull(mi,"lookupAttrubutes in the DatabaseDrivenMethodDefinitionSourcew is null"); 
 45                 String secureObjectName=mi.getDeclaringClass().getName() +"."+ mi.getName(); 
 46                 //Function 为数据库中保护资源的映射 
 47                 Function secureObject=functionCache.getFunctionByCache(secureObjectName); 
 48 
 49                 if(secureObject==null)//if secure object not exist in database 
 50                 { 
 51                         secureObject=(Function)baseDao.loadByKey(Function.class"protectfunction", secureObjectName); 
 52                         functionCache.putFunctionInCache(secureObject); 
 53                 } 
 54                     
 55                 if(secureObject==null
 56                         Assert.notNull(secureObject,"secureObject(Function) not found in db"); 
 57                 //retrieving roles associated with this secure object 
 58                 
 59                 Collection roles = null
 60                 GrantedAuthority[] grantedAuthoritys = cache.getAuthorityFromCache(secureObject.getName()); 
 61                 // 如果是第一次 cache 为空 
 62                 if(grantedAuthoritys == null){ 
 63                         
 64                         Set rolesSet = secureObject.getRoles(); 
 65                         Iterator it = rolesSet.iterator(); 
 66                         List list = new ArrayList(); 
 67                         while(it.hasNext()){ 
 68                                 
 69                                 Role role = (Role)it.next(); 
 70                                 GrantedAuthority g = new  GrantedAuthorityImpl(role.getName()); 
 71                                 list.add(g);        
 72                         } 
 73                         grantedAuthoritys = (GrantedAuthority[])list.toArray(new GrantedAuthority[0]); 
 74                         cache.putAuthorityInCache(secureObject.getName(),grantedAuthoritys); 
 75                         roles = Arrays.asList(grantedAuthoritys); 
 76                 }else
 77                         
 78                         roles = Arrays.asList(grantedAuthoritys); 
 79                 } 
 80                 
 81                 if(!roles.isEmpty()){ 
 82                         ConfigAttributeEditor configAttrEditor=new ConfigAttributeEditor(); 
 83                         StringBuffer rolesStr=new StringBuffer(); 
 84                         for(Iterator it = roles.iterator();it.hasNext();){ 
 85                                 GrantedAuthority role=(GrantedAuthority)it.next(); 
 86                                 rolesStr.append(role.getAuthority()).append(","); 
 87                         } 
 88 
 89                         configAttrEditor.setAsText( rolesStr.toString().substring(0,rolesStr.length()-1) ); 
 90                         ConfigAttributeDefinition configAttrDef=(ConfigAttributeDefinition)configAttrEditor.getValue(); 
 91                         return configAttrDef; 
 92                 } 
 93 
 94                 Assert.notEmpty(roles,"collection of roles is null or empty"); 
 95                 return null
 96                 
 97 
 98         } 
 99 
100         public Iterator getConfigAttributeDefinitions() { 
101                 
102                 return null
103         } 
104 
105 
106         public IBaseDao getBaseDao() { 
107                 return baseDao; 
108         } 
109 
110 
111         public void setBaseDao(IBaseDao baseDao) { 
112                 this.baseDao = baseDao; 
113         } 
114 
115         public AuthorityBasedFunctionCache getCache() { 
116                 return cache; 
117         } 
118 
119         public void setCache(AuthorityBasedFunctionCache cache) { 
120                 this.cache = cache; 
121         } 
122 
123 
124 

2:定义 基于方法的 自定义标志

通过以上的分析 , 要想使用acegi 做页面的显示控制仅仅靠角色(Role)是不行的,因为用户可能随时定义出新的角色,所以只能 基于方法(Function)的控制。可是acegi 只是提供了基于 角色的 接口GrantedAuthority ,怎么办?  ,如法炮制。 首先定义出我们自己的GrantedFunction,实现也雷同 GrantedAuthorityImpl

 1 package sample.auth; 
 2 
 3 import java.io.Serializable; 
 4 public class GrantedFunctionImpl implements GrantedFunction , Serializable{ 
 5 
 6     private String function; 
 7 
 8     //~ Constructors =========================================================== 
 9 
10     public GrantedFunctionImpl(String function) { 
11         super(); 
12         this.function = function; 
13     } 
14 
15     protected GrantedFunctionImpl() { 
16         throw new IllegalArgumentException("Cannot use default constructor"); 
17     } 
18 
19     //~ Methods ================================================================ 
20 
21     public String getFunction() { 
22         return this.function; 
23     } 
24 
25     public boolean equals(Object obj) { 
26         if (obj instanceof String) { 
27             return obj.equals(this.function); 
28         } 
29 
30         if (obj instanceof GrantedFunction) { 
31             GrantedFunction attr = (GrantedFunction) obj; 
32 
33             return this.function.equals(attr.getFunction()); 
34         } 
35 
36         return false
37     } 
38 
39     public int hashCode() { 
40         return this.function.hashCode(); 
41     } 
42 
43     public String toString() { 
44         return this.function; 
45     } 
46 
47 
48 

以下是我的标志实现,大致思路是 根据 页面 的传来的 方法名(即 FunctionName)查询出对应的Functions,并且包装成grantedFunctions ,然后根据用户的角色查询出用户对应的Functions ,再取这两个集合的交集,最后再根据这个集合是否为空判断是否显示标志体的内容。
  1 package sample.auth; 
  2 import java.util.Arrays; 
  3 import java.util.Collection; 
  4 import java.util.Collections; 
  5 import java.util.HashSet; 
  6 import java.util.Iterator; 
  7 import java.util.List; 
  8 import java.util.Set; 
  9 
 10 import javax.servlet.jsp.JspException; 
 11 import javax.servlet.jsp.tagext.Tag; 
 12 import javax.servlet.jsp.tagext.TagSupport; 
 13 
 14 import org.acegisecurity.Authentication; 
 15 import org.acegisecurity.GrantedAuthority; 
 16 import org.acegisecurity.context.SecurityContextHolder; 
 17 import org.springframework.util.StringUtils; 
 18 import org.springframework.web.util.ExpressionEvaluationUtils; 
 19 
 20 import sample.web.action.AppContext; 
 21 /** 
 22 
 23 @author limq 
 24 
 25 */ 
 26 public class AuthorizeActionTag extends TagSupport{ 
 27 
 28             private String ifAllGranted = ""
 29             private String ifAnyGranted = ""
 30             private String ifNotGranted = ""
 31             
 32             public void setIfAllGranted(String ifAllGranted) throws JspException { 
 33                 this.ifAllGranted = ifAllGranted; 
 34             } 
 35 
 36             public String getIfAllGranted() { 
 37                 return ifAllGranted; 
 38             } 
 39 
 40             public void setIfAnyGranted(String ifAnyGranted) throws JspException { 
 41                 this.ifAnyGranted = ifAnyGranted; 
 42             } 
 43 
 44             public String getIfAnyGranted() { 
 45                 return ifAnyGranted; 
 46             } 
 47 
 48             public void setIfNotGranted(String ifNotGranted) throws JspException { 
 49                 this.ifNotGranted = ifNotGranted; 
 50             } 
 51 
 52             public String getIfNotGranted() { 
 53                 return ifNotGranted; 
 54             } 
 55             
 56             public int doStartTag() throws JspException { 
 57                 if (((null == ifAllGranted) || "".equals(ifAllGranted)) 
 58                     && ((null == ifAnyGranted) || "".equals(ifAnyGranted)) 
 59                     && ((null == ifNotGranted) || "".equals(ifNotGranted))) { 
 60                     return Tag.SKIP_BODY; 
 61                 } 
 62 
 63                 final Collection granted = getPrincipalFunctionByAuthorities(); 
 64 
 65                 final String evaledIfNotGranted = ExpressionEvaluationUtils 
 66                     .evaluateString("ifNotGranted", ifNotGranted, pageContext); 
 67 
 68                 if ((null != evaledIfNotGranted) && !"".equals(evaledIfNotGranted)) { 
 69                     Set grantedCopy = retainAll(granted, 
 70                                     parseSecurityString(evaledIfNotGranted)); 
 71 
 72                     if (!grantedCopy.isEmpty()) { 
 73                         return Tag.SKIP_BODY; 
 74                     } 
 75                 } 
 76 
 77                 final String evaledIfAllGranted = ExpressionEvaluationUtils 
 78                     .evaluateString("ifAllGranted", ifAllGranted, pageContext); 
 79 
 80                 if ((null != evaledIfAllGranted) && !"".equals(evaledIfAllGranted)) { 
 81                     if (!granted.containsAll(parseSecurityString(evaledIfAllGranted))) { 
 82                         return Tag.SKIP_BODY; 
 83                     } 
 84                 } 
 85 
 86                 final String evaledIfAnyGranted = ExpressionEvaluationUtils 
 87                     .evaluateString("ifAnyGranted", ifAnyGranted, pageContext); 
 88 
 89                 if ((null != evaledIfAnyGranted) && !"".equals(evaledIfAnyGranted)) { 
 90                     Set grantedCopy = retainAll(granted, 
 91                                     parseSecurityString(evaledIfAnyGranted)); 
 92 
 93                     if (grantedCopy.isEmpty()) { 
 94                         return Tag.SKIP_BODY; 
 95                     } 
 96                 } 
 97 
 98                 return Tag.EVAL_BODY_INCLUDE; 
 99             } 
100     /** 
101      * 得到用户的Authentication,并且从Authentication中获得 Authorities,进而得到 授予用户的 Function 
102      * @return 
103      */ 
104             private Collection getPrincipalFunctionByAuthorities() { 
105                     
106                     
107             Authentication currentUser = SecurityContextHolder.getContext() 
108             .getAuthentication(); 
109                 if (null == currentUser) { 
110                     return Collections.EMPTY_LIST; 
111                 } 
112 
113                 if ((null == currentUser.getAuthorities()) 
114                     || (currentUser.getAuthorities().length < 1)) { 
115                     return Collections.EMPTY_LIST; 
116                 } 
117            // currentUser.getAuthorities() 返回的是 GrantedAuthority[] 
118                 List granted = Arrays.asList(currentUser.getAuthorities()); 
119                 AuthDao authDao =(AuthDao) AppContext.getInstance().getAppContext().getBean("authDao"); 
120                 Collection grantedFunctions = authDao.getFunctionsByRoles(granted); 
121                 return grantedFunctions; 
122             } 
123 
124             /** 
125              * 得到用户功能(Function)的集合,并且验证是否合法 
126              * @param c Collection 类型 
127              * @return Set类型 
128              */ 
129             private Set SecurityObjectToFunctions(Collection c) { 
130                 Set target = new HashSet(); 
131 
132                 for (Iterator iterator = c.iterator(); iterator.hasNext();) { 
133                     GrantedFunction function = (GrantedFunction) iterator.next(); 
134 
135                     if (null == function.getFunction()) { 
136                         throw new IllegalArgumentException( 
137                             "Cannot process GrantedFunction objects which return null from getFunction() - attempting to process " 
138                             + function.toString()); 
139                     } 
140 
141                     target.add(function.getFunction()); 
142                 } 
143 
144                 return target; 
145             } 
146 
147             /** 
148              * 处理页面标志属性 ,用' ,'区分 
149              */ 
150             private Set parseSecurityString(String functionsString) { 
151                 final Set requiredFunctions = new HashSet(); 
152                 final String[] functions = StringUtils 
153                     .commaDelimitedListToStringArray(functionsString); 
154 
155                 for (int i = 0; i < functions.length; i++) { 
156                     String authority = functions[i]; 
157 
158                  // Remove the role's whitespace characters without depending on JDK 1.4+ 
159                  // Includes space, tab, new line, carriage return and form feed. 
160                  String function = StringUtils.replace(authority, " """); 
161                  function = StringUtils.replace(function, "\t"""); 
162                  function = StringUtils.replace(function, "\r"""); 
163                  function = StringUtils.replace(function, "\n"""); 
164                  function = StringUtils.replace(function, "\f"""); 
165 
166                  requiredFunctions.add(new GrantedFunctionImpl(function)); 
167                 } 
168 
169                 return requiredFunctions; 
170             } 
171             /** 
172              * 获得用户所拥有的Function 和 要求的 Function 的交集 
173              * @param granted 用户已经获得的Function 
174              * @param required 所需要的Function 
175              * @return 
176              */ 
177           
178             private Set retainAll(final Collection granted, final Set required) { 
179                 Set grantedFunction = SecurityObjectToFunctions(granted); 
180                 Set requiredFunction = SecurityObjectToFunctions(required); 
181                 // retailAll() 获得 grantedFunction 和 requiredFunction 的交集 
182                 // 即删除 grantedFunction 中  除了 requiredFunction 的项 
183                 grantedFunction.retainAll(requiredFunction); 
184 
185                 return rolesToAuthorities(grantedFunction, granted); 
186             } 
187 
188             /** 
189              * 
190              * @param grantedFunctions 已经被过滤过的Function            
191              * @param granted 未被过滤过的,即用户所拥有的Function 
192              * @return 
193              */ 
194             private Set rolesToAuthorities(Set grantedFunctions, Collection granted) { 
195                 Set target = new HashSet(); 
196 
197                 for (Iterator iterator = grantedFunctions.iterator(); iterator.hasNext();) { 
198                     String function = (String) iterator.next(); 
199 
200                     for (Iterator grantedIterator = granted.iterator(); 
201                         grantedIterator.hasNext();) { 
202                         GrantedFunction grantedFunction = (GrantedFunction) grantedIterator 
203                             .next(); 
204 
205                         if (grantedFunction.getFunction().equals(function)) { 
206                             target.add(grantedFunction); 
207 
208                             break
209                         } 
210                     } 
211                 } 
212 
213                 return target; 
214             } 
215 
216 
217 
再说明一下吧,通过 AppContext 获得了Spring的上下文,以及AuthDao(实际意义上讲以不再是单纯的Dao,应该是Service)
package sample.auth; 

import java.util.Collection; 
public interface  AuthDao 

    
/** 
     *  根据用户的角色集合 得到 用户的 操作权限 
     * 
@param granted 已授予用户的角色集合 
     * 
@return 操作权限的集合 
     
*/
 
        
public Collection getFunctionsByRoles(Collection granted); 
}
 
以下是AuthDao 的实现

package sample.auth; 

import java.util.Collection; 
import java.util.HashSet; 
import java.util.Iterator; 
import java.util.Set; 

import org.acegisecurity.GrantedAuthority; 

import sample.auth.cache.FunctionCache; 
import sample.auth.cache.info.RoleByNameCache; 
import sample.dao.IBaseDao; 
import sample.mappings.function.Function; 
import sample.mappings.role.Role; 


public class AuthDaoImpl  implements AuthDao { 

    
private IBaseDao baseDao; 
    
private FunctionCache cache; 
    
private RoleByNameCache roleCache; 
    
        
public RoleByNameCache getRoleCache() { 
                
return roleCache; 
        } 

        
public void setRoleCache(RoleByNameCache roleCache) { 
                
this.roleCache = roleCache; 
        } 

        
public FunctionCache getCache() { 
                
return cache; 
        } 

        
public void setCache(FunctionCache cache) { 
                
this.cache = cache; 
        } 

        
public IBaseDao getBaseDao() { 
        
return baseDao; 
    } 

    
public void setBaseDao(IBaseDao baseDao) { 
        
this.baseDao = baseDao; 
    } 

  

        
public Collection getFunctionsByRoles(Collection granted) { 
                Set set 
= new HashSet(); 
                
if(null == granted) throw new IllegalArgumentException("Granted Roles cannot be null"); 
        
                
for(Iterator it = granted.iterator();it.hasNext();){ 
            
            GrantedAuthority grantedAuthority 
= (GrantedAuthority)it.next(); 
            Role  role 
= roleCache.getRoleByRoleNameCache(grantedAuthority.getAuthority()); // 
            if(role == null){ 
                    role 
= (Role)baseDao.loadByKey(Role.class"name", grantedAuthority.getAuthority()); 
                    roleCache.putRoleInCache(role); 
            } 
            GrantedFunction[] grantedFunctions 
= cache.getFunctionFromCache(role.getName()); 
            
            
if(grantedFunctions == null){ 
                    
                    Set functions 
= role.getFunctions(); 
                            
for(Iterator it2 = functions.iterator();it2.hasNext();){        
                    Function function 
= (Function)it2.next(); 
                    GrantedFunction grantedFunction 
= new GrantedFunctionImpl(function.getName()); 
                                    set.add(  grantedFunction  ); 
                            } 
                  
                            grantedFunctions 
= (GrantedFunction[]) set.toArray(new GrantedFunction[0]); 
                            cache.putFuncitonInCache(role.getName(),grantedFunctions); 
            } 
            
            
for(int i = 0 ; i < grantedFunctions.length; i++){ 
                    GrantedFunction grantedFunction 
= grantedFunctions[i]; 
                    set.add(grantedFunction); 
            } 
                } 
        
                
return set; 
        } 



3 基于hibernate的用户验证

acegi 默认的 的 用户验证是 通过UserDetailsService 接口 实现的 也就是说我们只要实现了 它的loadUserByUsername 方法。
1 
2 public UserDetails loadUserByUsername(String username) 
3         throws UsernameNotFoundException, DataAccessException; 
以下是我的实现
  1 package sample.auth; 
  2 
  3 import java.util.ArrayList; 
  4 import java.util.Iterator; 
  5 import java.util.List; 
  6 import java.util.Set; 
  7 
  8 import org.acegisecurity.GrantedAuthority; 
  9 import org.acegisecurity.GrantedAuthorityImpl; 
 10 import org.acegisecurity.userdetails.User; 
 11 import org.acegisecurity.userdetails.UserDetails; 
 12 import org.acegisecurity.userdetails.UserDetailsService; 
 13 import org.acegisecurity.userdetails.UsernameNotFoundException; 
 14 import org.springframework.dao.DataAccessException; 
 15 
 16 import sample.auth.cache.AuthorityBasedUserCache; 
 17 import sample.dao.IBaseDao; 
 18 import sample.mappings.role.Role; 
 19 import sample.utils.MisUtils; 
 20 
 21 public class HibernateDaoImpl implements UserDetailsService{ 
 22 
 23         
 24         private String rolePrefix = ""
 25         private boolean usernameBasedPrimaryKey = false
 26     private AuthorityBasedUserCache cache; 
 27 
 28     private IBaseDao baseDao; 
 29 
 30         public String getRolePrefix() { 
 31                 return rolePrefix; 
 32         } 
 33         
 34         public void setRolePrefix(String rolePrefix) { 
 35                 this.rolePrefix = rolePrefix; 
 36         } 
 37         
 38         public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { 
 39                 UserDetails user = getUsersByUsernameQuery(username); 
 40                 if(user == nullreturn null
 41                 
 42                 GrantedAuthority[] arrayAuths =getAuthoritiesByUsernameQuery(username); 
 43              if (arrayAuths.length == 0) { 
 44                     throw new UsernameNotFoundException("User has no GrantedAuthority"); 
 45                 } 
 46               
 47              return new User(username, user.getPassword(), user.isEnabled(), 
 48                      truetruetrue, arrayAuths); 
 49         } 
 50 
 51         /** 
 52         * 根据用户名查找用户 
 53         * @param username 
 54         * @return 
 55         * @throws DataAccessException 
 56         */ 
 57         public UserDetails getUsersByUsernameQuery(String username)throws DataAccessException { 
 58                         sample.mappings.user.User misUser = (sample.mappings.user.User)baseDao.loadByKey(sample.mappings.user.User.class,"name",username); 
 59                         if(misUser != null
 60                         { 
 61                         org.acegisecurity.userdetails.UserDetails user = 
 62                         new User(misUser.getName(),misUser.getPassword(),MisUtils.parseBoolean(misUser.getEnable()),true,true,true,getAuthoritiesByUsernameQuery(username)); 
 63                         return user; 
 64                         }else 
 65                         return null;        
 66                 } 
 67         
 68         /** 
 69           * 根据用户名查找角色 
 70           * @param username 
 71           * @return GrantedAuthority[] 用户角色 
 72           * @throws DataAccessException 
 73           */ 
 74         public GrantedAuthority[] getAuthoritiesByUsernameQuery(String username) 
 75                 throws DataAccessException { 
 76                 sample.mappings.user.User misUser 
 77                 = (sample.mappings.user.User)baseDao.loadByKey(sample.mappings.user.User.class,"name",username); 
 78 
 79         if(misUser != null){ 
 80                 GrantedAuthority[] grantedAuthoritys = cache.getAuthorityFromCache(misUser.getName()); 
 81         
 82                 if(grantedAuthoritys == null){ 
 83                 
 84                         Set roles =     misUser.getRoles(); 
 85                         Iterator it = roles.iterator(); 
 86                 
 87                         List list = new ArrayList(); 
 88                         while(it.hasNext() ){ 
 89         
 90                                 GrantedAuthorityImpl gai = new GrantedAuthorityImpl(  ((Role)it.next()).getName()  ); 
 91                                 list.add(gai); 
 92                                 } 
 93                         grantedAuthoritys =(GrantedAuthority[]) list.toArray(new  GrantedAuthority[0]); 
 94                         cache.putAuthorityInCache(misUser.getName(),grantedAuthoritys); 
 95                         return grantedAuthoritys; 
 96                 
 97                 } 
 98                return grantedAuthoritys; 
 99         } 
100 
101                 return null
102 
103 
104         public IBaseDao getBaseDao() { 
105                 return baseDao; 
106         } 
107 
108         public void setBaseDao(IBaseDao baseDao) { 
109                 this.baseDao = baseDao; 
110         } 
111 
112         public AuthorityBasedUserCache getCache() { 
113                 return cache; 
114         } 
115 
116         public void setCache(AuthorityBasedUserCache cache) { 
117                 this.cache = cache; 
118         } 
119 
120 
121 
通过以上对acegi 的 处理,足以满足我们目前在spring下基于RBAC的动态权限管理。同时在对频繁的数据库查询上使用了Ehcache作为缓存,在性能上有了很大的改善。

代码 下载:
http://www.blogjava.net/Files/limq/acegi-2.rar 可能不好打开,需要重新开一个IE

posted on 2006-01-19 02:16 limq 阅读(15541) 评论(27)  编辑  收藏 所属分类: 编程技巧

评论

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

可不可以把源码打包给我们下载,最好有一个sample
2006-01-19 09:27 | lili

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

可不可以看看你整个例子的源文件阿
我的mail:zjnbshifox@163.com
2006-01-19 09:46 | zjnbshifox

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

代码我已经上传了~http://www.blogjava.net/Files/limq/acegi-2.rar
2006-01-19 11:29 | limq

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

http://forum.springframework.org/showthread.php?t=10185&page=2&highlight=writing+ObjectDefinitionSource
2006-02-09 14:57 | rubin

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

嗯,是在这里参考了很多~
2006-02-11 22:59 | limq

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

下载了,但运行不起来,能不能给个可以运行的上来啊?avgi2003@tom.com。谢了。
日志:

2006-02-16 10:30:34,703 [org.hibernate.impl.SessionFactoryImpl]-[INFO] closing
2006-02-16 10:30:34,734 [org.springframework.cache.ehcache.EhCacheManagerFactoryBean]-[INFO] Shutting down EHCache CacheManager
2006-02-16 10:30:34,734 [net.sf.ehcache.CacheManager]-[WARN] CacheManager already shutdown
2006-02-16 10:30:34,765 [org.springframework.web.context.ContextLoader]-[ERROR] Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authDaoTarget' defined in ServletContext resource [/WEB-INF/applicationContext-basic.xml]: Can't resolve reference to bean 'roleCache' while setting property 'roleCache'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'roleCache' is defined
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'roleCache' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:352)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedBeanDefinition(AbstractBeanFactory.java:671)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:147)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:176)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:105)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1013)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:824)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:345)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:226)
2006-02-16 11:30 | avgi

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

仔细看了一下acegi
发现无论是基于角色,还是基于功能的权限管理,都是对于业务逻辑的权限管理
即使是进一步的划分,到了dao这一层也就下不去了
但是如果有这样的需求:
用户A的权限比用户B的权限高,那么在查询某些数据的时候,A所能看到的字段就要比B的多
对于这样的需求,有什么比较好的方式来解决么?
2006-02-17 15:05 | 情书捉刀

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

你也可以看一下他demo的schema。
他确实控制到的dao,如果要实现控制到对象object,也是可以的,
只不过要通过增加表来实现。。。。。。。。。
2006-03-31 16:43 | Robert Tao

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

用了文章的方法,可是论证不通过时跳转的页面不成功..错误如下,,,,

org.acegisecurity.AccessDeniedException: Access is denied

org.acegisecurity.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
org.acegisecurity.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:347)
org.acegisecurity.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:113)
org.acegisecurity.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:79)
org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
org.acegisecurity.providers.anonymous.AnonymousProcessingFilter.doFilter(AnonymousProcessingFilter.java:138)
org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
org.acegisecurity.ui.AbstractProcessingFilter.doFilter(AbstractProcessingFilter.java:246)
org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
org.acegisecurity.context.HttpSessionContextIntegrationFilter.doFilter(HttpSessionContextIntegrationFilter.java:220)
org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
org.acegisecurity.util.FilterChainProxy.doFilter(FilterChainProxy.java:173)
org.acegisecurity.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:120)
2006-04-24 03:58 | 嘻哈标明

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

以上的问题已经解决。。。

在这再闪感谢作者提供这样的文章,我按上面扩展我的acegi有如下问题:

首先我的实验项目是这样的:spring+tapestry+hibernate+acegi

一、方法拦截不正常
1、容器(tomcat)初次启动,操作受保护的方法,当无权限时拦截有效,但不自动跳回失败页面。
2、当只要一个用户登录,整个容器受保护的方法都失效,即其他未登录用户也可以操作受保护的方法,但已经登录用户的Session与未登录用户的Session是不一样的。

我是一个spring+tapestry+hibernate+acegi,希望能得到作者以及大家的支持。再次感谢
2006-04-25 18:13 | 嘻哈标明

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

请问各位怎么样把acegi的数据库转换成sqserver????
2006-05-25 10:32 | yujun

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

登录失败提示如下
请帮助分析一下原因 是数据的问题吗?
java.lang.NullPointerException
sample.auth.cache.info.EhCacheFunctinoByName.putFunctionInCache(EhCacheFunctinoByName.java:53)
sample.auth.DatabaseDrivenMethodDefinitionSourcew.lookupAttributes(DatabaseDrivenMethodDefinitionSourcew.java:50)
org.acegisecurity.intercept.method.AbstractMethodDefinitionSource.getAttributes(AbstractMethodDefinitionSource.java:51)
jrockit.reflect.VirtualNativeMethodInvoker.invoke(Ljava.lang.Object;[Ljava.lang.Object;)Ljava.lang.Object;(Unknown Source)
java.lang.reflect.Method.invoke(Ljava.lang.Object;[Ljava.lang.Object;I)Ljava.lang.Object;(Unknown Source)
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:287)
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:181)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:148)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
$Proxy3.getAttributes(Ljava.lang.Object;)Lorg.acegisecurity.ConfigAttributeDefinition;(Unknown Source)
org.acegisecurity.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:288)
sample.auth.interceptor.MethodCacheInterceptor.invoke(MethodCacheInterceptor.java:65)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
$Proxy1.getAllDepartment()Ljava.util.List;(Unknown Source)
sample.web.action.department.DepartmentListAction.execute(DepartmentListAction.java:18)
org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:421)
org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:226)
org.apache.struts.action.ActionServlet.process(ActionServlet.java:1164)
org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:397)
javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
javax.servlet.http.HttpServlet.service(HttpServlet.java:802)

2006-06-21 10:45 | tim200

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

请问,我怎样session中,记录其他的值,如用户的电话号码等?
谢谢!!
2006-08-23 16:19 | yyd12345

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

请问,我下载源码后怎么不能导入sample.Contact包呢?能否发过包含所有文件的源码给我,小弟不胜感谢!
2006-09-13 14:27 | fxyz

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

请问,我下载源码后怎么不能导入sample.Contact包呢?能否发过包含所有文件的源码给我,小弟不胜感谢!邮箱:powerful_boys@sohu.com
2006-09-13 14:29 | fxyz

# 几乎不使用任何框架的办法,一样可以实现细粒度控制  回复  更多评论   

有这么麻烦么?当然完全通用的角色权限管理是不现实的,这个与各个系统的业务逻辑紧密相关。完全可以使用最朴素的做法,将权限做成菜单,菜单的动作就是出来的页面,功能点则对应该页面上的按钮动作。用户与角色是多对多关系,通过关联表联系;角色与权限是多对多关系,也通过关联表联系;另外还有一个角色权限功能点表对应该角色的某条权限的功能点设置,所以这个表是多字段联合关键字。最后,需要一个预先定义好的静态表一样的东西,权限表和功能点表,注意前者是具有层次递归性的。
本人不使用任何框架,顺利完成一个项目,建议大家不要去想什么框架技术,框架的应用会带来以后重用的巨大负担。其实我这个办法重用起来非常容易,分析好业务逻辑,定义好菜单,设计好角色,就一切OK。主要是有几个非常复杂的算法,涉及递归处理和展现页面等。有兴趣与我联系。

email:starfaraway@sina.com 回复
2006-09-17 23:13 | lpy

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

http://www.sydan-ei-ole-mukana.beibi.info ^^^ http://www.sydaninfarktin-oire.beibi.info ^^^ http://www.film-kaate-brudar.biseksuell.info ^^^ http://www.gallerier-lade-lefdal.biseksuell.info ^^^ http://www.film-kaate-brudar.erotiska.info ^^^ http://www.gallerier-lade-lefdal.erotiska.info ^^^ http://www.oerfaren-farsa-mpg.fitta69.info ^^^ http://www.bbw-whore.fitta69.info ^^^ http://www.heta-brud-knulla.fotsex.info ^^^ http://www.snygga-pattar.fotsex.info ^^^ http://www.derbi-videot-hemaiseva.isomuna.info ^^^ http://www.geisha-pussy-porno.isomuna.info ^^^ http://www.leikkeet-lesbo-orgasm.laukeaminen.info ^^^ http://www.kuuma-norsunluu.laukeaminen.info ^^^ http://www.gutter-ung-gratis.rype.info ^^^ http://www.filmrull-knulle-vaapen.rype.info ^^^ http://www.vintage-nudist.sadsprut.info ^^^ http://www.galleri-pen-handjager.sadsprut.info ^^^ http://www.kylma-mahtava-poika.tytsy.info ^^^ http://www.sopo-mpegit.tytsy.info ^^^ http://www.images-anal-mpgs.18analsex.com ^^^ http://www.ass-lickers-freesex.18analsex.com ^^^ http://www.negre-bocchinare-immagini.pazzesesso.com ^^^ http://www.xxx-belle-diciottenni.pazzesesso.com ^^^ http://www.fotografia-anal-sex.figanere.com ^^^ http://www.filmato-succhiatrice-piacere.figanere.com ^^^ http://www.gratis-quindicenne-porcona.inculatexxx.com ^^^ http://www.clarence-mms-mpg.inculatexxx.com ^^^ http://www.download-dolcetto-cannibal.prostitutaculo.com ^^^ http://www.milf-wifey-download.prostitutaculo.com ^^^ http://www.mpeg-radio-brasiliane.lesbicastrip.com ^^^ http://www.mpg-sexworld-com.lesbicastrip.com ^^^ http://www.xxx-univision-utah.007sexogratis.com ^^^ http://www.costa-rica-clip.007sexogratis.com ^^^ http://www.download-sexy-caracas.3sexogratis.com ^^^ http://www.jpg-sweet-lolita.3sexogratis.com ^^^ http://www.gals-jovencita-filipina.analsexogratis.com ^^^ http://www.catalog-calendario-maya.analsexogratis.com ^^^ http://www.bellezas-desnuda-gals.cam-sexo-gratis.com ^^^ http://www.avi-viejitos-cojiendo.cam-sexo-gratis.com ^^^
2007-01-02 17:12 | NeVaL

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

狗屁
2007-04-13 14:27 | 狗屁

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

人家已经缺省实现了,你只要拦截一下,就可以轻松扩展,何必多次一举
2007-06-18 13:57 | ewe

# re: Acegi+hibernate 动态实现基于角色的权限管理 [未登录]  回复  更多评论   

sample.Contact包呢
2007-11-23 14:06 | vinson

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

楼主能不能做一个可运行的eclipse例子出来啊,还有,要打那些jar包加上去
2008-03-01 22:53 | YN.LING

# 看来一堆人都还没看懂楼主讲的是什么啊  回复  更多评论   

那种什么控制菜单点击的最原始做法就不要拿出来丢人现眼了,一大堆安全隐患,随便搞搞就给你搞死了。还把递归算法都拿出来当宝贝。
那些只知道缺省实现的也别出来说了,估计还没搞懂楼主为什么要这样搞
2008-05-09 17:58 | 123

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

你好:
我下载了你的例子,但是有那么多包不难找
能否留个联系方式,我的QQ是157114471,可以的话把包传我,谢谢,感激中...
2008-05-16 15:00 | liufl

# re: Acegi+hibernate 动态实现基于角色的权限管理 [未登录]  回复  更多评论   

不错,不错
2008-05-27 09:25 | 小白

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   


不知道这么例子是否已经达到了我的要求...正在发难..

我的情况和楼主一样..用户要求可以自己定义角色 和角色的权限...

看了楼主在页面上貌似也用了acegi 的标签来做..这不写死了吗??

QQ:125749092 希望等得到大家的指点....
2008-06-05 15:00 | 你好

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

包里面文件不全啊。
2008-06-23 15:09 | xx

# re: Acegi+hibernate 动态实现基于角色的权限管理   回复  更多评论   

你加入 资源模块的同时 ,没想到要加入 组织结构吗? 包括如果后期的工作流的开发。跟这个必不可少的。
2008-07-05 11:15 | 大帅

只有注册用户登录后才能发表评论。


网站导航: