再论 Acegi 权限存储策略
本文原出处
http://starcraft.blogdriver.com/starcraft/1135045.html
在我之前的一篇文章里, 说明了在 Acegi 中如何将资源权限数据存储到数据库中, 文章见 http://www.hibernate.org.cn/viewtopic.php?t=17538,
虽然文中方式实现了从数据库读取资源权限, 但是代码量较大, 并且重载了 SecurityEnforcementFilter, 造成比较大的侵入性,
这里我将提供另一种更简洁的方式实现此功能.
入口还是 org.acegisecurity.intercept.web.FilterSecurityInterceptor, 资源权限配置来自于
objectDefinitionSource, 标准配置方式采用 FilterInvocationDefinitionSourceEditor 解析, 支持 Perl5 和 AntPath 两种风格,
为了实现从其它位置(典型如数据库), 我们要做的就是实现一个自定义的 FilterInvocationDefinitionSource, 查看类层次结构图后可以发现,
acegi 中已经有一个 AbstractFilterInvocationDefinitionSource 已经实现此接口, 只要实现一个抽象方法即可
public abstract ConfigAttributeDefinition lookupAttributes(String url);
public class RdbmsBasedFilterInvocationDefinitionSource extends AbstractFilterInvocationDefinitionSource
/** * <class>ConfigableFilterInvocationDefinition</class> 支持 Perl5 和 ant Path 两种风格的资源配置方式 * @since 2006-1-19 * @author 王政 * @version $Id: ConfigableFilterInvocationDefinition.java,v 1.3 2006/01/19 09:40:37 wz Exp $ */public interface ConfigableFilterInvocationDefinition {/** The Perl5 expression */String PERL5_KEY = "PATTERN_TYPE_PERL5"; /** The ant path expression */ String ANT_PATH_KEY = "PATTERN_TYPE_APACHE_ANT"; /** 标准分隔符 */ String STAND_DELIM_CHARACTER = ","; /** * Set resource expression, the value must be {@link #PERL5_KEY_REG_EXP} or {@link #ANT_PATH_KEY} * @see #REOURCE_EXPRESSION_PERL5_REG_EXP * @see #RESOURCE_EXPRESSION_ANT_PATH_KEY * @param resourceExpression the resource expression */ void setResourceExpression(String resourceExpression); /** * * @return resource expression */ String getResourceExpression(); /** * Set whether convert url to lowercase before comparison * @param convertUrlToLowercaseBeforeComparison whether convertUrlToLowercaseBeforeComparison */ void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison); /** * * @return whether convert url to lowercase before comparison */ boolean isConvertUrlToLowercaseBeforeComparison(); }/** * <class>RdbmsBasedFilterInvocationDefinitionSource</class> 是基于数据库的权限存储实现, 它支持两种风格的配置 * @see com.skyon.uum.security.acegi.intercept.web.ConfigableFilterInvocationDefinition * @see org.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap * @see org.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap * @since 2006-1-19 * @author 王政 * @version $Id: RdbmsBasedFilterInvocationDefinitionSource.java,v 1.6 2006/02/13 03:20:55 wz Exp $ */public class RdbmsBasedFilterInvocationDefinitionSource extends AbstractFilterInvocationDefinitionSource implements ConfigableFilterInvocationDefinition, InitializingBean { //~ Static fields/initializers ============================================= private static final Log logger = LogFactory.getLog(RdbmsBasedFilterInvocationDefinitionSource.class); //~ Instance fields ======================================================== private String resourceExpression = PERL5_KEY; private boolean convertUrlToLowercaseBeforeComparison = false; private ResourceMappingProvider resourceMappingProvider; // ~ Methods ================================================================ /** * * @see org.acegisecurity.intercept.web.AbstractFilterInvocationDefinitionSource#lookupAttributes(java.lang.String) */public ConfigAttributeDefinition lookupAttributes(String url) {FilterInvocationDefinitionSource actualSource = populateFilterInvocationDefinitionSource();if (RegExpBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {return ((RegExpBasedFilterInvocationDefinitionMap) actualSource).lookupAttributes(url);} else if (PathBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {return ((PathBasedFilterInvocationDefinitionMap) actualSource).lookupAttributes(url);}throw new IllegalStateException("wrong type of " + actualSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class + " or " + PathBasedFilterInvocationDefinitionMap.class);}/** * * @see org.acegisecurity.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions() */public Iterator getConfigAttributeDefinitions() {FilterInvocationDefinitionSource actualSource = populateFilterInvocationDefinitionSource();if (RegExpBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {return ((RegExpBasedFilterInvocationDefinitionMap) actualSource).getConfigAttributeDefinitions();} else if (PathBasedFilterInvocationDefinitionMap.class.isInstance(actualSource)) {return ((PathBasedFilterInvocationDefinitionMap) actualSource).getConfigAttributeDefinitions();}throw new IllegalStateException("wrong type of " + actualSource + ", it should be " + RegExpBasedFilterInvocationDefinitionMap.class + " or " + PathBasedFilterInvocationDefinitionMap.class);} private FilterInvocationDefinitionSource populateFilterInvocationDefinitionSource() { FilterInvocationDefinitionMap definitionSource = null; if (PERL5_KEY.equals(getResourceExpression())) { definitionSource = new RegExpBasedFilterInvocationDefinitionMap(); } else if (ANT_PATH_KEY.equals(getResourceExpression())) { definitionSource = new PathBasedFilterInvocationDefinitionMap(); } else { throw new IllegalArgumentException("wrong resourceExpression value"); } definitionSource.setConvertUrlToLowercaseBeforeComparison(isConvertUrlToLowercaseBeforeComparison()); ResourceMapping[] mappings = getResourceMappingProvider().getResourceMappings(); if (mappings == null || mappings.length ==0) { return (FilterInvocationDefinitionSource) 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(STAND_DELIM_CHARACTER); } } String value = valueBuffer.toString(); addSecureUrl(definitionSource, mapping.getResourcePath(), value); } return (FilterInvocationDefinitionSource )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); } 省去 getter, setter....}public interface ResourceMappingProvider { String RESOURCE_PATH_PREFIX = "/"; /** * Get Resource Mapping * @return resource mapping */ ResourceMapping[] getResourceMappings(); }public class ResourceMapping { // url private String resourcePath; // 即角色 private String[] recipients = new String[0]; 省去 getter, setter....}<bean id="resourceCache" autowire="byType"/> <!-- ======================== AUTHENTICATION ======================= --> <!-- Note the order that entries are placed against the objectDefinitionSource is critical. The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL. Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last --> <bean id="filterInvocationInterceptor" /> 4 楼 malinser 2007-01-21 你好,我正在做ACegi的论文,有些问题想请教一下,我QQ是14453787,希望你的指导 5 楼 1123321 2007-07-27 大哥,能否借您的完整代码Look一下,我从您原来的blog:
http://starcraft.blogdriver.com/starcraft/找到这里,实在没有找到您的联系方式,所以留下自己的联系方式:
QQ:56868655
MSN:cn_zhongyiming@hotmail.com
静侯佳音!
祝您工作愉快!