`
Feiing
  • 浏览: 240051 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

再论 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);


因此, 自定义一个 Class 如下 :
public class RdbmsBasedFilterInvocationDefinitionSource extends AbstractFilterInvocationDefinitionSource 


因为 acegi 中已经提供了 Perl5 和 AntPath 的实现, 只需要集成过来即可, 因此定义接口如下

/**
 * <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();
   
}



再让 RdbmsBasedFilterInvocationDefinitionSource 实现此接口即可, 下面是简略代码

/**
 * <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....

}



ResourceMappingProvider

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....

}



这样就很完美的既支持了从数据库的读取数据, 又可以自由选择 Perl5 和 AntPath 两种风格的配置, 最后不要忘了给 ResourceMappingProvider 加一层 cache, 我的配置如下

<bean id="resourceCache" class="com.skyon.uum.security.acegi.intercept.web.cache.EhCacheBasedResourceCache">
		<property name="dataSource">
			<ref bean="dataSource"></ref>
		</property>
		<property name="cache">
			<bean parent="cacheTemplate">				
				<property name="cacheName"><value>resourceCache</value></property>
			</bean>
        </property>
		<property name="allResourcesQuery">
			<value>
			select distinct t.id, t.id, t.parent_id, t.url, t.title, t.layer, t.type, t.application_id
			from uum_resource t order by t.orderField
			</value>
		</property>
	</bean>
	
	<bean id="permissionCache" class="com.skyon.uum.security.acegi.intercept.web.cache.EhCacheBasedPermissionCache">
		<property name="dataSource">
			<ref bean="dataSource"></ref>
		</property>
		<property name="cache">
			<bean parent="cacheTemplate">				
				<property name="cacheName"><value>permissionCache</value></property>
			</bean>
        </property>
		<property name="recipentsResourceMappingQuery">
			<value>
			select r.name, p.resource_id from uum_permission p left outer join uum_role r on p.role_id = r.id
			</value>
		</property>	
	</bean>
	
			
	<bean id="resourceMappingProvider" class="com.skyon.uum.security.acegi.intercept.web.ResourceMappingProviderImpl" 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" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
        <property name="authenticationManager"><ref local="authenticationManager"/></property>
        <property name="accessDecisionManager"><ref local="accessDecisionManager"/></property>
        <property name="objectDefinitionSource"><ref local="filterInvocationDefinitionSource"></ref></property>
    </bean>
	
	<bean id="filterInvocationDefinitionSource" class="com.skyon.uum.security.acegi.intercept.web.RdbmsBasedFilterInvocationDefinitionSource">
		<property name="resourceMappingProvider">
			<ref local="resourceMappingProvider"></ref>
		</property>
		<property name="resourceExpression">
			<value>PATTERN_TYPE_APACHE_ANT</value>
		</property>
	</bean>



这段时间看了很多人对 Acegi 的评价, 有不少观点认为 Acegi 的配置太过繁琐, 其实权限控制本来就不是一件很轻松的事, Acegi 用 AOP 实现, 配置文件的确有些繁琐,
但是只要一个配置文件就解决了整个系统的权限问题, 可谓一劳永逸, 相比较在 Action 中实现应该还是利远大于弊, 也有人说用 WebWork 的 Interceptor 实现, 虽然也是不错的 solution,
但是不要忘了, 并不是所有的项目都使用 webwork , 假如有一个 struts 的项目, 权限控制就会有移植性的问题.

我的 msn : shartcn@msn.com, 有问题欢迎讨论
分享到:
评论
5 楼 1123321 2007-07-27  
大哥,能否借您的完整代码Look一下,我从您原来的blog:
http://starcraft.blogdriver.com/starcraft/找到这里,实在没有找到您的联系方式,所以留下自己的联系方式:
QQ:56868655
MSN:cn_zhongyiming@hotmail.com
静侯佳音!
祝您工作愉快!
4 楼 malinser 2007-01-21  
你好,我正在做ACegi的论文,有些问题想请教一下,我QQ是14453787,希望你的指导
3 楼 acerjsj 2006-12-26  
2 楼 kjj 2006-07-31  
楼主使用了cache 数据库资源的方法动态更新配置资源
没有提供cache类 的代码啊 那几个查询语句 不甚明白阿
1 楼 baizheng 2006-02-22  
嗯,我是支持Acegi 的

相关推荐

    Spring Acegi权限控制

    Spring Acegi权限控制是Spring框架中用于实现Web应用安全的一种解决方案。Acegi Security(现已被Spring Security替代)是一个功能强大的安全框架,它主要解决了认证(Authentication)和授权(Authorization)这两...

    最简单acegi权限管理实例

    总的来说,这个"最简单acegi权限管理实例"是一个很好的学习起点,通过实践可以了解Acegi的基本用法,包括用户认证、权限控制、配置文件的理解以及如何与Spring框架集成。在实际项目中,我们需要根据需求进行更复杂的...

    acegi

    - "db"可能指的是数据库相关的配置或日志,Acegi通常需要配置用户数据库来存储用户信息和权限数据。 - "aopacegi"可能是一个包含Acegi与AOP相关配置或实现的文件,可能涉及切面的定义和安全策略的配置。 Acegi ...

    使用acegi控制用户权限实例

    在本实例中,我们将深入探讨如何使用Acegi来控制用户的权限。Acegi Security已经被Spring Security替代,但其核心思想和机制仍然适用于现代的Spring Security。 首先,我们需要理解Acegi的基础概念。Acegi的核心是`...

    Acegi 数据库配置安全策略 源代码及图解

    2. **数据库配置**:Acegi允许将安全配置存储在数据库中,这意味着安全策略可以动态地进行修改,而无需每次变动时都重新部署应用。这在权限复杂多变的企业级应用中非常实用,管理员可以直接在数据库层面进行权限的增...

    acegi实现用户权限

    开发者还可以通过实现自定义的UserDetailsService接口来获取和验证用户信息,这使得AceGI能够与现有的用户存储系统(如数据库或LDAP)集成。 另外,AceGI提供了丰富的日志和审计功能,可以帮助开发者追踪和分析安全...

    acegi java权限验证框架ppt讲座和代码

    Acegi Java权限验证框架是Spring Security的前身,它是一个强大且灵活的安全框架,用于Java企业级应用程序。这个框架提供了一套完整的解决方案,包括用户认证、访问控制、安全配置以及会话管理等多个方面,旨在帮助...

    mypro.rar_Acegi Appfuse2 _acegi_java 数据库_java 权限_数据权限控制

    Acegi的强大之处在于它能够无缝集成到Spring环境中,使得开发者可以方便地实现复杂的权限控制策略。在本项目中,Acegi主要负责用户的登录验证、角色分配以及资源访问权限的控制。 Appfuse是一个开源项目,旨在简化...

    acegi+ldap

    在Spring和Acegi的结合中,Acegi提供了一个灵活且可扩展的安全基础设施,而LDAP作为身份验证和授权的数据源,可以存储用户账户信息和权限设置。将Acegi与LDAP整合,可以使应用程序利用LDAP服务器进行用户登录验证和...

    batis+acegi实现的动态权限控制

    标题 "batis+acegi实现的动态权限控制" 暗示了这个项目是关于整合Spring框架中的Acegi安全模块和MyBatis ORM框架,来创建一个动态的权限管理系统。Acegi是Spring早期的安全组件,现在已被Spring Security所取代,但...

    Acegi使用.pdf

    不同于传统的安全框架,Acegi采用了面向切面编程(AOP)的方式来处理认证和授权问题,这使得它能够与业务代码保持高度的解耦,同时也赋予了开发人员更多的灵活性来定制安全策略。本文将深入探讨Acegi的关键组件、...

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

    在权限管理中,我们可能需要存储用户、角色和权限的相关信息。通过Hibernate,我们可以方便地创建这些实体类,定义它们之间的关系,并实现CRUD操作。 4. **配置Acegi与Hibernate** 配置Acegi以使用Hibernate进行...

    acegisecurity-1.0.7

    此外,Acegi Security还支持预授权和后授权策略,使得权限管理更加灵活。 三、会话管理 Acegi Security对会话进行了强化管理,可以防止会话劫持、会话固定攻击等。它提供了会话超时检测、会话固定保护(session-...

    Acegi学习笔记(JAVA系统安全编程时用到)

    在 `acegi-config.xml` 文件中,你可以定义认证和授权策略,比如用户存储(UserDetailsService)、认证管理器(AuthenticationManager)和权限访问决策器(AccessDecisionManager)。 Acegi 还提供了许多预定义的...

    acegi-sample.rar_acegi

    Acegi框架的核心是它对用户认证和权限控制的处理。它允许开发者定义复杂的认证机制,如基于用户名和密码的登录,或者支持数字证书等。同时,Acegi提供了细粒度的授权机制,可以基于角色、URL、方法级别进行访问控制...

    Acegi_db1.rar_acegi

    Acegi_db1.rar_acegi 是一个与Acegi安全框架相关的压缩包,它可能包含了用于数据库配置和安全策略实现的源代码以及相关的说明文档。Acegi是Spring Security的前身,是一个非常重要的Java安全框架,主要用于企业级...

    基于spring的Acegi安全框架认证与授权的分析及扩展.pdf

    由于Acegi默认配置文件策略可能无法满足所有场景下的需求,尤其是在需要动态调整用户权限的情况下。为了解决这一问题,可以采用基于数据库的策略来进行动态扩展。 - **基于数据库的策略**: 1. 将权限信息存储在...

    grails-acegi-0.5.zip

    2. `application.properties`:这个文件用于存储应用的属性,可以包含Grails Acegi插件的配置参数,如用户的认证方式、权限设置等。 3. `plugin.xml`:XML格式的插件配置文件,通常包含插件的版本、作者等信息,有时...

    acegi-security-tiger-1.0.7.jar

    这个版本的jar文件"acegi-security-tiger-1.0.7.jar"是该框架的核心库,包含了实现安全控制的所有类和接口,使得开发者能够更方便地管理用户的登录、权限分配以及访问控制。 Acegi Security的主要功能包括: 1. **...

Global site tag (gtag.js) - Google Analytics