Acegi 資源配置動態擴展實現
本文原出處 : http://starcraft.blogdriver.com/starcraft/1089862.htmlAcegi 資源配置動態擴展實現
王 政 (Feiing) 于 2005-12-11
1. 問題提出
在使用 Acegi Security Framework 的過程中, 如果細心的話, 會發現其資源和角色配置是在配置文件中的, 下面是 Appfuse 中相關配置 :
java代碼:? |
? ? <bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"> ? ? ? ? <property name="authenticationManager"><ref local="authenticationManager"/></property> ? ? ? ? <property name="accessDecisionManager"><ref local="accessDecisionManager"/></property> ? ? ? ? ?<property name="objectDefinitionSource"> ? ? ? ? ? ? <value> ? ? ? ? ? ? ? ? CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON ? ? ? ? ? ? ? ? PATTERN_TYPE_APACHE_ANT ? ? ? ? ? ? ? ? /signup.html=ROLE_ANONYMOUS,admin,tomcat ? ? ? ? ? ? ? ? /passwordhint.html*=ROLE_ANONYMOUS,admin,tomcat ? ? ? ? ? ? ? ? /**/*.html*=admin,tomcat ? ? ? ? ? ? ? ? /clickstreams.jsp=admin ? ? ? ? ? ? </value> ? ? ? ? </property> ? ? </bean> |
上面的配置從功能上實現了資源與角色的映射, 但用戶可能會提出在運行期動態改變權限分配的需求, 配置文件策略可能略顯不足, 下面我將提供一種基于數據庫的策略解決此問題.
2. E-R 模型
下圖是需要的 E-R 模型

圖1 Acegi 標準 RBAC E-R設計
圖中的用戶與角色不再多做解釋, 我們主要關注一下 Permission 表 和 Resource 表, 這里 Resource 表用于存儲系統資源, 在 web 層一般來說就是 url, 如果使用 acl, 就是 aclClass, 此時 Permission 表中的 aclMask 用來存儲對應的 acl 權限, 考慮到 acl 在 web 項目中使用率不高, 下面我將著重介紹 web 層的權限控制, 對 acl 有興趣的讀者可以自己參閱 Acegi Reference Guide.
3. 如何阻止 acegi 從配置文件讀取權限配置
從 Appfuse 中的示例性配置可以看出, acegi 對權限配置的要求是 “ 資源 = 角色1, 角色2 … 角色 n ”, 看過源代碼的讀者應該知道, 最終這些配置將被組裝為 net.sf.acegisecurity.intercept. ObjectDefinitionSource(web 層對應的實現是 net.sf.acegisecurity.intercept.web. FilterInvocationDefinitionSource), 那么我們怎么才能用數據庫的數據來組裝 FilterInvocationDefinitionSource ? 這里涉及到一個 PropertyEditor 問題, 在 Acegi 中, FilterInvocationDefinitionSource 是通過 net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSourceEditor 組裝的, 假如我們不想讓 FilterInvocationDefinitionSourceEditor 從配置文件中讀取權限配置, 就需要自己實現一個 ProdertyEditor 來覆蓋默認實現, 下面是我的 配置 :

