acegi+ssh动态实现基于角色的权限管理(四)

 资源信息:
 code:

public Resource(String authResource, int resType, GrantedAuthority[] authorities) {
  // TODO 自动生成构造函数存根
  if(authResource == null ||"".equals(authResource)){
   throw new IllegalArgumentException("can't put null or empty value to resource string");
  }
  if(resType == 0){
   throw new IllegalArgumentException("can't put zero value to resource type");
  }
  this.resString = authResource;
  this.resType = resType;
  setAuthorities(authorities);
 }
 
 
 
 public GrantedAuthority[] getAuthorities() {
  return authorities;
 }
 
 protected void setAuthorities(GrantedAuthority[] authorities) {
  Assert.notNull(authorities,"can't put a null GrantedAuthority arrays");
  this.authorities = authorities;
  for (int i = 0; i < authorities.length; i++) {
   Assert.notNull(authorities, "Granted Authority "+i+"is null" +
     "||GrantedAuthorities[] can't put any null elements");
  }
 }

 

getter and setter.......

 
 
 
 
 
 
 
 
 
 
资源缓存:
code:

public class ResourceCache {
 private Cache cache;
 
 /**
  * 根据资源获取资源串
  */
 public ResourceDetails getAuthorityFromCache(String orgResource) {
  // TODO 自动生成方法存根
  Element element =null;
  try {
   element = cache.get(orgResource);
  } catch (CacheException e) {
   // TODO 自动生成 catch 块
   throw new DataRetrievalFailureException("Cache failure:"+e.getMessage(),e);
  }
  if(element==null){
   return null;
  }else{
   return (ResourceDetails) element.getValue();
  }
 }

 
 /**
  * 从Cache缓存移除资源权限
  * @param orgResource
  */
 public void removeAuthorityFromCache(String orgResource) {
  // TODO 自动生成方法存根
  this.cache.remove(orgResource);
 }

 /**
  * 将资源串放置Cache中
  * @param rd
  */
 public void putAuthorityInCache(ResourceDetails rd) {
  // TODO 自动生成方法存根
  Element element = new Element(rd.getResString(),rd);
  this.cache.put(element);
 }


 public Cache getCache() {
  return cache;
 }

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

 public List getFunctions() {
  // TODO 自动生成方法存根
  return getResourcesByType(Constants.RESOURCE_FUNCTION);
 }

 public List getComponents() {
  // TODO 自动生成方法存根
  return getResourcesByType(Constants.RESOURCE_COMPONENT);
 }

 public List getUrlResStrings() {
  // TODO 自动生成方法存根
  return getResourcesByType(Constants.RESOURCE_URL);
 }


 /**
  * 通过资源类型获取资源
  * @param i
  * @return
  */
 @SuppressWarnings("unchecked")
 private List getResourcesByType(int type) {
  // TODO 自动生成方法存根
  List resources = null;
  List resList = new ArrayList();
  try {
   resources = this.cache.getKeys();
  } catch (IllegalStateException e) {
   // TODO 自动生成 catch 块
   throw new IllegalStateException("find source from cache error:"+e.getMessage(),e);
  } catch (CacheException e) {
   // TODO 自动生成 catch 块
   throw new UnsupportedOperationException("cache error"+e.getMessage(),e);
  }
  for (Iterator iter = resources.iterator(); iter.hasNext();) {
   String resString = (String) iter.next();
   ResourceDetails rd=getAuthorityFromCache(resString);
   if(rd!=null && type ==rd.getResType()){
    resList.add(resString);
   }
  }
  return resList;
 }

标识资源类别:

public class Constants {

 public static final int RESOURCE_URL = 1;
 public static final int RESOURCE_FUNCTION = 2;
 public static final int RESOURCE_COMPONENT = 3;

 
配置管理userCache和resourceCache

