差沙的密码 -- SSHWSFC's code
阅读本Blog请自备塑料袋一只
posts - 14,  comments - 59,  trackbacks - 0
Acegi好早就实现了ACL(好像是0.5),但是使用起来确实有点麻烦,所以用的不是太广泛。这里简单的说明一下使用方法,希望有更多的朋友来试试。

首先要理解Acegi里面Voter的概念,ACL正是在一个Voter上扩展起来的。现来看一下AclVoter的配置。

    <bean id="aclBeanReadVoter" class="org.acegisecurity.vote.BasicAclEntryVoter">
        
<property name="processConfigAttribute">
            
<value>ACL_READ</value>
        
</property>
        
<property name="processDomainObjectClass">
            
<value>org.springside.modules.security.acl.domain.AclDomainAware</value>
        
</property>
        
<property name="aclManager">
            
<ref local="aclManager"/>
        
</property>
        
<property name="requirePermission">
            
<list>
                
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
                
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/>
            
</list>
        
</property>
    
</bean>
  1. ACL_READ指的是这个Voter对哪些SecurityConfig起作用,我们可以把ACL_READ配置在想要拦截的Method上。比方说我们要拦截readOrder这个方法,以实现ACL控制,可以这样配置。
    orderManager.readOrder=ACL_READ
  2. processDomainObjectClass指出哪些DomainObject是要进行ACL校验的。
  3. aclManager是一个比较重要的概念,主要负责在权限列表中根据用户和DomainObject取得acl列表。
  4. requirePermission指出要进行这个操作必须具备的acl权限,比方说read操作就必须有ADMINISTRATION或READ两个权限。

其实整个过程看下来比较清晰,下面来看一下AclManager如何配置。

    <!-- ========= ACCESS CONTROL LIST LOOKUP MANAGER DEFINITIONS ========= -->

    
<bean id="aclManager" class="org.acegisecurity.acl.AclProviderManager">
        
<property name="providers">
            
<list>
                
<ref local="basicAclProvider"/>
            
</list>
        
</property>
    
</bean>

    
<bean id="basicAclProvider" class="org.acegisecurity.acl.basic.BasicAclProvider">
        
<property name="basicAclDao">
            
<ref local="basicAclExtendedDao"/>
        
</property>
    
</bean>

    
<bean id="basicAclExtendedDao" class="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl">
        
<property name="dataSource">
            
<ref bean="dataSource"/>
        
</property>
    
</bean>

很明显ACLManager继承了Acegi的一贯风格,Provider可以提供多种取得ACL访问列表的途径,默认的是用basicAclProvider在数据库中取得。既然提到了数据库,那我们就来看一下Acegi默认提供的ACL在数据库里的保存表结构:

CREATE TABLE acl_object_identity (
id 
IDENTITY NOT NULL,
object_identity VARCHAR_IGNORECASE(
250NOT NULL,
parent_object 
INTEGER,
acl_class VARCHAR_IGNORECASE(
250NOT NULL,
CONSTRAINT unique_object_identity UNIQUE(object_identity),
FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id)
);
CREATE TABLE acl_permission (
id 
IDENTITY NOT NULL,
acl_object_identity 
INTEGER NOT NULL,
recipient VARCHAR_IGNORECASE(
100NOT NULL,
mask 
INTEGER NOT NULL,
CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient),
FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id)
);
  1. acl_object_identity表存放了所有受保护的domainObject的信息。其中object_identity字段保存了domainObject的class和id,默认的保存格式是:domainClass:domainObjectId。
  2. acl_permission 就是ACL权限列表了,recipient 是用户或角色信息,mask表示了这个用户或角色对这个domainObject的访问权限。注意这些信息的保存格式都是可以根据自己的需要改变的。

这样读取和删除的时候Acegi就能很好的完成拦截工作,但是读取一个List的时候,如何才能把该用户不能操作的domainObject剔除掉呢?这就需要afterInvocationManager来完成这个工作。下面来看下配置:

    <!-- ============== "AFTER INTERCEPTION" AUTHORIZATION DEFINITIONS =========== -->

    
