周末参加了两次活动,一次是BJUG,一次是BEA UG。
BJUG的活动相对轻松些,没有固定的topic,每人都要写一个topic然后大家投票选择。每个人都可以发言,在topic演讲者演讲的时候可以随时举手打断提出自己的疑问和看法。
第一个topic是关于tw的Pre-Sales,很有意思的Sale。其实BUG和项目延期一直是开发中让人头痛的两个问题。现在我就饱受这样的苦恼。解决的方法其实也很简单:测试和快速迭代。冰云从经济学的基础上分析了快速版本迭代的收益。问题在于:对一个接近完成而又测试覆盖率很低的项目还有敏捷的必要吗?如何在现有工作中的团队里引入敏捷?这其实也是我想提出的问题。个人觉得如果从项目一开始就敏捷会容易的多。接着这个话题,讲了他所在团队如何进行敏捷。每天的日构建,每周固定一次的版本迭代,和客户频繁的交互需求,良好的项目进度和反馈。这个团队敏捷的让人嫉妒。我觉得敏捷的推行除去个人的部分关键在于领导的决心,开始的不适应是肯定的,领导能不能坚持才最重要。
第二个topic是郑晔介绍他的开源项目xruby。我对ruby并不了解。理解xruby是把ruby转化为java的class文件,同时提供了运行期的一些扩展。因为ruby是动态语言,所以这些扩展也是必须的。很有意义的项目,用ruby快速开发,然后通过xruby编译为java,最后部署到java服务器里。只是我后来想想,与其给ruby做辛苦的转换,不如通过这些扩展给java本身提供更多的动态特性。也许出现一个框架性的东西也有可能。
xruby的主页最后一个topic也就比较随意了,因为时间不够了。讨论了个人的职业发展。确实要和自己的性格结合起来。
遗憾就是自己一直都没有一个很好的topic,工作中的总结太少,或者说自己知道的东西还很少。下次一定努力。
BEA UG。其实很早就和霍泰稳说好要去帮忙的,路上堵车,到的时候都快一点了,也是很不好意思的。认识了胡长城,认识了李文章,碰见了莫映,都是工作流方面的专家。第一个演讲嘉宾是李文章,当时我也提出了自己的问题,有些问题上还需要继续请教,应该还会有机会。莫映,聊了一会,他们做的东西和我们竟然非常相似,寒一个。其实现在谁都在向一个平台产品发展。胡长城,约好了要到我们公司来看看。呵呵,最后抽奖时还幸运的被抽到一个背包,真有意思。
posted @
2007-03-26 18:17 ronghao 阅读(613) |
评论 (0) |
编辑 收藏
昨天参加了BEA UG的活动。其中第二场是BEA罗振东先生的BPM讲解。因为公司一直就是做工作流的,所以对BPM这个概念一直是非常的关注,但是一直也是搞不太清楚Workflow与BPM的关系,总是以为BPM是对WorkFlow的一层包装而已,是新瓶装老酒。在听罗振东先生演讲的过程中,我开始有了一些自己的认识。
一句话说:
BPM是建立在EAI基础上的工作流。和工作流不同,BPM关注的是一个很完整概念上的业务流程,这个业务流程可能需要横跨多个IT系统,这些系统通过某种方式暴露出流程中所需要的服务(webservice是一种选择),BPM推动这个流程的流转。同时,相对于以往的工作流单纯的流程流转,BPM提供了更多,包括流程仿真,过程分析、过程优化等等。意思就是,在某个流程运行一段时间以后,BPM会基于数据提供对该流程的分析(数据挖掘?),从而能够基于这些分析提供对上层管理决策的支持。有点像运筹学。
那么,一个工作流厂商是否可以很容易的研发出BPM的产品?答案是不行。看看哪些BPM的厂商,无一不是在EAI方面有很多经验的大公司。所以,BPM实施的关键还是要建立在EAI实施的基础上。至于BPM和SOA,如果以前的系统是基于SOA架构的,那自然EAI起来是会更加容易,BPM理所当然是拥抱SOA的。(BEA的产品没用adapter)
那么当前工作流的发展方向呢?个人认为可以从BPM的功能里找到一些线索,那就是流程仿真,过程分析、过程优化。比如一个请假申请流程,统计一下,在哪个节点的办理效率最低,哪些节点在实际中不是必须等等,当然这些都是工作流本身基于流程的数据进行的独立的分析,有点决策的意思在里面。
呵呵,个人的一些浅见。希望多批评。
posted @
2007-03-26 14:27 ronghao 阅读(2281) |
评论 (1) |
编辑 收藏
一开始我把控制数据权限写在业务里,以订单管理为例,先讨论一个最简单的情况。管理员可以看所有的订单,而用户只能看自己的订单。这里的管理员是一个角色。我会这么写(一些次要代码都省略了):
List getOrders(String userId){
String sql;
Role role=orgService.getRoleForUser(userId);
if(("admin").equals(role.getName()))
sql="select * from order";
else
sql="select * from order where author="+userId;
Object o=excuteSql(sql);
return excute(o);
}
恩,不错,很好的完成了权限控制。过了没多久,公司发展了,老板增加了人手,老板发话了,我要设置区域管理员分管不同区域的订单。管理员分为北京地区管理员,上海地区管理员,其他地区管理员和总管理员。怎么办?修改代码吧
List getOrders(String userId){
String sql;
Role role=orgService.getRoleForUser(userId);
if(("admin").equals(role.getName()))
sql="select * from order";
else if("bjadmin").equals(role.getName()))
sql="select * from order where area='beijing'";
else if("shadmin").equals(role.getName()))
sql="select * from order where area='shanghai'";
else if("qtadmin").equals(role.getName()))
sql="select * from order where area='qita'";
else
sql="select * from order where author="+userId;
Object o=excuteSql(sql);
return excute(o);
}
恩恩,这就搞定了,可怎么也感觉不爽,也许该做点什么。一堆if/else权限判断让人心烦,再写个类把这些sql管理起来好了,那就动手吧
public class SqlManager{
String getSql(String userId){
String sql;
Role role=orgService.getRoleForUser(userId);
if(("admin").equals(role.getName()))
sql="select * from order";
else if("bjadmin").equals(role.getName()))
sql="select * from order where area='beijing'";
else if("shadmin").equals(role.getName()))
sql="select * from order where area='shanghai'";
else if("qtadmin").equals(role.getName()))
sql="select * from order where area='qita'";
else
sql="select * from order where author="+userId;
return sql;
}
}
这样把权限判断移到SqlManager里,业务代码就清爽了很多,再增加管理员就修改SqlManager好了
List getOrders(String userId){
String sql=sqlManager.getSql(userId);
Object o=excuteSql(sql);
return excute(o);
}
呵呵,看起来还不错。但是等等,我们的业务方法为什么需要userId这个参数呢,是啊是啊,权限判断用到了它,但是那和我业务又有什么关系呢,不爽。现在AOP不是很流行吗,你不用AOP怎么能说明你技术高呢?快用吧快用吧,用不着也要想着方法用。
业务方法简化为
List getOrders(){
String sql="";
Object o=excuteSql(sql);
return excute(o);
}
对excuteSql方法我们来AOP一下,注入权限判断过的sql.嘿嘿,技术水平又一次得到了显现。业务方法是简单了,可我的SqlManager倒是复杂了,还是很不幸福。咋办?用个配置文件吧,hibernate不是老鼓励我们把sql写在配置文件里吗?
<xml>
<sql role="admin">select * from order</sql>
<sql role="bjadmin">select * from order where area='beijing'</sql>
<sql role="shadmin">select * from order where area='shanghai'</sql>
<sql role="qtadmin">select * from order where area='qita'</sql>
<sql role="none">select * from order where author=?</sql>
</xml>
这样SqlManager就可以把行数缩小了,就可以敏捷一点了。
public class SqlManager{
String getSql(String userId){
String sql;
Role role=orgService.getRoleForUser(userId);
sql=getSqlfromXml(role.getName());
return sql;
}
String getSqlfromXml(String rolename){
.
}
} 以后再增加权限连类都不用修改了,改xml好了。等等,你是不是把问题太简单化了。现在不仅仅是订单,货物也要这么分区域管理。
不错,我们应该想着通用一下了。这样,把SqlManager抽象一下
String abstract getSqlfromXml(String rolename);
然后做几个子类好了 OrderSqlManager, GoodsSqlManager .
可是,哥们,书上说,要面向接口编程,你这样不太好吧。没事,再接口一下:
public interface SqlManagerInterface{
String getSql(String userId);
}
还是没法用啊。也许现在可以看看acegi的provider机制了,把这一大堆SqlManager全部作为provider,根据不同的模块选择不同的provider,统一拦截excuteSql方法,生成不同的sql到数据库执行。xml不爽?db也可以。然后,再然后呢?改你的类名,重构,和acegi整合一下。
呵呵,完全是个人的一些想法,希望多批评提提意见。我想表达的意思是:也许把数据权限再抽象一些,以组件的形式来减少侵入是可以做到的。
posted @
2007-03-23 18:35 ronghao 阅读(6705) |
评论 (6) |
编辑 收藏
什么是权限系统,权限系统究竟在整个系统中起到什么作用,或者说权限系统必须提供哪些功能?
谈谈个人的看法,我认为授权和验证是一个权限系统最基本的功能,而一个更完善的权限系统会增加上如何验证和验证后如何处理这两种功能。
对于一个权限系统的基本元素,我觉得只有两个:Principal(权限主体),权限。用户,用户组,角色等都可以抽象为权限主体这个概念;而权限则是资源+操作的抽象。权限可分为两种,一种纯粹就是资源,没有操作,也可以认为是默认操作;另外一种就是带操作的资源。授权本身没有什么说的,赋予权限主体权限;验证简单的说就是传入权限主体和当前操作需要的权限,然后验证返回说可以操作或是不可以操作yes/no。同时要注意的是验证的时候需要一个类(接口)来返回用户当前所拥有的权限,然后与当前操作需要的权限进行比对,最后返回比对结果。
以acegi为例,下面分开来说明:
首先,acegi是没有授权功能的,它简单把权限主体与资源配置在了xml里,当然这也带来了可扩展性,可以看一个配置:
/index.jsp=ROLE_ANONYMOUS,ROLE_USER
/hello.htm=ROLE_ANONYMOUS,ROLE_USER
/logoff.jsp=ROLE_ANONYMOUS,ROLE_USER
/switchuser.jsp=ROLE_SUPERVISOR
/j_acegi_switch_user=ROLE_SUPERVISOR
/acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER
/**=ROLE_USER
/index.jsp,/hello.htm这些URL都是资源,因为它默认的操作就是访问,所以访问特定URL就成了一个权限,ROLE_ANONYMOUS,ROLE_USER无疑就是权限主体。
Acegi对方法访问权限的配置也是和上面一样的。
然后,看看acegi的验证,其实它的验证就是一个个的Voter,当你访问一个方法时,由它获得当前用户和需要的权限(就是该方法),然后返回yes/no。
OK,这就是一个完整的权限系统了。当然acegi如果仅仅提供上面两个基本功能,它是不会向现在这样成功的,它最大的亮点就是提供了如何验证和验证后如何处理这两种功能。对url它提供了filter,方法也一样提供了Interceptor,这就是如何验证,和我们在action里验证没有任何区别,只是换了一种方式而已。然后它验证后的处理方式:yes就继续往下执行,no就抛出异常最后再处理这个异常。我们在执行每个action前同样也可以做到这一点。
这里着重要提到的是数据权限。比如说A只能对部门A下的数据有查看权限,B可以对部门A的数据有修改权限和对部门B的数据的查看权限。
看起来这个需求是比较麻烦的,但是我们可以按上面说的拆分以下:
资源1:部门A下的数据 ;资源2:部门B下的数据
操作1:查看; 操作2:修改
产生四种权限:
权限1:资源1+操作1 ;权限2:资源1+操作2;权限3:资源2+操作1;权限4:资源2+操作2
权限主体A拥有权限:1 ; 权限主体B拥有权限:2,3
这样就非常清晰了。
总结:我始终认为拥有授权和验证功能就完成了一个完整的权限系统。至于如何去校验权限,不管你是filter还是aop那是具体实现时的细节,采取的不同策略。而验证后的处理则是和不同策略相对应的。在你考虑设计一个权限系统时首先考虑的是如何实现授权和验证,而不是一开始就考虑我是采取filter,还是aop,还是动态改变sql。
谢谢wolfsquare,它的
blog相关
posted @
2007-03-21 19:04 ronghao 阅读(4144) |
评论 (1) |
编辑 收藏
本文假设你对Acegi其他部分已经比较熟悉。Acegi 的ACL控制是建立在对相应业务方法拦截的基础上的。这里以Acegi自带的contacts例子来说明。
先看看总的配置:
<bean id="contactManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="businessAccessDecisionManager"/></property>
<property name="afterInvocationManager"><ref local="afterInvocationManager"/></property>
<property name="objectDefinitionSource">
<value>
sample.contact.ContactManager.getAll=AFTER_ACL_COLLECTION_READ
sample.contact.ContactManager.getById=AFTER_ACL_READ
sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN
sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN
</value>
</property>
</bean>
该拦截器实现了org.aopalliance.intercept.MethodInterceptor接口。在方法被调用之前,拦截器会先调用 AuthenticationManager判断用户身份是否已验证,然后从objectDefinitionSource中获取方法所对应的权限,再调用AccessDecisionManager来匹配用户权限和方法对应的权限。如果用户没有足够权限调用当前方法,则抛出 AccessDeniedException使方法不能被调用。方法调用后会调用AfterInvocationManager对返回的结果进行再次处理。下面依次说明。
AccessDecisionManager的配置:
<bean id="businessAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref local="roleVoter"/>
<ref local="aclContactReadVoter"/>
<ref local="aclContactDeleteVoter"/>
<ref local="aclContactAdminVoter"/>
</list>
</property>
</bean>
AccessDecisionManager接口有decide()和support()方法。decide()方法决策是否批准通过即方法是否容许调用,如果没抛出AccessDeniedException则为允许访问资源,否则拒绝访问。support()方法是根据配置属性和受保护资源的类来判断是否需要对该资源作出决策判断。
AccessDecisionManager的 decisionVoters属性需要一个或多个Voter(投票者),Voter必须实现AccessDecisionVoter 接口。Voter的工作是去匹配用户已拥有的权限和受保护的资源要求的权限,在该资源有相应权限的情况下,如果匹配则投允许票,否则投反对票。
allowIfAllAbstainDecisions属性表示是否允许所有都弃权时就通过。Voter的实现类RoleVoter在当受保护资源的名字由ROLE_开始时才参与投票。
AccessDecisionManager有三个实现类,功能各不相同:
AffirmativeBased: 当至少有一个Voter投允许票时才通过
UnanimousBased: 没有Voter投反对票时才通过
ConsensusBased: 当所有Voter都投允许票时才通过
下面列出一个Voter的配置:
<bean id="aclContactDeleteVoter" class="org.acegisecurity.vote.BasicAclEntryVoter">
<property name="processConfigAttribute"><value>ACL_CONTACT_DELETE</value></property>
<property name="processDomainObjectClass"><value>sample.contact.Contact</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.DELETE"/>
</list>
</property>
</bean>
上面第一个配置里有这么一行:sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
所以在调用sample.contact.ContactManager.delete这个方法时aclContactDeleteVoter会参与投票,它会获得sample.contact.Contact这个对象(这个对象从delete方法的参数中获得),然后通过aclManager去获得当前用户对该Contact实例的ACL权限,最后拿这个权限与我们需要的权限比对,我们配置需要的权限是org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION和org.acegisecurity.acl.basic.SimpleAclEntry.DELETE。如果我们通过aclManager获得的权限包括这两个配置的权限之一,Voter就投容许票,方法容许调用。如果不包括,那对不起,反对票,AccessDecisionManager就会抛出AccessDeniedException。方法拒绝调用。
AclManager的配置:
<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是整个ACL中一个很核心的概念,它包含了两个方法AclEntry[] getAcls(Object domainInstance)和
AclEntry[] getAcls(Object domainInstance, Authentication authentication)。在了解这两个方法前,我们先了解AclEntry这个对象。AclEntry只是一个接口,系统中一般都是造型为BasicAclEntry。它包括了这个Entry所保护的domainObject instance(这里是Contact),实际它实现上是以AclObjectIdentity来替代这个domainObject的(domainClass+domainObjectId);它包括了谁(Recipient)拥有这个domainObject instance以及他所对这个domainObject instance的操作权限(mask)。
一个domainObject instance对应了多个AclEntry,比如一条通讯录张三可以查看,而李四可以管理,一个Contact instance就对应了两个AclEntry,第一个AclEntry包含信息:所保护的domainObject(Contact),谁(张三),权限(查看);第二个AclEntry包含信息:所保护的domainObject(Contact),谁(李四),权限(管理)。
这样AclManager的两个方法就很好理解了getAcls(Object domainInstance)返回所有这个domainInstance所对应的权限信息,
getAcls(Object domainInstance, Authentication authentication)在第一个方法返回结果的基础上做了过滤,过滤出和authentication(当前用户)相关的权限信息。如果当前用户是张三,则返回与张三对应的记录。
这样Acegi就会拦截业务方法发挥相应的作用,但是在业务方法返回一个List或是单个domainObject instance的时候,同样也是需要把用户没有权限查看的domainObject instance过滤掉的,这时就要用afterInvocationManager了,
看配置:
<bean id="afterInvocationManager" class="org.acegisecurity.afterinvocation.AfterInvocationProviderManager">
<property name="providers">
<list>
<ref local="afterAclRead"/>
<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>
<!-- Processes AFTER_ACL_READ configuration settings -->
<bean id="afterAclRead" class="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationProvider">
<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会对配置AFTER_ACL_COLLECTION_READ的方法进行拦截,这里是sample.contact.ContactManager.getAll方法,它会遍历方法返回的domainObject,然后挨个通过aclManager判断当前用户对domainObject的权限,如果和需要的权限不和,则过滤掉。呵呵,传说中的虎牙子就在此时产生了!
afterAclRead则依次类推。
参考了ss wiki里相关的文档,特别是差沙和cac的文档,写的相当好。另外acegi的代码也是相当的易读。
posted @
2007-03-20 19:07 ronghao 阅读(4532) |
评论 (0) |
编辑 收藏