 <!-- set userCahce -->
 <bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
  <property name="cache">
   <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    <property name="cacheManager" ref="cacheManager"/>
    <property name="cacheName" value="userCache"/>
   </bean>
  </property>
 </bean>

<!-- set resourceCache -->
 <bean id="resourceCache" class="com.runsa.components.acegi.resource.impl.ResourceCache"
   autowire="byName">
  <property name="cache">
   <bean id="resourceCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    <property name="cacheManager" ref="cacheManager"/>
    <property name="cacheName" value="resourceCache"/>
   </bean>
  </property>
 </bean>

 
缓存管理器使用spring的ehcache
 <!-- set cacheManager -->
 <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
  <property name="configLocation">
   <!-- load from classpath,or it can't load ehcache.xml -->
   <value>classpath:ehcache.xml</value>
  </property>
 </bean>
缓存的配置信息,在这里可以通过property设置,也可以通过配置文件实现

<ehcache>
 <diskStore path="java.io.tmpdir"/>

 <!-- set default cache -->
 <defaultCache
   maxElementsInMemory="30000"
   eternal="false"
   overflowToDisk="true"
   timeToIdleSeconds="0"
   timeToLiveSeconds="0"
   diskPersistent="false"
   diskExpiryThreadIntervalSeconds="120"/>

 <!-- acegi user cache-->
 <cache name="userCache"
     maxElementsInMemory="30000"
     eternal="true"
     overflowToDisk="true"/>

 <!-- acegi resource cache-->
 <cache name="resourceCache"
     maxElementsInMemory="30000"
     eternal="true"
     overflowToDisk="true"/>

</ehcache>

设置其最大缓存数量及过期时间,userCache与resourceCache设置永不过期eternal="true"
注意的问题:ehcache.xml需要通过classpath加载,否则加载不了.服务器启动时,
warning,ehcache.jar的ehcache_failsafe.xml load failed....这样缓存的默认时间为:120s,缓存过期之后,就无法获取userCache和resourceCache的资源,将会认证无效.
 
下面实现方法级的权限设置.
<!-- * set methodServiceSecurity -->
 <bean id="methodServiceSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="accessDecisionManager" ref="businessAccessDecisionManager"/>
  <property name="objectDefinitionSource" ref="methodDefinitionSouce"/>
 </bean>
 
 <!-- set  businessAccessDicisionManager-->
 <bean id="businessAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
  <property name="allowIfAllAbstainDecisions" value="false"/>
  <property name="decisionVoters">
   <bean class="org.acegisecurity.vote.RoleVoter">
    <property name="rolePrefix" value="ROLE_"/>
   </bean>
  </property>
 </bean>
  