<bean id="afterInvocationManager" class="org.acegisecurity.afterinvocation.AfterInvocationProviderManager">
        
<property name="providers">
            
<list>
                
<ref local="afterAclCollectionRead"/>
            
</list>
        
</property>
    
</bean>
    
<!-- Processes AFTER_ACL_COLLECTION_READ configuration settings -->
    
<bean id="afterAclCollectionRead" class="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider">
        
<property name="aclManager">
            
<ref local="aclManager"/>
        
</property>
        
<property name="requirePermission">
            
<list>
                
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
                
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/>
            
</list>
        
</property>
    
</bean>

afterAclCollectionRead会在拦截的方法执行结束的时候执行。主要的作用就是在返回的List中挨个检查domainObject的操作权限,然后根据requirePermission来剔除不符合的domainObject。
posted @ 2006-06-17 00:20 差沙 阅读(2257) | 评论 (4)编辑 收藏
acegi1.0发布,其实有点出乎意料,因为我一向认为acegi的代码已经相当稳定了,但是acegi力求精益求精,从新版还是能看到不少实用的改动和升级。这里简单分析一下。

[SEC-183] - Avoid unnecessary HttpSession creation when using Anonymous and Remember-Me authentication

以前如果使用HttpSessionContextIntegrationFilter的话,不管你是否需要创建session,他都会给你创建。这在一些Base验证的时候是多余的。现在加上了forceEagerSessionCreation,在创建session的时候做了控制。

[SEC-29] - Save POST request parameters before redirect

在前几个版本出现这个问题,如果实现了登陆自动跳转,acegi仅仅是简单记录了URL,没有深入的纪录信息。新版本中acegi不仅仅是保持POST中的数据不会丢失,request里面的东西几乎全都序列化保存下来了,实现可以看看SavedRequest。

[SEC-40] - HibernateDao.scroll() performance

[SEC-92] - Hibernate ACL implementation

这个比较激动的改进在1.0的源码中没有找到,看alex的意思好像是仅仅提供各演示,目的是为了生成数据脚本方便点。(其实这个还真的没法做成特别通用的,毕竟每个人的ACL实现都有可能不同)

[SEC-147] - BasicAclEntryAfterInvocationProvider should support processDomainObjectClass

对List进行ACL交验的时候,会把第一个元素取出,看看是否AssignableFrom这个processDomainObjectClass ,算是做一下安全检查吧。

[SEC-172] - Allow SimpleAclEntry to take 'null' as recipient constructor argument

其实应该是不允许recipient 为空。

[SEC-187] - inHttp & inHttps not fully utilized in AuthenticationProcessingFilterEntryPoint

[SEC-191] - AclTag class should use the BeanFactoryUtils.beanNamesForTypeIncludingAncestors method to search for the AclManager

AclTag在寻找AclManager 时候会更加灵活了,得益于spring的强大。

<明天继续吧。。。。>

[SEC-194] - RememberMeServices should be available when using BasicAuth logins

[SEC-195] - Create Acegi-backed CAS3 AuthenticationHandler

[SEC-196] - Update web site and documentation to reference JA-SIG CAS

[SEC-203] - Allow setting the AuthenticationManager onto the ConcurrentSessionController for inverted dependency

[SEC-204] - Better detection of malformed text in FilterInvocationDefinitionSourceEditor

[SEC-205] - Allow multiple URLs in DefaultInitialDirContextFactory

[SEC-206] - TokenBasedRememberMeServices using context root when setting cookie paths (inc code)

[SEC-207] - Implement countermeasures against session attacks

[SEC-209] - Make AbstractProcessingFilter.eventPublisher field protected

[SEC-217] - Improve Siteminder Filter

[SEC-220] - Allow ExceptionTranslationFilter to not catch exceptions

[SEC-221] - AbstractProcessingFilter.onPreAuthentication exceptions should be caught

[SEC-224] - Make Authentication.getPrincipal() for CAS return the UserDetails

[SEC-229] - Allow redirects to external URLs in AbstractProcessingFilter

