af:selectManyChoice 是一個(gè)下拉多選框,如下圖:
這個(gè)組件使用起來(lái)不是那么容易,它需要將表單中的一個(gè)column的attribute 和 組件中的selectitems之間互相轉(zhuǎn)換。
正好今天有個(gè)客戶問(wèn)起,興致所致就寫了簡(jiǎn)單的例子,下載。
我不詳細(xì)展開講了,簡(jiǎn)要描述下:
1) af:selectManyChoice 的value attribute 綁定到一個(gè)managed bean的get和set 方法上
2) af:selectManyChoice的autosumit=true, immediate=true
3) 可選擇值為一個(gè)Static List的View Object, 在頁(yè)面的binding中配置該lov,并且在selectItems的value屬性綁定到該lov。
4)在af:selectManyChoice 的value對(duì)應(yīng)的manage bean的get/set方法中,將頁(yè)面選擇的index對(duì)應(yīng)到lov的具體值并存放到數(shù)據(jù)庫(kù),讀取則反之。
下載包的密碼是: gene.xujin@gmail.com
在ADF頁(yè)面上有兩種EL的引用af:tree,af:treeTable,af:table的數(shù)據(jù)集合的方式。一種是#{row.attributeName},另一種是#{row.bindings.attributeName.inputValue}。 這兩者的區(qū)別是:前者是只讀的 而后者是可讀寫的。前者這是將該屬性以字符串的形式返回,該EL的返回值是一個(gè)String類型,后者則不同,#{row.bindings.attributeName}是一個(gè)Attribute Binding, 在運(yùn)行時(shí)是一個(gè)FacesCtrlAttrsBinding 類的實(shí)例,該類是框架內(nèi)部使用的類,繼承了JUCtrlAttrsBinding。所以只要表格需要更新,就應(yīng)該使用{row.bindings.attributeName.inputValue},另外一個(gè)例子是#{node.attributeName},node表示了樹組件中的一個(gè)節(jié)點(diǎn),可以對(duì)應(yīng)到table組件的row。
ADF具有一個(gè)非常健壯的驗(yàn)證框架。表單驗(yàn)證可以在各個(gè)層次上進(jìn)行(view, model和business service)。如果需要詳細(xì)研究驗(yàn)證機(jī)制,請(qǐng)參考Fusion Developer's Guide for Oracle Application Development Framework.
在現(xiàn)實(shí)的開發(fā)過(guò)程中,經(jīng)常有一些頁(yè)面需要暫時(shí)的跳過(guò)默認(rèn)的驗(yàn)證,本文旨在討論下常見(jiàn)的use case:
1. Keep immediate=true
一個(gè)最常用的方式就是將UICOMPONENT的immediate屬性設(shè)置為true,這樣可以將對(duì)組件的處理提前到Apply Request Values階段。關(guān)于immediate屬性可以參考 wiki
典型用例:cancel button,在取消按鈕點(diǎn)擊時(shí),頁(yè)面是不需要驗(yàn)證提交的表單的。可以將按鈕組建的immediate屬性設(shè)為'true'來(lái)實(shí)現(xiàn)這個(gè)功能。 如果是一個(gè)reset按鈕,那么除了immediate屬性需要設(shè)為'true‘外,最好還要調(diào)用下相關(guān)其他input組件的resetValue()方法,來(lái)恢復(fù)初始狀態(tài)。
2. SkipValidation
另外一種方式,是設(shè)置pagedefinition 文件的SkipValidation 屬性。該屬性可以控制跳過(guò)model層的驗(yàn)證。需要注意的是,這個(gè)屬性只對(duì)entity level的validation rule起作用,對(duì)于entity object的attribute上定義的validation rule是沒(méi)有作用的。另外對(duì)于瀏覽器端的驗(yàn)證(javascript),這個(gè)屬性也是沒(méi)法控制的。
這個(gè)屬性可以在每個(gè)頁(yè)面對(duì)應(yīng)的Page Definition 文件中找到,如下圖:

這個(gè)屬性可能有的值,見(jiàn)下圖:

a. SkipValidation=true
設(shè)為true則在form提交過(guò)程中會(huì)跳過(guò)Model層的驗(yàn)證,直到commit的時(shí)候才會(huì)進(jìn)行驗(yàn)證。
典型場(chǎng)景:如需要在一張表中輸入很多行信息,用戶可能希望可以在頁(yè)面中以任何順序輸入信息,不希望每次table selection事件都會(huì)觸發(fā)驗(yàn)證。
b. SkipValidation=custom
設(shè)為custom的時(shí)候,需要開發(fā)人員實(shí)現(xiàn)一個(gè)oracle.binding.BindingContainerValidator的接口的類,并用EL配置,如圖:

