资源信息:
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