圖2 customerEditorConfigurer 配置
那么, 這個 PropertyEditor 中需要做些什么呢 ? 要做的就是使用一個比較特殊的標記, 當遇到這個特殊標記的時候直接略過解析, 我這里使用的標記是 “DONT_USE_ME”, 然后在 PropertyEditor 中簡單的如下實現即可:
java代碼:? |
/* * Copyright 2004-2005 wangz. * Project shufe_newsroom */ package com.skyon.um.security.acegi.intercept.web; import java.beans.PropertyEditorSupport; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import net.sf.acegisecurity.ConfigAttributeDefinition; import net.sf.acegisecurity.ConfigAttributeEditor; import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionMap; import net.sf.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap; import net.sf.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @since 2005-8-4 * @author 王政 * @version $Id: FilterInvocationDefinitionSourceDynamicExtentionEditor.java,v 1.2 2005/11/04 15:55:07 wangzheng Exp $ */ publicclass FilterInvocationDefinitionSourceDynamicExtentionEditor extends ? ? ? ? PropertyEditorSupport{ ? ? publicstaticfinalString ANT_PATH_KEY = "PATTERN_TYPE_APACHE_ANT"; ? ? ? ? publicstaticfinalString LOWER_CASE_URL_KEY = "CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON"; ? ? publicstaticfinalString DONT_USE_ME_KEY = "DONT_USE_ME"; ? ? ? ? publicstaticfinalString STAND_DELIM_CHARACTER = ","; ? ? privatestaticfinal Log logger = LogFactory.getLog(FilterInvocationDefinitionSourceDynamicExtentionEditor.class); ? ? ? ? /** ? ? ?* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String) ? ? ?*/ ? ? publicvoid setAsText(String text)throwsIllegalArgumentException{? ? ? ? ? ? ? ? ? ? ? ? FilterInvocationDefinitionMap source = new RegExpBasedFilterInvocationDefinitionMap(); ? ? ? ? ? ? ? ? ? ? ? ? if(StringUtils.isBlank(text)){ ? ? ? ? ? ? // Leave target object empty ? ? ? ? }else{? ? ? ? ? ? ? ? ? ? ? // Check if we need to override the default definition map ? ? ? ? ? ? if(text.lastIndexOf(ANT_PATH_KEY) != -1){ ? ? ? ? ? ? ? ? source = new PathBasedFilterInvocationDefinitionMap(); ? ? ? ? ? ? ? ? if(logger.isDebugEnabled()){ ? ? ? ? ? ? ? ? ? ? logger.debug(("Detected PATTERN_TYPE_APACHE_ANT directive; using Apache Ant style path expressions")); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? if(text.lastIndexOf(LOWER_CASE_URL_KEY) != -1){ ? ? ? ? ? ? ? ? if(logger.isDebugEnabled()){ ? ? ? ? ? ? ? ? ? ? logger.debug("Instructing mapper to convert URLs to lowercase before comparison"); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? source.setConvertUrlToLowercaseBeforeComparison(true); ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? if(text.indexOf(DONT_USE_ME_KEY) != -1){ ? ? ? ? ? ? ? ? if(logger.isDebugEnabled()){ ? ? ? ? ? ? ? ? ? ? logger.debug("DETECTED " + DONT_USE_ME_KEY + " directive;? skip parse, Use " + EhCacheBasedFilterInvocationDefinitionSourceCache.class + " to parse!"); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? addSecureUrl(source, "/dontuseme", "dontuseme"); ? ? ? ? ? ? }else{ ? ? ? ? ? ? ? ? BufferedReader br = newBufferedReader(newStringReader(text)); ? ? ? ? ? ? ? ? int counter = 0; ? ? ? ? ? ? ? ? String line; ? ? ? ? ? ? ? ? while(true){ ? ? ? ? ? ? ? ? ? ? counter++; ? ? ? ? ? ? ? ? ? ? try{ ? ? ? ? ? ? ? ? ? ? ? ? line = br.readLine(); ? ? ? ? ? ? ? ? ? ? }catch(IOException ioe){ ? ? ? ? ? ? ? ? ? ? ? ? throw newIllegalArgumentException(ioe.getMessage()); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? if(line == null){ ? ? ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? line = line.trim(); ? ? ? ? ? ? ? ? ? ? if(logger.isDebugEnabled()){ ? ? ? ? ? ? ? ? ? ? ? ? logger.debug("Line " + counter + ": " + line); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? if(line.startsWith("//")) { ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? if(line.equals(LOWER_CASE_URL_KEY)){ ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? if(line.lastIndexOf('=') == -1){ ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? // Tokenize the line into its name/value tokens ? ? ? ? ? ? ? ? ? ? String[] nameValue = org.springframework.util.StringUtils.delimitedListToStringArray(line, "="); ? ? ? ? ? ? ? ? ? ? String name = nameValue[0]; ? ? ? ? ? ? ? ? ? ? String value = nameValue[1]; ? ? ? ? ? ? ? ? ? ? addSecureUrl(source, name, value);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? } ? ? ? ? setValue(source); ? ? } ? ? ? ? /** ? ? ?* @param source ? ? ?* @param name ? ? ?* @param value ? ? ?* @throws IllegalArgumentException ? ? ?*/ ? ? privatevoid addSecureUrl(FilterInvocationDefinitionMap source, String name, String value) ? ? ? ? throwsIllegalArgumentException{ ? ? ? ? ? ? ? ? // Convert value to series of security configuration attributes ? ? ? ? ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor(); ? ? ? ? configAttribEd.setAsText(value); ? ? ? ? ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd.getValue(); ? ? ? ? // Register the regular expression and its attribute ? ? ? ? source.addSecureUrl(name, attr); ? ? } } |
Ok, 現在 FilterInvocationDefinitionSourceDynamicExtentionEditor 遇到配置文件中的 “DONT_USE_ME” 時將直接略過, 下面是我的 filterInvocationInterceptor 配置:

圖3 filterInvocationInterceptor 配置
現在, 我們已經成功阻止 acegi 從配置文件讀取權限配置, 下一個問題就是:
4. 如何從表中數據組裝 FilterInvocationDefinitionSource
為了實現此功能, 需要一個自定義的資源定義接口來提供 FilterInvocationDefinitionSource, 此接口可能會是這樣 :
java代碼:? |
/* * Copyright 2005-2010 the original author or autors *? *? ? http://www.skyon.com.cn * * Project { SkyonFramwork } */ package com.skyon.um.security.acegi.intercept.web; import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource; import org.springframework.beans.factory.FactoryBean; import com.skyon.framework.spring.ehcache.FlushableCache; /** * <class>FilterInvocationDefinitionSourceCache</class> use to hold the global FilterInvocationDefinitionSource, * it keeps a static variable , if the source been changed(generally the database data),? the reload method should be called * * @see com.skyon.um.security.acegi.intercept.event.FilterInvocationDefinitionSourceChangedEvent * @see com.skyon.um.security.acegi.intercept.event.FilterInvocationDefinitionSourceListener * @see com.skyon.um.security.acegi.intercept.web.SecurityEnforcementDynamicExtensionFilter * @since 2005-8-7 * @author 王政 * @version $Id: FilterInvocationDefinitionSourceCache.java,v 1.1 2005/11/04 15:55:07 wangzheng Exp $ */ publicinterface FilterInvocationDefinitionSourceCache extends FactoryBean, FlushableCache { ? ? ? ? ? ? ? ? /** The Perl5 expression? */ ? ? int REOURCE_EXPRESSION_PERL5_REG_EXP = 1; ? ? ? ? /** The ant path expression */ ? ? int RESOURCE_EXPRESSION_ANT_PATH_KEY = 2; ? ? ? ? /** ? ? ?* Set resource expression, the value must be {@link #REOURCE_EXPRESSION_PERL5_REG_EXP} or {@link #RESOURCE_EXPRESSION_ANT_PATH_KEY} ? ? ?* @see #REOURCE_EXPRESSION_PERL5_REG_EXP ? ? ?* @see #RESOURCE_EXPRESSION_ANT_PATH_KEY ? ? ?* @param resourceExpression the resource expression ? ? ?*/ ? ? void setResourceExpression(int resourceExpression); ? ? ? ? /** ? ? ?* Set whether convert url to lowercase before comparison ? ? ?* @param convertUrlToLowercaseBeforeComparison whether convertUrlToLowercaseBeforeComparison ? ? ?*/ ? ? void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison); ? ? ? ? ? ? /** ? ? ? ? * Get the defination source, generally from a database schema ? ? ? ? * @return the defination source ? ? ? ? */ ? ? FilterInvocationDefinitionSource getFilterInvocationDefinitionSource(); ? ? ? ? ? ? } |
其核心方法是 FilterInvocationDefinitionSource getFilterInvocationDefinitionSource(), 此方法將代替配置文件提供資源和角色的配置, 下面是實現
java代碼:? |
/* * Copyright 2005-2010 the original author or autors *? *? ? http://www.skyon.com.cn * * Project { SkyonFramwork } */ package com.skyon.um.security.acegi.intercept.web; import net.sf.acegisecurity.ConfigAttributeDefinition; import net.sf.acegisecurity.ConfigAttributeEditor; import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionMap; import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource; import net.sf.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap; import net.sf.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import com.skyon.framework.spring.ehcache.CacheUtils; import com.skyon.framework.spring.ehcache.SerializableObjectProvider; import com.skyon.framework.spring.support.MandatorySingletonBeanSupport; /** * @since 2005-8-7 * @author 王政 * @version $Id: EhCacheBasedFilterInvocationDefinitionSourceCache.java,v 1.2 2005/11/17 09:38:25 wangzheng Exp $ */ publicclass EhCacheBasedFilterInvocationDefinitionSourceCache extends MandatorySingletonBeanSupport ? ? ? ? implements FilterInvocationDefinitionSourceCache, InitializingBean { ? ? ? ? privatestaticfinal Log logger = LogFactory.getLog(EhCacheBasedFilterInvocationDefinitionSourceCache.class); ? ? ? ? privateint resourceExpression; ? ? ? ? privateboolean convertUrlToLowercaseBeforeComparison = false; ? ? ? ? ? ? private ResourceMappingProvider resourceMappingProvider; ? ? ? ? private Cache cache; ? ? ? ? privateObject lock = newObject(); ? ? ? ? /** ? ? ?* @see com.skyon.um.security.acegi.intercept.web.FilterInvocationDefinitionSourceCache#getFilterInvocationDefinitionSource() ? ? ?*/ ? ? public FilterInvocationDefinitionSource getFilterInvocationDefinitionSource(){ ? ? ? ? ? ? synchronized (lock){ ? ? ? ? ? ? ? ? Element element = CacheUtils.get(getCache(), "key"); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(element == null){ ? ? ? ? ? ? ? ? ? ? ? ? FilterInvocationDefinitionSource definitionSource = (FilterInvocationDefinitionSource) getFilterInvocationDefinitionSourceFromBackend(); ? ? ? ? ? ? ? ? ? ? ? ? element = newElement("key", new SerializableObjectProvider(definitionSource)); ? ? ? ? ? ? ? ? ? ? ? ? getCache().put(element); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return(FilterInvocationDefinitionSource)((SerializableObjectProvider) element.getValue()).getSourceObject(); ? ? ? ? ? ? } ? ? } ? ? ? ? ? ? ? ? ? ? publicvoid flushCache(){ ? ? ? ? ? ? ? ? CacheUtils.flushCache(getCache()); ? ? ? ? ? ? ? ? getFilterInvocationDefinitionSource(); ? ? ? ? } ? ? ? ? private FilterInvocationDefinitionMap getFilterInvocationDefinitionSourceFromBackend(){? ? ? ? ? ? ? ? ? ? logger.info(" 開始加載系統資源權限數據到緩存... "); ? ? ? ? ? ? ? ? ? ? ? ? FilterInvocationDefinitionMap definitionSource = null; ? ? ? ? ? ? ? ? ? ? switch(resourceExpression){ ? ? ? ? ? ? case REOURCE_EXPRESSION_PERL5_REG_EXP : { ? ? ? ? ? ? ? ? definitionSource = new RegExpBasedFilterInvocationDefinitionMap(); ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? ? ? case RESOURCE_EXPRESSION_ANT_PATH_KEY : { ? ? ? ? ? ? ? ? definitionSource = new PathBasedFilterInvocationDefinitionMap(); ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? ? ? default : { ? ? ? ? ? ? ? ? throwException(); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? ? ? ? ? definitionSource.setConvertUrlToLowercaseBeforeComparison(isConvertUrlToLowercaseBeforeComparison()); ? ? ? ? ? ? ? ? ResourceMapping[] mappings = getResourceMappingProvider().getResourceMappings(); ? ? ? ? if(mappings == null || mappings.length ==0){ ? ? ? ? ? ? return definitionSource; ? ? ? ? } ? ? ? ? ? ? ? ? for(int i = 0; i < mappings.length; i++){ ? ? ? ? ? ? ResourceMapping mapping = mappings[i]; ? ? ? ? ? ? String[] recipents = mapping.getRecipients(); ? ? ? ? ? ? ? ? ? ? ? ? if(recipents == null || recipents.length == 0){ ? ? ? ? ? ? ? ? if(logger.isErrorEnabled()){ ? ? ? ? ? ? ? ? ? ? logger.error("Notice, the resource : " + mapping.getResourcePath() + " hasn't no recipents, it will access by any one ! "); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? StringBuffer valueBuffer = new StringBuffer(); ? ? ? ? ? ? for (int j = 0; j < recipents.length; j++) { ? ? ? ? ? ? ? ? valueBuffer.append(recipents[j]); ? ? ? ? ? ? ? ? if (j < recipents.length - 1) { ? ? ? ? ? ? ? ? ? ? valueBuffer.append(FilterInvocationDefinitionSourceDynamicExtentionEditor.STAND_DELIM_CHARACTER); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? String value = valueBuffer.toString();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? addSecureUrl(definitionSource, mapping.getResourcePath(), value); ? ? ? ? ?} ? ? ? ? ? ? ? ? logger.info(" 成功加載系統資源權限數據到緩存 ! "); ? ? ? ? return definitionSource; ? ? } ? ? /** ? ? ?* @param source ? ? ?* @param name ? ? ?* @param value ? ? ?* @throws IllegalArgumentException ? ? ?*/ ? ? private synchronized void addSecureUrl(FilterInvocationDefinitionMap source, String name, String value) ? ? ? ? throws IllegalArgumentException { ? ? ? ? ? ? ? ? // Convert value to series of security configuration attributes ? ? ? ? ConfigAttributeEditor configAttribEd = new ConfigAttributeEditor(); ? ? ? ? configAttribEd.setAsText(value); ? ? ? ? ConfigAttributeDefinition attr = (ConfigAttributeDefinition) configAttribEd.getValue(); ? ? ? ? // Register the regular expression and its attribute ? ? ? ? source.addSecureUrl(name, attr); ? ? } ? ? ? ? public void afterPropertiesSet() throws Exception { ? ? ? ? if (resourceExpression != REOURCE_EXPRESSION_PERL5_REG_EXP && resourceExpression != RESOURCE_EXPRESSION_ANT_PATH_KEY) { ? ? ? ? ? ? throwException(); ? ? ? ? }? ? ? ? ? ? ? ? Assert.notNull(getResourceMappingProvider(), " resourceMappingProvider must be specified");? ? ? ? ? ? ? Assert.notNull(getCache(), " cache must be specified");? ? ? ? } ? ? /** ? ? ?* @throws IllegalArgumentException ? ? ?*/ ? ? private void throwException() throws IllegalArgumentException { ? ? ? ? throw new IllegalArgumentException("wrong resourceExpression value"); ? ? } ? ? ? ? ? ? /** ? ? ?* @return Returns the resourceMappingProvider. ? ? ?*/ ? ? public ResourceMappingProvider getResourceMappingProvider() { ? ? ? ? return resourceMappingProvider; ? ? } ? ? /** ? ? ?* @param resourceMappingProvider The resourceMappingProvider to set. ? ? ?*/ ? ? public void setResourceMappingProvider(ResourceMappingProvider resourceMappingProvider) { ? ? ? ? this.resourceMappingProvider = resourceMappingProvider; ? ? } ? ? /** ? ? ?* @return Returns the convertUrlToLowercaseBeforeComparison. ? ? ?*/ ? ? public boolean isConvertUrlToLowercaseBeforeComparison() { ? ? ? ? return convertUrlToLowercaseBeforeComparison; ? ? } ? ? /** ? ? ?* @param convertUrlToLowercaseBeforeComparison The convertUrlToLowercaseBeforeComparison to set. ? ? ?*/ ? ? public void setConvertUrlToLowercaseBeforeComparison( ? ? ? ? ? ? boolean convertUrlToLowercaseBeforeComparison) { ? ? ? ? this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison; ? ? } ? ? /** ? ? ?* @return Returns the resourceExpression. ? ? ?*/ ? ? public int getResourceExpression() { ? ? ? ? return resourceExpression; ? ? } ? ? /** ? ? ?* @param resourceExpression The resourceExpression to set. ? ? ?*/ ? ? public void setResourceExpression(int resourceExpression) { ? ? ? ? this.resourceExpression = resourceExpression; ? ? } ? ? ? ? /** ? ? ? ? * @return Returns the cache. ? ? ? ? */ ? ? ? ? public Cache getCache() { ? ? ? ? ? ? ? ? return cache; ? ? ? ? } ? ? ? ? /** ? ? ? ? * @param cache The cache to set. ? ? ? ? */ ? ? ? ? public void setCache(Cache cache) { ? ? ? ? ? ? ? ? this.cache = cache; ? ? ? ? } ? ? ? ? } |
實現采用 EhCache 緩存資源權限配置, 這樣如果資源權限數據發生變化, 可以 flush Cache 從數據庫重新讀取. 至于代碼中的 ResourceMapingProvider 實現, 簡單的把 Resource 表和 Role 表中的數據讀取過來即可, 這里不再贅述.
5. 如何將數據庫中的權限配置傳遞給 FilterInvocationInterceptor
完成以上步驟后, 最后一步就是如何把 FilterInvocationDefinitionSourceCache 中的 FilterInvocationDefinitionSource 傳遞給 FilterInvocationInterceptor, Simple implemention :
java代碼:? |
public class SecurityEnforcementDynamicExtensionFilter extends ? ? ? ? SecurityEnforcementFilter implements InitializingBean { … 略去 publicvoid doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throwsIOException, ServletException {? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? // get the defination source form soure holder ? ? ? ? ? ? ? ? getFilterSecurityInterceptor().setObjectDefinitionSource(getDefinitionSourceCache().getFilterInvocationDefinitionSource()); } } |
配置:
It’s Over Now.
posted on 2006-04-28 23:41 Vincent.Chen 閱讀(233) 評論(0) 編輯 收藏