周末參加了兩次活動,一次是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 閱讀(624) |
評論 (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 閱讀(2291) |
評論 (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 閱讀(6719) |
評論 (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 閱讀(4155) |
評論 (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 閱讀(4546) |
評論 (0) |
編輯 收藏