 <!-- set methodDefinitionSouce for myself implement with db -->
 <bean id="methodDefinitionSouce" class="com.runsa.components.acegi.inteceptor.method.DBMethodDefinitionSource">
  <property name="acegiCacheManager" ref="acegiCacheManager"/>
 </bean>
MethodSecurityInterceptor在方法调用前,将进行拦截认证.
objectDefinitionSource,定义的保护资源.通过配置文件加载的话
<value>
     CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
     PATTERN_TYPE_APACHE_ANT
     com.runsa.service.Users.find* = ROLE_ADMIN,ROLE_USER
     com.runsa.service.Users.add* = ROLE_ADMIN
     com.runsa.service.Users.update* = ROLE_ADMIN
     com.runsa.service.Users.delete* = ROLE_ADMIN
<value>
这样就可以了,但为了动态实现,就从db中获取吧.
 
DBMethodDefinitionSource,自己实现MethodDefinitionSource.
 

public class DBFilterInvocationDefinationSource extends
  AbstractFilterInvocationDefinitionSource {
 private boolean convertUrlToLowercaseBeforeComparison = false;
 
 private boolean useAntPath = false;
 
 private AcegiCacheManager acegiCacheManager;
 
 //ant path
 private final PathMatcher pathMatcher=new AntPathMatcher();
 
 //perl5 path
 private final PatternMatcher patternMatcher = new Perl5Matcher();
 
 
 
 /**
  * 返回当前的url对应的role
  */
 @SuppressWarnings("unchecked")
 @Override
 public ConfigAttributeDefinition lookupAttributes(String url) {
  // TODO 自动生成方法存根
  acegiCacheManager.initResourceCache();
  
  if(isUseAntPath()){
   //第一个?标记索引
   int firstQuestionMarkIndex = url.lastIndexOf("?");
   
   if(firstQuestionMarkIndex != -1){
    url = url.substring(0, firstQuestionMarkIndex);
   }
  } 
   
   //获取所有的url
   List urls=acegiCacheManager.getUrlResString();
   
   //先顺序排列
   Collections.sort(urls);
   //然后倒叙排列
   Collections.reverse(urls);
   
   //将url在比较前都转换为小写
   if(convertUrlToLowercaseBeforeComparison){
    url = url.toLowerCase();
   }
   //授予权限操作
   GrantedAuthority authorities []= new GrantedAuthority[0];
   for (Iterator iter = urls.iterator(); iter.hasNext();) {
    String resString = (String) iter.next();
    boolean matched = false;
    //使用ant匹配url
    if(isUseAntPath()){
     matched = pathMatcher.match(resString, url);
    }else{
     //perl5编译url
     Pattern compiledPattern;
     Perl5Compiler compiler=new Perl5Compiler();
     try {
      compiledPattern = compiler.compile(resString, Perl5Compiler.READ_ONLY_MASK);
     } catch (MalformedPatternException e) {
      // TODO 自动生成 catch 块
      throw new IllegalArgumentException("资源字符串参数格式错误:"+resString,e);
     }
     matched = patternMatcher.matches(url, compiledPattern);
    }
    
    //匹配正确
    if(matched){
     ResourceDetails rd = acegiCacheManager.getAuthorityFromCache(resString);
     authorities = rd.getAuthorities();
     break;
    }
   }
   
   //配置res,role,权限大于0
   if(authorities.length>0){
    String authoritiesStr = " ";
    for (int i = 0; i < authorities.length; i++) {
     authoritiesStr += authorities[i].getAuthority()+",";
    }
    
    String authStr = authoritiesStr.substring(0,authoritiesStr.length()-1);
    ConfigAttributeEditor configAttrEditor =  new ConfigAttributeEditor();
    configAttrEditor.setAsText(authStr.trim());
    return (ConfigAttributeDefinition) configAttrEditor.getValue();
   }
   
  return null;
 }

 public Iterator getConfigAttributeDefinitions() {
  // TODO 自动生成方法存根
  return null;
 }

getter and setter.......

 
好,那么现在要对你的方法进行拦截,就很方便了.
<bean id="newsService" parent="txTemplate">
  <property name="proxyInterfaces" value="com.runsa.service.NewsService"/>
  <property name="target" ref="newsServiceTarget"/>
  <property name="preInterceptors" ref="methodServiceSecurity"/>
 </bean>
在service中事务开始前进行安全调用拦截preInterceptors就OK.
 
最后一步,在users,roles,authorities改变的时候,更新userCache和resourceCache.一种方法就是,对service或者dao的改变users,roles,authorities
信息的时候,进行拦截,并更新相应的userCache和resourceCache,就可以达到动态实现了.但还有一种方法,就是利用hibernate的eventListener,对insert,update,delete,load进行监听操作.下边看我的实现:
 

<bean id="sessionFactory"
  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="eventListeners">
   <map>
    <entry key="post-insert"><ref bean="authenticationPostInsertListenerEvent"/></entry>
    <entry key="post-update"><ref bean="authenticationPostUpdateListenerEvent"/></entry>
    <entry key="post-delete"><ref bean="authenticationPostDeleteListenerEvent"/></entry>
   </map>
  </property>
  <property name="hibernateProperties">

  ..........

  </property>

.........

</bean>

 

 然后自己实现相应的监听器

 <!-- set authenticationPostUpdateListenerEvent --> 
 <bean id="authenticationPostUpdateListenerEvent"
   class="com.runsa.components.acegi.cache.listener.AuthenticationPostUpdateListenerEvent">
  <property name="updateAcegiCache" ref="updateAcegiCache"/>
 </bean>

.................................

 
 ....next page

你可能感兴趣的:(bean,cache,ssh,配置管理,Acegi)