這個(gè)選擇項(xiàng)給了開發(fā)人員更多的空間來(lái)應(yīng)對(duì)復(fù)雜的業(yè)務(wù)需求,開發(fā)人員可以自定義如何對(duì)表單進(jìn)行驗(yàn)證。
c. SkipValidation = skipDataControls
skipDataControls 選擇項(xiàng)的意思是會(huì)跳過(guò)data control這個(gè)level的驗(yàn)證,或者說(shuō)是事務(wù)級(jí)別的驗(yàn)證。它只會(huì)對(duì)當(dāng)前的binding container中的iterator的current row進(jìn)行驗(yàn)證,而不會(huì)對(duì)其他不相關(guān)的提交的數(shù)據(jù)進(jìn)行驗(yàn)證。
典型場(chǎng)景: 在一個(gè)頁(yè)面上使用了多個(gè)data control,提交一個(gè)data control不會(huì)驗(yàn)證同一個(gè)頁(yè)面中的其他data control中的數(shù)據(jù)。
d. SkipValidation=false
默認(rèn)值,不會(huì)跳過(guò)驗(yàn)證。
在刪除某幾條數(shù)數(shù)據(jù)的時(shí)候,可以用多選項(xiàng)選中多條記錄,再點(diǎn)擊刪除按鈕的時(shí)候需要做一下判斷是否確認(rèn)刪除,如果一條記錄都沒(méi)有刪除,那么就不彈出確認(rèn)的對(duì)話框。
<script>
function CheckAll(srcElem, do_check){
??????? if(typeof(srcElem)=='undefined') return;
??????? var cnt = (typeof(srcElem.length)!='undefined')? srcElem.length : 0;
??????? if(cnt){
??????????????? for(var i=0;i<cnt; i++)
??????????????????????? srcElem[i].checked = do_check;
??????? }else
??????????????? srcElem.checked = do_check;
}
function deleteYN(){
//if(YN(this.form.elements['delBox'], this.checked)){if(!confirm('確實(shí)要?jiǎng)h除嗎?')) return false;}else return false;
if(typeof(form1.elements['delBox'])=='undefined'){
return false;
}else{
?if(form1.delBox.checked==true){??
??if(!confirm('確實(shí)要?jiǎng)h除嗎?'))
????return false;
???else
?????? return true;
?}else{?
?for(var i=0;i<form1.delBox.length; i++){
??if(form1.delBox[i].checked==true){
???if(!confirm('確實(shí)要?jiǎng)h除嗎?'))
????return false;
???else
?????? return true;?
??}
?}
?}
return false;?
}
return false;
}
</script>
<input type="submit" name="delSubmit" value="刪除" onClick="return YN();" >
<input type="checkbox" name="alcheck" value="0" title="點(diǎn)擊全選或者全不選" onclick="CheckAll(this.form.elements['delBox'], this.checked)" >
???? Acegi安全系統(tǒng),是一個(gè)用于Spring Framework的安全框架,能夠和目前流行的Web容器無(wú)縫集成。它使用了Spring的方式提供了安全和認(rèn)證安全服務(wù),包括使用Bean Context,攔截器和面向接口的編程方式。因此,Acegi安全系統(tǒng)能夠輕松地適用于復(fù)雜的安全需求。
?????? 安全涉及到兩個(gè)不同的概念,認(rèn)證和授權(quán)。前者是關(guān)于確認(rèn)用戶是否確實(shí)是他們所宣稱的身份。授權(quán)則是關(guān)于確認(rèn)用戶是否有允許執(zhí)行一個(gè)特定的操作。
?????? 在Acegi安全系統(tǒng)中,需要被認(rèn)證的用戶,系統(tǒng)或代理稱為"Principal"。Acegi安全系統(tǒng)和其他的安全系統(tǒng)不同,它并沒(méi)有角色和用戶組的概念。
Acegi系統(tǒng)設(shè)計(jì)
??關(guān)鍵組件
??????Acegi安全系統(tǒng)包含以下七個(gè)關(guān)鍵的功能組件:
????????1 Authentication對(duì)象,包含了Principal,Credential和Principal的授權(quán)信息。同時(shí)還可以包含關(guān)于發(fā)起認(rèn)證請(qǐng)求的客戶的其他信息,如IP地址。
????????2 ContextHolder對(duì)象,使用ThreadLocal儲(chǔ)存Authentication對(duì)象的地方。
????????3 AuthenticationManager,用于認(rèn)證ContextHolder中的Authentication對(duì)象。
????????4 AccessDecissionManager,用于授權(quán)一個(gè)特定的操作。
????????5 RunAsManager,當(dāng)執(zhí)行特定的操作時(shí),用于選擇性地替換Authentication對(duì)象。
????????6 Secure Object攔截器,用于協(xié)調(diào)AuthenticationManager,AccessDecissionManager,RunAsManager和特定操作的執(zhí)行。
????????7 ObjectDefinitionSource,包含了特定操作的授權(quán)定義。
??????這七個(gè)關(guān)鍵的功能組件的關(guān)系如下圖所示(圖中灰色部分是關(guān)鍵組件):
安全管理對(duì)象
?????? Acegi安全系統(tǒng)目前支持兩類安全管理對(duì)象。
?????? 第一類的安全管理對(duì)象管理AOP Alliance的MethodInvocation,開發(fā)人員可以用它來(lái)保護(hù)Spring容器中的業(yè)務(wù)對(duì)象。為了使Spring管理的Bean可以作為MethodInvocation來(lái)使用,Bean可以通過(guò)ProxyFactoryBean和BeanNameAutoProxyCreator來(lái)管理,就像在Spring的事務(wù)管理一樣使用。
?????? 第二類是FilterInvocation。它用過(guò)濾器(Filter)來(lái)創(chuàng)建,并簡(jiǎn)單地包裝了HTTP的ServletRequest,ServletResponse和FilterChain。FilterInvocation可以用來(lái)保護(hù)HTTP資源。通常,開發(fā)人員并不需要了解它的工作機(jī)制,因?yàn)樗麄冎恍枰獙ilter加入web.xml,Acegi安全系統(tǒng)就可以工作了。
安全配置參數(shù)
?????? 每個(gè)安全管理對(duì)象都可以描述數(shù)量不限的各種安全認(rèn)證請(qǐng)求。例如,MethodInvocation對(duì)象可以描述帶有任意參數(shù)的任意方法的調(diào)用,而FilterInvocation可以描述任意的HTTP URL。
?????? Acegi安全系統(tǒng)需要記錄應(yīng)用于每個(gè)認(rèn)證請(qǐng)求的安全配置參數(shù)。例如,對(duì)于BankManager.getBalance(int accountNumber)方法和BankManager.approveLoan(int applicationNumber)方法,它們需要的認(rèn)證請(qǐng)求的安全配置很不相同。
?????? 為了保存不同的認(rèn)證請(qǐng)求的安全配置,需要使用配置參數(shù)。從實(shí)現(xiàn)的視角來(lái)看,配置參數(shù)使用ConfigAttribute接口來(lái)表示。Acegi安全系統(tǒng)提供了ConfigAttribute接口的一個(gè)實(shí)現(xiàn),SecurityConfig,它把配置參數(shù)保存為一個(gè)字符串。
?????? ConfigAttributeDefinition類是ConfigAttribute對(duì)象的一個(gè)簡(jiǎn)單的容器,它保存了和特定請(qǐng)求相關(guān)的ConfigAttribute的集合。
?????? 當(dāng)安全攔截器收到一個(gè)安全認(rèn)證請(qǐng)求時(shí),需要決定應(yīng)用哪一個(gè)配置參數(shù)。換句話說(shuō),它需要找出應(yīng)用于這個(gè)請(qǐng)求的ConfigAttributeDefinition對(duì)象。這個(gè)查找的過(guò)程是由ObjectDefinitionSource接口來(lái)處理的。這個(gè)接口的主要方法是public ConfigAttributeDefinition getAttributes(Object object),其中Object參數(shù)是一個(gè)安全管理對(duì)象。因?yàn)榘踩芾韺?duì)象包含有認(rèn)證請(qǐng)求的詳細(xì)信息,所以O(shè)bjectDefinitionSource接口的實(shí)現(xiàn)類可以從中獲得所需的詳細(xì)信息,以查找相關(guān)的ConfigAttributeDefiniton對(duì)象。
Acegi如何工作
?????? 為了說(shuō)明Acegi安全系統(tǒng)如何工作,我們?cè)O(shè)想一個(gè)使用Acegi的例子。通常,一個(gè)安全系統(tǒng)需要發(fā)揮作用,它必須完成以下的工作:
??????1 首先,系統(tǒng)從客戶端請(qǐng)求中獲得Principal和Credential;
??????2 然后系統(tǒng)認(rèn)證Principal和Credential信息;
??????3 如果認(rèn)證通過(guò),系統(tǒng)取出Principal的授權(quán)信息;
??????4 接下來(lái),客戶端發(fā)起操作請(qǐng)求;
??????5 系統(tǒng)根據(jù)預(yù)先配置的參數(shù)檢查Principal對(duì)于該操作的授權(quán);
??????6 如果授權(quán)檢查通過(guò)則執(zhí)行操作,否則拒絕。
??????那么,Acegi安全系統(tǒng)是如何完成這些工作的呢?首先,我們來(lái)看看Acegi安全系統(tǒng)的認(rèn)證和授權(quán)的相關(guān)類:
??????安全攔截器的抽象基類,它包含有兩個(gè)管理類,AuthenticationManager和AccessDecisionManager。AuthenticationManager用于認(rèn)證ContextHolder中的Authentication對(duì)象(包含了Principal,Credential和Principal的授權(quán)信息);AccessDecissionManager則用于授權(quán)一個(gè)特定的操作。
??????下面來(lái)看一個(gè)MethodSecurityInterceptor的例子:
?
?? <bean id="bankManagerSecurity"
???????????????????? class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor">
???????????? <property name="validateConfigAttributes">
????????????????????<value>true</value>
????????????</property>
????????????<property name="authenticationManager">
?????????????????? <ref bean="authenticationManager"/>
????????????</property>
????????????<property name="accessDecisionManager">
??????????????????<ref bean="accessDecisionManager"/>
????????????</property>
????????????<property name="objectDefinitionSource">
??????????????????<value>
???????????????????? net.sf.acegisecurity.context.BankManager.delete*=
???????????????????????????? ROLE_SUPERVISOR,RUN_AS_SERVER
???????????????????? net.sf.acegisecurity.context.BankManager.getBalance=
???????????????????????????? ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_
??????????????????</value>
????????????</property>
??????</bean>
??????上面的配置文件中,MethodSecurityInterceptor是AbstractSecurityInterceptor的一個(gè)實(shí)現(xiàn)類。它包含了兩個(gè)管理器,authenticationManager和accessDecisionManager。這兩者的配置如下:
??????
<bean id="authenticationDao" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
?????????????? <property name="dataSource"><ref bean="dataSource"/></property>
??????</bean>
??????<bean id="daoAuthenticationProvider"
???????????????????? class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
?????????????? <property name="authenticationDao"><ref bean="authenticationDao"/></property>
??????</bean>
??????<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
?????????????? <property name="providers">
??????????????????????<list><ref bean="daoAuthenticationProvider"/></list>
?????????????? </property>
??????</bean>
??????<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
??????<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
?????????????? <property name="allowIfAllAbstainDecisions"><value>false</value></property>
?????????????? <property name="decisionVoters">
??????????????????????<list><ref bean="roleVoter"/></list>
?????????????? </property>
??????</bean>
?????? 準(zhǔn)備工作做好了,現(xiàn)在我們來(lái)看看Acegi安全系統(tǒng)是如何實(shí)現(xiàn)認(rèn)證和授權(quán)機(jī)制的。以使用HTTP BASIC認(rèn)證的應(yīng)用為例子,它包括下面的步驟:
?????? 1. 用戶登錄系統(tǒng),Acegi從acegisecurity.ui子系統(tǒng)的安全攔截器(如BasicProcessingFilter)中得到用戶的登錄信息(包括Principal和Credential)并放入Authentication對(duì)象,并保存在ContextHolder對(duì)象中;
?????? 2. 安全攔截器將Authentication對(duì)象交給AuthenticationManager進(jìn)行身份認(rèn)證,如果認(rèn)證通過(guò),返回帶有Principal授權(quán)信息的Authentication對(duì)象。此時(shí)ContextHolder對(duì)象的Authentication對(duì)象已擁有Principal的詳細(xì)信息;
?????? 3. 用戶登錄成功后,繼續(xù)進(jìn)行業(yè)務(wù)操作;
?????? 4. 安全攔截器(bankManagerSecurity)收到客戶端操作請(qǐng)求后,將操作請(qǐng)求的數(shù)據(jù)包裝成安全管理對(duì)象(FilterInvocation或MethodInvocation對(duì)象);
?????? 5. 然后,從配置文件(ObjectDefinitionSource)中讀出相關(guān)的安全配置參數(shù)ConfigAttributeDefinition;
?????? 6. 接著,安全攔截器取出ContextHolder中的Authentication對(duì)象,把它傳遞給AuthenticationManager進(jìn)行身份認(rèn)證,并用返回值更新ContextHolder的Authentication對(duì)象;
?????? 7. 將Authentication對(duì)象,ConfigAttributeDefinition對(duì)象和安全管理對(duì)象(secure Object)交給AccessDecisionManager,檢查Principal的操作授權(quán);
?????? 8. 如果授權(quán)檢查通過(guò)則執(zhí)行客戶端請(qǐng)求的操作,否則拒絕;
AccessDecisionVoter
?????? 注意上節(jié)的accessDecisionManager是一個(gè)AffirmativeBased類,它對(duì)于用戶授權(quán)的投票策略是,只要通過(guò)其中的一個(gè)授權(quán)投票檢查,即可通過(guò);它的allowIfAllAbstainDecisions屬性值是false,意思是如果所有的授權(quán)投票是都是棄權(quán),則通不過(guò)授權(quán)檢查。
?????? Acegi安全系統(tǒng)包括了幾個(gè)基于投票策略的AccessDecisionManager,上節(jié)的RoleVoter就是其中的一個(gè)投票策略實(shí)現(xiàn),它是AccessDecisionVoter的一個(gè)子類。AccessDecisionVoter的具體實(shí)現(xiàn)類通過(guò)投票來(lái)進(jìn)行授權(quán)決策,AccessDecisionManager則根據(jù)投票結(jié)果來(lái)決定是通過(guò)授權(quán)檢查,還是拋出AccessDeniedException例外。
?????? AccessDecisionVoter接口共有三個(gè)方法:
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config);
public boolean supports(ConfigAttribute attribute);
public boolean supports(Class clazz);
?????? 其中的vote方法返回int返回值,它們是AccessDecisionVoter的三個(gè)靜態(tài)成員屬性:ACCESS_ABSTAIN,,ACCESS_DENIED和ACCESS_GRANTED,它們分別是棄權(quán),否決和贊成。
?????? Acegi安全系統(tǒng)中,使用投票策略的AccessDecisionManager共有三個(gè)具體實(shí)現(xiàn)類:AffirmativeBased、ConsensusBased和UnanimousBased。它們的投票策略是,AffirmativeBased類只需有一個(gè)投票贊成即可通過(guò);ConsensusBased類需要大多數(shù)投票贊成即可通過(guò);而UnanimousBased類需要所有的投票贊成才能通過(guò)。
?????? RoleVoter類是一個(gè)Acegi安全系統(tǒng)AccessDecisionVoter接口的實(shí)現(xiàn)。如果ConfigAttribute以ROLE_開頭,RoleVoter則進(jìn)行投票。如果GrantedAuthority的getAutority方法的String返回值匹配一個(gè)或多個(gè)以ROLE_開頭的ConfigAttribute,則投票通過(guò),否則不通過(guò)。如果沒(méi)有以ROLE_開頭的ConfigAttribute,RoleVoter則棄權(quán)。
安全攔截器
??攔截器如何工作
??MethodInvocation攔截器
??FilterInvocation攔截器
認(rèn)證
??認(rèn)證請(qǐng)求
??認(rèn)證管理器
??Authentication Provider
授權(quán)
??Access Decision Manager
??Voting Decision Manager
??授權(quán)管理推薦
ContextHolder的用戶接口
??用戶接口目標(biāo)
??HTTP會(huì)話認(rèn)證
??HTTP Basic認(rèn)證
1、Log4j的概念
?? Log4j中有三個(gè)主要的組件,它們分別是Logger、Appender和Layout,Log4j 允許開發(fā)人員定義多個(gè)Logger,每個(gè)Logger擁有自己的名字,Logger之間通過(guò)名字來(lái)表明隸屬關(guān)系。有一個(gè)Logger稱為Root,它永遠(yuǎn) 存在,且不能通過(guò)名字檢索或引用,可以通過(guò)Logger.getRootLogger()方法獲得,其它Logger通過(guò) Logger.getLogger(String name)方法。
?? Appender則是用來(lái)指明將所有的log信息存放到什么地方,Log4j中支持多種appender,如 console、files、GUI components、NT Event Loggers等,一個(gè)Logger可以擁有多個(gè)Appender,也就是你既可以將Log信息輸出到屏幕,同時(shí)存儲(chǔ)到一個(gè)文件中。
?? Layout的作用是控制Log信息的輸出方式,也就是格式化輸出的信息。
?? Log4j中將要輸出的Log信息定義了5種級(jí)別,依次為DEBUG、INFO、WARN、ERROR和FATAL,當(dāng)輸出時(shí),只有級(jí)別高過(guò)配置中規(guī)定的 級(jí)別的信息才能真正的輸出,這樣就很方便的來(lái)配置不同情況下要輸出的內(nèi)容,而不需要更改代碼,這點(diǎn)實(shí)在是方便啊。
2、Log4j的配置文件
??雖然可以不用配置文件,而在程序中實(shí)現(xiàn)配置,但這種方法在如今的系統(tǒng)開發(fā)中顯然是不可取的,能采用配置文件的地方一定一定要用配置文件。Log4j支持兩 種格式的配置文件:XML格式和Java的property格式,本人更喜歡后者,首先看一個(gè)簡(jiǎn)單的例子吧,如下:
?
log4j.rootLogger=debug, stdout, R
??log4j.appender.stdout=org.apache.log4j.ConsoleAppender
??log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
??# Pattern to output the caller's file name and line number.
??log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
??log4j.appender.R=org.apache.log4j.RollingFileAppender
??log4j.appender.R.File=example.log
??log4j.appender.R.MaxFileSize=100KB
??# Keep one backup file
??log4j.appender.R.MaxBackupIndex=1
??log4j.appender.R.layout=org.apache.log4j.PatternLayout
??log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
????????
??首先,是設(shè)置root,格式為 log4j.rootLogger=[level],appenderName, ...,其中l(wèi)evel就是設(shè)置需要輸出信息的級(jí)別,后面是appender的輸出的目的地,appenderName就是指定日志信息輸出到哪個(gè)地方。您可以同時(shí)指定多個(gè)輸出目的地。配置日志信息輸出目的地Appender,其語(yǔ)法為
??log4j.appender.appenderName = fully.qualified.name.of.appender.class
??log4j.appender.appenderName.option1 = value1
??...
??log4j.appender.appenderName.option = valueN
Log4j提供的appender有以下幾種:
??org.apache.log4j.ConsoleAppender(控制臺(tái))
??org.apache.log4j.FileAppender(文件)
??org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個(gè)日志文件)
??org.apache.log4j.RollingFileAppender(文件大小到達(dá)指定尺寸的時(shí)候產(chǎn)生新文件)
??org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)
配置日志信息的格式(布局),其語(yǔ)法為:
??log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
??log4j.appender.appenderName.layout.option1 = value1
??....
??log4j.appender.appenderName.layout.option = valueN
Log4j提供的layout有以下幾種:
??org.apache.log4j.HTMLLayout(以HTML表格形式布局),
??org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
??org.apache.log4j.SimpleLayout(包含日志信息的級(jí)別和信息字符串),
??org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時(shí)間、線程、類別等等信息)
3、Log4j在程序中的使用
??要在自己的類中使用Log4j,首先聲明一個(gè)靜態(tài)變量Logger logger=Logger.getLog("classname");在使用之前,用PropertyConfigurator.configure ("配置文件")配置一下,現(xiàn)在就可以使用了,用法如下:logger.debug("debug message")或者logger.info("info message"),看下面一個(gè)小例子:
?
import com.foo.Bar;
??import org.apache.log4j.Logger;
??import org.apache.log4j.PropertyConfigurator;
??public class MyApp {
????static Logger logger = Logger.getLogger(MyApp.class.getName());
????public static void main(String[] args) {
??????// BasicConfigurator replaced with PropertyConfigurator.
??????PropertyConfigurator.configure(args[0]);
??????logger.info("Entering application.");
??????Bar bar = new Bar();
??????bar.doIt();
??????logger.info("Exiting application.");
????}
??}
[簡(jiǎn)介]
對(duì)于一個(gè)典型的Web應(yīng)用,完善的認(rèn)證和授權(quán)機(jī)制是必不可少的,在SpringFramework中,Juergen Hoeller提供的范例JPetStore給了一些這方面的介紹,但還遠(yuǎn)遠(yuǎn)不夠,Acegi是一個(gè)專門為SpringFramework提供安全機(jī)制的 項(xiàng)目,全稱為Acegi Security System for Spring,當(dāng)前版本為0.5.1,就其目前提供的功能,應(yīng)該可以滿足絕大多數(shù)應(yīng)用的需求。
本文的主要目的是希望能夠說(shuō)明如何在基于Spring構(gòu)架的Web應(yīng)用中使用Acegi,而不是詳細(xì)介紹其中的每個(gè)接口、每個(gè)類。注意,即使對(duì)已經(jīng)存在的Spring應(yīng)用,通過(guò)下面介紹的步驟,也可以馬上享受到Acegi提供的認(rèn)證和授權(quán)。
[基礎(chǔ)工作]
在你的Web應(yīng)用的lib中添加Acegi下載包中的acegi-security.jar
[web.xml]
實(shí)現(xiàn)認(rèn)證和授權(quán)的最常用的方法是通過(guò)filter,Acegi亦是如此,通常Acegi需要在web.xml添加以下5個(gè)filter:
<filter>
??<filter-name>Acegi Channel Processing Filter</filter-name>
??<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
??<init-param>
????<param-name>targetClass</param-name>
????<param-value>net.sf.acegisecurity.securechannel.ChannelProcessingFilter</param-value>
??</init-param>
</filter>
<filter>
??<filter-name>Acegi Authentication Processing Filter</filter-name>
??<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
??<init-param>
????<param-name>targetClass</param-name>
????<param-value>net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter</param-value>
??</init-param>
</filter>
<filter>
??<filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
??<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
??<init-param>
????<param-name>targetClass</param-name>
????<param-value>net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter</param-value>
??</init-param>
</filter>
<filter>
??<filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
??<filter-class>net.sf.acegisecurity.ui.AutoIntegrationFilter</filter-class>
</filter>
<filter>
??<filter-name>Acegi HTTP Request Security Filter</filter-name>
??<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
??<init-param>
????<param-name>targetClass</param-name>
????<param-value>net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value>
??</init-param>
</filter>
最先引起迷惑的是net.sf.acegisecurity.util.FilterToBeanProxy,Acegi自己的文檔上解釋是: “What??FilterToBeanProxy does is delegate the Filter's methods through to a bean which is obtained from the
Spring application context. This enables the bean to benefit from the Spring application context lifecycle support and configuration flexibility.”,如希望深究的話,去看看源代碼應(yīng)該不難理解。
再下來(lái)就是添加filter-mapping了:
<filter-mapping>
??<filter-name>Acegi Channel Processing Filter</filter-name>
??<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
??<filter-name>Acegi Authentication Processing Filter</filter-name>
??<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
??<filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
??<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
??<filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
??<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
??<filter-name>Acegi HTTP Request Security Filter</filter-name>
??<url-pattern>/*</url-pattern>
</filter-mapping>
這里,需要注意以下兩點(diǎn):
1) 這幾個(gè)filter的順序是不能更改的,順序不對(duì)將無(wú)法正常工作;
2) 如果你的應(yīng)用不需要安全傳輸,如https,則將"Acegi Channel Processing Filter"相關(guān)內(nèi)容注釋掉即可;
3) 如果你的應(yīng)用不需要Spring提供的遠(yuǎn)程訪問(wèn)機(jī)制,如Hessian and Burlap,將"Acegi HTTP BASIC Authorization
Filter"相關(guān)內(nèi)容注釋掉即可。
[applicationContext.xml]
接下來(lái)就是要添加applicationContext.xml中的內(nèi)容了,從剛才FilterToBeanFactory的解釋可以看出,真正的filter都
在Spring的applicationContext中管理:
1) 首先,你的數(shù)據(jù)庫(kù)中必須具有保存用戶名和密碼的table,Acegi要求table的schema必須如下:
CREATE TABLE users (
????username VARCHAR(50) NOT NULL PRIMARY KEY,
????password VARCHAR(50) NOT NULL,
????enabled BIT NOT NULL
);
CREATE TABLE authorities (
????username VARCHAR(50) NOT NULL,
????authority VARCHAR(50) NOT NULL
);
CREATE UNIQUE INDEX ix_auth_username ON authorities ( username, authority );
ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users foreign key (username) REFERENCES users
(username);
2) 添加訪問(wèn)你的數(shù)據(jù)庫(kù)的datasource和Acegi的jdbcDao,如下:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
??<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
??<property name="url"><value>${jdbc.url}</value></property>
??<property name="username"><value>${jdbc.username}</value></property>
??<property name="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
??<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
3) 添加DaoAuthenticationProvider:
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
??<property name="authenticationDao"><ref bean="authenticationDao"/></property>
??<property name="userCache"><ref bean="userCache"/></property>
</bean>
<bean id="userCache" class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
??<property name="minutesToIdle"><value>5</value></property>
</bean>
如果你需要對(duì)密碼加密,則在daoAuthenticationProvider中加入:<property name="passwordEncoder"><ref
bean="passwordEncoder"/></property>,Acegi提供了幾種加密方法,詳細(xì)情況可看包
net.sf.acegisecurity.providers.encoding
4) 添加authenticationManager:
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
??<property name="providers">
????<list>
??????<ref bean="daoAuthenticationProvider"/>
????</list>
?? </property>
</bean>
5) 添加accessDecisionManager:
<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
??<property name="allowIfAllAbstainDecisions">
????<value>false</value>
??</property>
??<property name="decisionVoters">
????<list><ref bean="roleVoter"/></list>
??</property>
</bean>
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
6) 添加authenticationProcessingFilterEntryPoint:
<bean id="authenticationProcessingFilterEntryPoint"
class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
??<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
??<property name="forceHttps"><value>false</value></property>
</bean>
其中acegilogin.jsp是登陸頁(yè)面,一個(gè)最簡(jiǎn)單的登錄頁(yè)面如下:
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %>
<%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter" %>
<%@ page import="net.sf.acegisecurity.AuthenticationException" %>
<html>
??<head>
????<title>Login</title>
??</head>
??<body>
????<h1>Login</h1>
????<form action="<c:url value='j_acegi_security_check'/>" method="POST">
??????<table>
????????<tr><td>User:</td><td><input type='text' name='j_username'></td></tr>
????????<tr><td>Password:</td><td><input type='password' name='j_password'></td></tr>
????????<tr><td colspan='2'><input name="submit" type="submit"></td></tr>
????????<tr><td colspan='2'><input name="reset" type="reset"></td></tr>
??????</table>
????</form>
??</body>
</html>
7) 添加filterInvocationInterceptor:
<bean id="filterInvocationInterceptor"
class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
??<property name="authenticationManager">
????<ref bean="authenticationManager"/>
??</property>
??<property name="accessDecisionManager">
????<ref bean="accessDecisionManager"/>
??</property>
??<property name="objectDefinitionSource">
????<value>
??????CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
??????\A/sec/administrator.*\Z=ROLE_SUPERVISOR
??????\A/sec/user.*\Z=ROLE_TELLER
????</value>
??</property>
</bean>
這里請(qǐng)注意,要objectDefinitionSource中定義哪些頁(yè)面需要權(quán)限訪問(wèn),需要根據(jù)自己的應(yīng)用需求進(jìn)行修改,我上面給出
的定義的意思是這樣的:
a. CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON意思是在比較請(qǐng)求路徑時(shí)全部轉(zhuǎn)換為小寫
b. \A/sec/administrator.*\Z=ROLE_SUPERVISOR意思是只有權(quán)限為ROLE_SUPERVISOR才能訪問(wèn)/sec/administrator*的頁(yè)面
c. \A/sec/user.*\Z=ROLE_TELLER意思是只有權(quán)限為ROLE_TELLER的用戶才能訪問(wèn)/sec/user*的頁(yè)面
8) 添加securityEnforcementFilter:
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
??<property name="filterSecurityInterceptor">
????<ref bean="filterInvocationInterceptor"/>
??</property>
??<property name="authenticationEntryPoint">
????<ref bean="authenticationProcessingFilterEntryPoint"/>
??</property>
</bean>
9) 添加authenticationProcessingFilter:
<bean id="authenticationProcessingFilter"
class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
??<property name="authenticationManager">
????<ref bean="authenticationManager"/>
??</property>
??<property name="authenticationFailureUrl">
????<value>/loginerror.jsp</value>
??</property>
??<property name="defaultTargetUrl">
????<value>/</value>
??</property>
??<property name="filterProcessesUrl">
????<value>/j_acegi_security_check</value>
??</property>
</bean>
其中authenticationFailureUrl是認(rèn)證失敗的頁(yè)面。
10) 如果需要一些頁(yè)面通過(guò)安全通道的話,添加下面的配置:
<bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
??<property name="channelDecisionManager">
????<ref bean="channelDecisionManager"/>
??</property>
??<property name="filterInvocationDefinitionSource">
????<value>
??????CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
??????\A/sec/administrator.*\Z=REQUIRES_SECURE_CHANNEL
??????\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
??????\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL
??????\A.*\Z=REQUIRES_INSECURE_CHANNEL
????</value>
??</property>
</bean>
<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl">
??<property name="channelProcessors">
????<list>
??????<ref bean="secureChannelProcessor"/>
??????<ref bean="insecureChannelProcessor"/>
????</list>
??</property>
</bean>
<bean id="secureChannelProcessor" class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/>
<bean id="insecureChannelProcessor" class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/>
[缺少了什么?]
Acegi目前提供了兩種"secure object",分別對(duì)頁(yè)面和方法進(jìn)行安全認(rèn)證管理,我這里介紹的只是利用
FilterSecurityInterceptor對(duì)訪問(wèn)頁(yè)面的權(quán)限控制,除此之外,Acegi還提供了另外一個(gè)Interceptor――
MethodSecurityInterceptor,它結(jié)合runAsManager可實(shí)現(xiàn)對(duì)對(duì)象中的方法的權(quán)限控制,使用方法可參看Acegi自帶的文檔
和contact范例。
[最后要說(shuō)的]
本來(lái)以為只是說(shuō)明如何使用Acegi而已,應(yīng)該非常簡(jiǎn)單,但真正寫起來(lái)才發(fā)現(xiàn)想要條理清楚的理順?biāo)行枰腷ean還是很
困難的,但愿我沒(méi)有遺漏太多東西,如果我的文章有什么遺漏或錯(cuò)誤的話,還請(qǐng)參看Acegi自帶的quick-start范例,但請(qǐng)
注意,這個(gè)范例是不能直接拿來(lái)用的。
分析和學(xué)習(xí)Spring中的jpetstore用戶管理
??存在用戶的系統(tǒng),必然需要用戶的登錄和認(rèn)證,今天就通過(guò)分析Spring中自帶的jpetstore的例子來(lái)學(xué)習(xí)一下如何實(shí)現(xiàn)在Spring構(gòu)架的系統(tǒng)中用戶登錄。
1、首先從注冊(cè)用戶開始,先看看jpetstore-servlet.xml中關(guān)于注冊(cè)用戶的bean定義,從定義命名中就可以看出下面這段就是注冊(cè)用戶的:
??
<bean name="/shop/newAccount.do" class="org.springframework.samples.jpetstore.web.spring.AccountFormController">
????<property name="petStore"><ref bean="petStore"/></property>
????<property name="validator"><ref bean="accountValidator"/></property>
????<property name="successView"><value>index</value></property>
??</bean>
1). formView呢?從AccountFormController的構(gòu)造函數(shù)中得到,原來(lái)為EditAccountForm;??
2). EditoAccountForm.jsp中顯得非常亂,其實(shí)沒(méi)有多少難理解的地方,最主要的是這個(gè)form既是添加新用戶的,又是編輯用戶信息的,所以顯得有點(diǎn)亂糟糟的。
2、添加好了新用戶,接下來(lái)看看如何登錄,在jpetstore-servlet中發(fā)現(xiàn)這兩個(gè)相關(guān)bean定義,如下:
??
<bean name="/shop/signon.do" class="org.springframework.samples.jpetstore.web.spring.SignonController">
????<property name="petStore"><ref bean="petStore"/></property>
??</bean>
??<bean name="/shop/signonForm.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
????<property name="viewName"><value>SignonForm</value></property>
</bean>
1). 第二個(gè)bean是在運(yùn)行時(shí)用戶輸入用戶名和密碼的form,叫做SignonForm,對(duì)于這個(gè) ParameterizableViewController,用文檔里的話說(shuō)這是最簡(jiǎn)單的Controller,其作用就是在運(yùn)行中指向 Controller而不是直接指向jsp文件,僅此而已。
2). SignonForm.jsp,里面就是一個(gè)簡(jiǎn)單的form,其action就是第一個(gè)bean,即/shop/signon.do,最需要注意的是 signonForwardAction,其主要作用是forward到需要輸入用戶名和密碼的那個(gè)頁(yè)面上去,這個(gè)變量哪里來(lái)的呢?看看下面:
??
<bean id="secureHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
????<property name="interceptors">
??????<list>
????????<ref bean="signonInterceptor"/>
??????</list>
????</property>
????<property name="urlMap">
??????<map>
????????<entry key="/shop/editAccount.do"><ref local="secure_editAccount"/></entry>
????????<entry key="/shop/listOrders.do"><ref local="secure_listOrders"/></entry>
????????<entry key="/shop/newOrder.do"><ref local="secure_newOrder"/></entry>
????????<entry key="/shop/viewOrder.do"><ref local="secure_viewOrder"/></entry>
??????</map>
????</property>
??</bean>
??原來(lái),上面的signonInterceptor實(shí)現(xiàn)了preHandle,因此在請(qǐng)求上面的map頁(yè)面時(shí),首先要經(jīng)過(guò)這個(gè)Interceptor,看看 SignonInterceptor的源碼,原來(lái)在其中為signon.jsp賦予一個(gè)signonForwardAction對(duì)象,呵呵,總算明白了。
3). 接下來(lái)去學(xué)習(xí)一下SignonController,其主體部分中可以看出,首先取出用戶輸入的username和password,然后到數(shù)據(jù)庫(kù)中驗(yàn)證 有沒(méi)有這個(gè)用戶,如果沒(méi)有這個(gè)用戶,返回各錯(cuò)誤頁(yè)面;如果成功,首先生成一個(gè)UserSession對(duì)象,在request的session加入這個(gè) userSession,注意這部分代碼中給出了PagedListHolder分頁(yè)的簡(jiǎn)單使用方法,關(guān)于分頁(yè)顯示,以后再學(xué)習(xí)吧。
3、登錄成功后,就可以根據(jù)不同的用戶設(shè)施不同的行為了,取得用戶信息,無(wú)非就是從session取出userSession即可。