[SEC-231] - Add another DefaultLdapAuthoritiesPopulator.getGroupMembershipRoles

[SEC-234] - Allow WebAuthenticationDetails pluggable implementations

[SEC-236] - JbossAcegiLoginModule to use ApplicationContext interface

[SEC-238] - Add AuthenticationException to AbstractProcessingFilter.onUnsuccessfulAuthentication method signature

[SEC-242] - Logger in AbstractProcessingFilter

[SEC-244] - Column names instead of indexes for org.acegisecurity.userdetails.jdbc.JdbcDaoImpl

[SEC-246] - Enable late-binding of UserDetailsService on DaoAuthenticationProvider

[SEC-247] - Allow to specify resources that shouldn't be filtered in FilterChainProxy

[SEC-251] - DefaultLdapAuthoritiesPopulator: Add filter argument {1} for username as in Tomcat JNDIRealm

[SEC-255] - Reorder AuthenticationProcessingFilter to create HttpSession before delegating to AuthenticationDetailsSource

[SEC-257] - ExceptionTranslationFilter to use strategy interface for AccessDeniedException handling

[SEC-259] - AccessDecisionVoter: typo in JavaDoc

[SEC-260] - AbstractAccessDecisionManager and loggers

[SEC-262] - AbstractAccessDecisionManager needs standard handling ifAllAbstainDecisions

[SEC-264] - Introduction of LdapUserDetails and changes to LdapAuthenticator and LdapAuthoritiesPopulator interfaces

[SEC-276] - Restructure reference guide

posted @ 2006-06-01 23:05 差沙 阅读(545) | 评论 (0)编辑 收藏
这两天在springside受白衣的影响开始关注drools。说他是平民的脚本引擎一点都不假,使用起来极为方便,本来以为网上应该有不少的讲解了,但是发现几乎全是针对2.0版本讲解的。而drools加入jboss后有了质的变化,下面来看一下最新的3.0使用起来有什么不同:

首先我们要取得rule,规则引擎、规则引擎,取得规则是必要的。
private static RuleBase readRule() throws Exception {
        
//read in the source
        Reader source = new InputStreamReader( DroolsTest.class.getResourceAsStream( "/aclcreat.drl" ) );
        
        
//optionally read in the DSL (if you are using it).
        Reader dsl = new InputStreamReader( DroolsTest.class.getResourceAsStream( "/mylang.dsl" ) );

        
//Use package builder to build up a rule package.
        
//An alternative lower level class called "DrlParser" can also be used
        
        PackageBuilder builder 
= new PackageBuilder();

        
//this wil parse and compile in one step
        
//NOTE: There are 2 methods here, the one argument one is for normal DRL.
        
//builder.addPackageFromDrl( source );

        
//Use the following instead of above if you are using a DSL:
        builder.addPackageFromDrl( source, dsl );
        
        
//get the compiled package (which is serializable)
        Package pkg = builder.getPackage();
        
        
//add the package to a rulebase (deploy the rule package).
        RuleBase ruleBase = RuleBaseFactory.newRuleBase();
        ruleBase.addPackage( pkg );
        
return ruleBase;
    }

这里在官方的例子基础上做了自己的实现(其实什么都没改)。

可以看到,第一步是取得文件IO,这个文件就是我们要写得规则脚本,这个等下再说,大家可以假象一下脚本是个什么样子,现在只说怎么在程序中取得Rule。
接下来,是使用Builder取得一个package,既然builder都上来了说明能输入的脚本不止一个了。用addPackageFromDrl向这个builder压缩机里面输入脚本,当然还有另外一个文件dsl,这个后面再说。利用builder取得package。

最后构造一个BaseRule,利用Factory取得的时候是有选择的,RuleBaseFactory.newRuleBase(int type)其中的type可以为不同的Algorithm,有RETE和Leaps 两种。对这两种Algorithm的具体解释可以参看 http://citeseer.ist.psu.edu/context/505087/0 或是 drools的文档,其实我也不太懂。

把刚才的package添到ruleBase里面一个Rule就大功告成了。

接下来看看怎么执行它:
            WorkingMemory workingMemory = ruleBase.newWorkingMemory();
            
            
//go !
            Order order = new Order();
            order.setId(
1);
            order.setName(
"testOrder");
            order.setTotlePrice(
10);
                        
            User user 
= new User();
            user.setName(
"testAdmin");
            user.setAuth(
"USER_ADMIN");
            List
<String> roles = new ArrayList<String>();
            roles.add(
"ADMIN");
            user.setRoles(roles);
            

            User user1 
= new User();
            user1.setName(
"testUser");
            user1.setAuth(
"USER_USER");
            List
<String> roles1 = new ArrayList<String>();
            roles1.add(
"USER");
            user1.setRoles(roles1);
            
            workingMemory.assertObject(order);
            workingMemory.assertObject(user);
            workingMemory.assertObject(user1);
            
            workingMemory.fireAllRules();        
            
            List
<AclEntry> acls = workingMemory.getObjects(AclEntry.class);

用ruleBase生成一个WorkingMemory,WorkingMemory是Rule的执行引擎,装载rule和事实(很重要的概念),并统一执行他们。接下来我就在写我的事实,事实是什么,事实就是今天是什么天?订单总价多少?就是要告诉脚本的java对象。然后把事实一一压入WorkingMemory这个大压缩机。就瞧好吧。

OK可以执行了,fireAllRules!(真TM,COOL的名字)。当然有全部执行就有部分执行。你可以把规则分组,然后按组执行,或是指定rule的名字来执行(这里还是大家自己看看吧)。

???究竟执行了什么。当然是执行了我们的脚本,脚本在这里、看看它可不是xml了:
#created on: 2006-5-19
package com.sample;

#list any 
import classes here.

import com.sample.domain.Order;
import com.sample.domain.User;

import com.sample.AclEntry;
#expander mylang.dsl

#declare any global variables here

rule 
"Order TotlePrice more than $1000"    
    when
        #conditions
        $order : Order( totlePrice 
> 1000 )
        $user : User( roles contains 
"ADMIN" , $userName : name)
    then 
        #actions
        System.out.println(
"More Than");
        
assert(new AclEntry($order, $user, 1));
end

rule 
"Order TotlePrice less or equl than $1000"    
    when
        #conditions
        $order : Order( totlePrice 
<= 1000 )
        $user : User( $userName : name )
    then 
        #actions
        System.out.println(
"Less Than");
        
assert(new AclEntry($order, $user, 2));
end

每一个rule就是一个规则,所有的事实要一一过一遍这些规则。when是规则提出的条件,如果哪个事实符合这个条件,就进入then的环节,进行相应的处理。

分析一下条件:$order : Order( totlePrice > 1000 )。一看就知道是总价超过1000的订单。$order是把这个订单邦定,后面可以使用。
分析一下then: System.out.println就不解释了。assert(new AclEntry($order, $user, 2)); 这里的assert的意义就是告诉WorkingMemory一个事实,其实跟前面的加入事实一个道理。打个比方,如果有闪电,那么就有雷。

这样走完一个rule后大家很容易发现,其实是根据订单和用户的角色不同产生了不同的acl,然后我要拿到这些由事实得到的事实。

List<AclEntry> acls = workingMemory.getObjects(AclEntry.class);
这样就能在workingMemory里面掏出我们需要的事实来,新鲜出炉的哦。

相当粗略的讲了一下drools,目的是希望大家都来了解一下,共同学习。
posted @ 2006-05-28 20:53 差沙 阅读(2787) | 评论 (9)编辑 收藏

以前不写blog,很多技术学完就忘了。总是反复请教google,惭愧的很。决心以后记录自己技术的点点滴滴了。

posted @ 2006-05-27 22:52 差沙 阅读(454) | 评论 (0)编辑 收藏
仅列出标题
共2页: 上一页 1 2 
这家伙很懒,但起码还是写了一句话。

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿(8)

随笔分类

随笔档案

文章分类

搜索

  •  

最新评论

阅读排行榜

评论排行榜