五?FAQ?
5.1 FAQ
- Q: ? 能否脫離Spring框架來使用Acegi?
A:? 雖然Acegi 沒有要求必須使用Spring Framework,但事實上Acegi很大程度上利用了Spring的IOC和AOP,很難脫離Spring的單獨使用。
- Q:? Acegi有對xfire的支持嗎?
A: 有,詳見http://jira.codehaus.org/browse/XFIRE-389
- Q: 為何無論怎么設置都返回到登陸頁面無法成功登陸?
A:? 檢查登陸頁面或登陸失敗頁面是否只有ROLE_ANONYMOUS權限
5.2 Acegi 補習班
要了解Acegi,首先要了解以下幾個重要概念:
-
Authentication
Authentication對象包含了principal, credentials 和 authorities(authorities要賦予給principal的),同時也可以包含一些附加的認證請求信息,如TCP/IP地址和Session id等。
-
SecurityContextHolder
SecurityContextHolder包含ThreadLocal私有屬性用于存取SecurityContext, SecurityContext包含Authentication私有屬性, 看以下一段程序
public void getSecurityContextInformations() {
??SecurityContext sc = SecurityContextHolder.getContext();
??Authentication auth = sc.getAuthentication();
??Object principal = auth.getPrincipal();
??if (principal instanceof UserDetails) {
???
???String password = ((UserDetails) principal).getPassword();
???
???String username = ((UserDetails) principal).getUsername();
???
???GrantedAuthority[] authorities = ((UserDetails) principal).getAuthorities();
???for (int i = 0; i < authorities.length; i++) {
????String authority = authorities[i].getAuthority();
???}
??}
??Object details = auth.getDetails();
??if (details instanceof WebAuthenticationDetails) {
???
???String SessionId = ((WebAuthenticationDetails) details).getSessionId();
??}
?}
-
AuthenticationManager
通過Providers驗證在當前 ContextHolder中的Authentication對象是否合法。
-
AccessDecissionManager
經過投票機制來審批是否批準操作
-
RunAsManager
當執行某個操作時,RunAsManager可選擇性地替換Authentication對象
-
Interceptors
攔截器(如FilterSecurityInterceptor,JoinPoint,MethodSecurityInterceptor等)用于協調授權,認證等操作
posted @
2007-09-12 14:46 華夢行 閱讀(149) |
評論 (0) |
編輯 收藏
三?Acegi安全系統擴展?
????? 相信side對Acegi的擴展會給你耳目一新的感覺,提供完整的擴展功能,管理界面,中文注釋和靠近企業的安全策略。side只對Acegi不符合企業應用需要的功能進行擴展,盡量不改動其余部分來實現全套權限管理功能,以求能更好地適應Acegi升級。
3.1 基于角色的權限控制(RBAC)
??? Acegi 自帶的 sample 表設計很簡單: users表{username,password,enabled} authorities表{username,authority},這樣簡單的設計無法適應復雜的權限需求,故SpringSide選用RBAC模型對權限控制數據庫表進行擴展。?RBAC引入了ROLE的概念,使User(用戶)和Permission(權限)分離,一個用戶擁有多個角色,一個角色擁有有多個相應的權限,從而減少了權限管理的復雜度,可更靈活地支持安全策略。
??? 同時,我們也引入了resource(資源)的概念,一個資源對應多個權限,資源分為ACL,URL,和FUNTION三種。注意,URL和FUNTION的權限命名需要以AUTH_開頭才會有資格參加投票, 同樣的ACL權限命名需要ACL_開頭。
3.2?管理和使用EhCache
3.2.1 設立緩存
在SpringSide里的 Acegi 擴展使用 EhCache?就作為一種緩存解決方案,以緩存用戶和資源的信息和相對應的權限信息。
首先需要一個在classpath的ehcache.xml 文件,用于配置EhCache。
<ehcache>
???
??? <defaultCache
??????????? maxElementsInMemory="10000"
??????????? eternal="false"
??????????? overflowToDisk="true"
??????????? timeToIdleSeconds="0"
??????????? timeToLiveSeconds="0"
??????????? diskPersistent="false"
???????????diskExpiryThreadIntervalSeconds= "120"/>
??? <!-- acegi cache-->
??? <cache name="userCache"
?????????? maxElementsInMemory="10000"
?????????? eternal="true"
??????????overflowToDisk= "true"/>
??? <!-- acegi cache-->
??? <cache name="resourceCache"
?????????? maxElementsInMemory="10000"
?????????? eternal="true"
?????????? overflowToDisk="true"/>
</ehcache>
???? maxElementsInMemory設定了允許在Cache中存放的數據數目,eternal設定Cache是否會過期,overflowToDisk設定內存不足的時候緩存到硬盤,timeToIdleSeconds和timeToLiveSeconds設定緩存游離時間和生存時間,diskExpiryThreadIntervalSeconds設定緩存在硬盤上的生存時間,注意當eternal="true"時,timeToIdleSeconds,timeToLiveSeconds和diskExpiryThreadIntervalSeconds都是無效的。
<defaultCache>是除制定的Cache外其余所有Cache的設置,針對Acegi 的情況, 專門設置了userCache和resourceCache,都設為永不過期。在applicationContext-acegi-security.xml中相應的調用是
<bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
??????? <property name="cacheManager" ref="cacheManager"/>
??????? <property name="cacheName" value=" userCache"/>
??? </bean>
??? <bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache" autowire="byName">
??????? <property name="cache" ref="userCacheBackend"/>
??? </bean>
??? <bean id="resourceCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
??????? <property name="cacheManager" ref="cacheManager"/>
??????? <property name="cacheName" value=" resourceCache"/>
??? </bean>
??? <bean id="resourceCache" class="org.springside.modules.security.service.acegi.cache.ResourceCache" autowire="byName">
??????? <property name="cache" ref="resourceCacheBackend"/>
??? </bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
"cacheName" 就是設定在ehcache.xml 中相應Cache的名稱。
userCache使用的是Acegi 的EhCacheBasedUserCache(實現了UserCache接口), resourceCache是SpringSide的擴展類
public interface UserCache {
??? public UserDetails getUserFromCache (String username);
??? public void putUserInCache (UserDetails user);
??? public void removeUserFromCache (String username);
}
public class ResourceCache {
??? public ResourceDetails getAuthorityFromCache (String resString) {...
?? }
??? public void putAuthorityInCache (ResourceDetails resourceDetails) {...? }
?
?? public void removeAuthorityFromCache (String resString) {... }
??? public List getUrlResStrings() {... }
??? public List getFunctions() {.. }
}
UserCache 就是通過EhCache對UserDetails 進行緩存管理, 而ResourceCache 是對ResourceDetails 類進行緩存管理
public interface UserDetails extends Serializable {
??? public boolean isAccountNonExpired();
??? public boolean isAccountNonLocked();
??? public GrantedAuthority[] getAuthorities();
??? public boolean isCredentialsNonExpired();
??? public boolean isEnabled();
??? public String getPassword();
??? public String getUsername();
}
public interface ResourceDetails extends Serializable {
??? public String getResString();
??? public String getResType();
??? public GrantedAuthority[] getAuthorities();
}
UserDetails 包含用戶信息和相應的權限,ResourceDetails 包含資源信息和相應的權限。
public interface GrantedAuthority {
??? public String getAuthority ();
}
???? GrantedAuthority 就是權限信息,在Acegi 的 sample 里GrantedAuthority 的信息如ROLE_USER, ROLE_SUPERVISOR, ACL_CONTACT_DELETE, ACL_CONTACT_ADMIN等等,網上也有很多例子把角色作為GrantedAuthority ,但事實上看看ACL 就知道, Acegi本身根本就沒有角色這個概念,GrantedAuthority 包含的信息應該是權限,對于非ACL的權限用 AUTH_ 開頭更為合理, 如SpringSide里的 AUTH_ADMIN_LOGIN, AUTH_BOOK_MANAGE 等等。
3.2.2 管理緩存
???? 使用AcegiCacheManager對userCache和resourceCache進行統一緩存管理。當在后臺對用戶信息進行修改或賦權的時候, 在更新數據庫同時就會調用acegiCacheManager相應方法, 從數據庫中讀取數據并替換cache中相應部分,使cache與數據庫同步。
public class AcegiCacheManager extends BaseService {
??? private ResourceCache resourceCache ;
??? private UserCache userCache ;
??? /**
???? * 修改User時更改userCache
???? */
??? public void modifyUserInCache (User user, String orgUsername) {...??? }
??? /**
???? * 修改Resource時更改resourceCache
???? */
??? public void modifyResourceInCache (Resource resource, String orgResourcename) {...??? }
??? /**
???? * 修改權限時同時修改userCache和resourceCache
???? */
??? public void modifyPermiInCache (Permission permi, String orgPerminame) {...? }
??? /**
???? * User授予角色時更改userCache
???? */
??? public void authRoleInCache (User user) {...??? }
??? /**
???? * Role授予權限時更改userCache和resourceCache
???? */
??? public void authPermissionInCache (Role role) {...? }
??? /**
???? * Permissioni授予資源時更改resourceCache
???? */
??? public void authResourceInCache (Permission permi) {...? }
??? /**
???? * 初始化userCache
???? */
??? public void initUserCache () {...? }
??? /**
???? * 初始化resourceCache
???? */
??? public void initResourceCache () {... }
??? /**
???? * 獲取所有的url資源
???? */
??? public List getUrlResStrings () {... ?}
??? /**
???? * 獲取所有的Funtion資源
???? */
??? public List getFunctions () {...? }
??? /**
???? * 根據資源串獲取資源
???? */
??? public ResourceDetails getAuthorityFromCache (String resString) {...? }
??
?......
}
?
3.3 資源權限定義擴展
????
Acegi給出的sample里,資源權限對照關系是配置在xml中的,試想一下如果你的企業安全應用有500個用戶,100個角色權限的時候,維護這個xml將是個繁重無比的工作,如何動態更改用戶權限更是個頭痛的問題。
?? <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.create=ROLE_USER
??????????? sample.contact.ContactManager.getAllRecipients=ROLE_USER
??????????? sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ
??????????? sample.contact.ContactManager.getById=ROLE_USER,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>
??<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
????? <property name="authenticationManager"><ref bean="authenticationManager"/></property>
????? <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
????? <property name="objectDefinitionSource">
???????? <value>
?????? CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
?????? PATTERN_TYPE_APACHE_ANT
?????? /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
???????? </value>
????? </property>
?? </bean>
?對如此不Pragmatic的做法,SpringSide進行了擴展, 讓Acegi 能動態讀取數據庫中的權限資源關系。
3.3.1 Aop Invocation Authorization
??? <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
??????? <property name="authenticationManager" ref="authenticationManager"/>
??????? <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
??????? <property name="objectDefinitionSource" ref="methodDefinitionSource"/>
??? </bean>
??? <bean id="methodDefinitionSource" class="org.springside.security.service.acegi.DBMethodDefinitionSource">
??????? <property name="acegiCacheManager" ref="acegiCacheManager"/>
??? </bean>
????
研究下Aceig的源碼,ObjectDefinitionSource的實際作用是返回一個ConfigAttributeDefinition對象,而Acegi Sample 的方式是用MethodDefinitionSourceEditor把xml中的文本Function資源權限對應關系信息加載到MethodDefinitionMap ( MethodDefinitionSource?的實現類 )中, 再組成ConfigAttributeDefinition,而我們的擴展目標是從緩存中讀取信息來組成ConfigAttributeDefinition。
????
MethodSecurityInterceptor是通過調用AbstractMethodDefinitionSource的lookupAttributes(method)方法獲取ConfigAttributeDefinition。所以我們需要實現自己的ObjectDefinitionSource,繼承AbstractMethodDefinitionSource并實現其lookupAttributes方法,從緩存中讀取資源權限對應關系組成并返回ConfigAttributeDefinition即可。SpringSide中的DBMethodDefinitionSource類的部分實現如下 :
public class DBMethodDefinitionSource extendsAbstractMethodDefinitionSource {
......
??? protected ConfigAttributeDefinitionlookupAttributes(Method mi) {
??????? Assert.notNull(mi, "lookupAttrubutes in the DBMethodDefinitionSource is null");
??????? String methodString = mi.getDeclaringClass().getName() + "." + mi.getName();
??????? if (!acegiCacheManager.isCacheInitialized()) {
??????????? //初始化Cache
??????????? acegiCacheManager.initResourceCache();
??????? }
??????? //獲取所有的function
??????? List methodStrings = acegiCacheManager.getFunctions();
??????? Set auths = new HashSet();
??????? //取權限的合集
??????? for (Iterator iter = methodStrings.iterator(); iter.hasNext();) {
??????????? String mappedName = (String) iter.next();
??????????? if (methodString.equals(mappedName)
??????????????????? || isMatch(methodString, mappedName)) {
??????????????? ResourceDetails resourceDetails = acegiCacheManager.getAuthorityFromCache(mappedName);
??????????????? if (resourceDetails == null) {
??????????????????? break;
??????????????? }
??????????????? GrantedAuthority[] authorities = resourceDetails.getAuthorities();
??????????????? if (authorities == null || authorities.length == 0) {
??????????????????? break;
??????????????? }
??????????????? auths.addAll(Arrays.asList(authorities));
??????????? }
??????? }
??????? if (auths.size() == 0)
??????????? return null;
??????? ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();
??????? String authoritiesStr = " ";
??????? for (Iterator iter = auths.iterator(); iter.hasNext();) {
??????????? GrantedAuthority authority = (GrantedAuthority) iter.next();
??????????? authoritiesStr += authority.getAuthority() + ",";
??????? }
??????? String authStr = authoritiesStr.substring(0, authoritiesStr.length() - 1);
??????? configAttrEditor.setAsText(authStr);
?????? //組裝并返回ConfigAttributeDefinition
??????? return (ConfigAttributeDefinition) configAttrEditor.getValue();
??? }
??? ......
}
要注意幾點的是:
1) 初始化Cache是比較浪費資源的,所以SpringSide中除第一次訪問外的Cache的更新是針對性更新。
2) 因為method采用了匹配方式(詳見 isMatch() 方法) , 即對于*Book和save*這兩個資源來說,只要當前訪問方法是Book結尾或以save開頭都算匹配得上,所以應該取這些能匹配上的資源的相對應的權限的合集。
3) 使用ConfigAttributeEditor 能更方便地組裝ConfigAttributeDefinition。
3.3.2 Filter Invocation Authorization
??? <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
??????? <property name="authenticationManager" ref="authenticationManager"/>
??????? <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
??????? <property name="objectDefinitionSource" ref="filterDefinitionSource"/>
??? </bean>
??? <bean id="filterDefinitionSource" class="org.springside.security.service.acegi.DBFilterInvocationDefinitionSource">
??????? <property name="convertUrlToLowercaseBeforeComparison" value="true"/>
??????? <property name="useAntPath" value="true"/>
??????? <property name="acegiCacheManager" ref="acegiCacheManager"/>
??? </bean>
????
PathBasedFilterInvocationDefinitionMap和RegExpBasedFilterInvocationDefinitionMap都是 FilterInvocationDefinitionSource的實現類,當PATTERN_TYPE_APACHE_ANT字符串匹配上時時,FilterInvocationDefinitionSourceEditor 選用PathBasedFilterInvocationDefinitionMap 把xml中的文本URL資源權限對應關系信息加載。
????
FilterSecurityInterceptor通過FilterInvocationDefinitionSource的lookupAttributes(url)方法獲取ConfigAttributeDefinition。 所以,我們可以通過繼承FilterInvocationDefinitionSource的抽象類AbstractFilterInvocationDefinitionSource,并實現其lookupAttributes方法,從緩存中讀取URL資源權限對應關系即可。SpringSide的DBFilterInvocationDefinitionSource類部分實現如下:
public class DBFilterInvocationDefinitionSource extends?AbstractFilterInvocationDefinitionSource {
......
??? public ConfigAttributeDefinition lookupAttributes(String url) {
??????? if (!acegiCacheManager.isCacheInitialized()) {
??????????? acegiCacheManager.initResourceCache();
??????? }
??????? if (isUseAntPath()) {
??????????? // Strip anything after a question mark symbol, as per SEC-161.
??????????? int firstQuestionMarkIndex = url.lastIndexOf("?");
??????????? if (firstQuestionMarkIndex != -1) {
??????????????? url = url.substring(0, firstQuestionMarkIndex);
??????????? }
??????? }
??????? List urls = acegiCacheManager.getUrlResStrings();
??????? //URL資源倒敘排序
??????? Collections.sort(urls);
??????? Collections.reverse(urls);
//是否先全部轉為小寫再比較
??????? if (convertUrlToLowercaseBeforeComparison) {
??????????? url = url.toLowerCase();
??????? }
??????? GrantedAuthority[] authorities = new GrantedAuthority[0];
??????? for (Iterator iterator = urls.iterator(); iterator.hasNext();) {
??????????? String resString = (String) iterator.next();
??????????? boolean matched = false;
//可選擇使用AntPath和Perl5兩種不同匹配模式
??????????? if (isUseAntPath()) {
??????????????? matched = pathMatcher.match(resString, url);
??????????? } else {
??????????????? Pattern compiledPattern;
??????????????? Perl5Compiler compiler = new Perl5Compiler();
??????????????? try {
??????????????????? compiledPattern = compiler.compile(resString,
??????????????????????????? Perl5Compiler.READ_ONLY_MASK);
??????????????? } catch (MalformedPatternException mpe) {
??????????????????? throw new IllegalArgumentException(
??????????????????????????? "Malformed regular expression: " + resString);
??????????????? }
??????????????? matched = matcher.matches(url, compiledPattern);
??????????? }
??????????? if (matched) {
??????????????? ResourceDetails rd = acegiCacheManager.getAuthorityFromCache(resString);
??????????????? authorities = rd.getAuthorities();
??????????????? break;
??????????? }
??????? }
??????? if (authorities.length > 0) {
??????????? String authoritiesStr = " ";
??????????? for (int i = 0; i < authorities.length; i++) {
??????????????? authoritiesStr += authorities[i].getAuthority() + ",";
??????????? }
??????????? String authStr = authoritiesStr.substring(0, authoritiesStr
??????????????????? .length() - 1);
??????????? ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();
??????????? configAttrEditor.setAsText(authStr);
??????????? return (ConfigAttributeDefinition) configAttrEditor.getValue();
??????? }
??????? return null;
??? }
......
?}
繼承AbstractFilterInvocationDefinitionSource注意幾點:
1)? 需要先把獲取回來的URL資源按倒序派序,以達到 a/b/c/d.* 在 a/.* 之前的效果(詳見 Acegi sample 的applicationContext-acegi-security.xml 中的filterInvocationInterceptor的注釋),為的是更具體的URL可以先匹配上,而獲取具體URL的權限,如a/b/c/d.*權限AUTH_a, AUTH_b 才可查看,? a/.* 需要權限AUTH_a 才可查看,則如果當前用戶只擁有權限AUTH_b,則他只可以查看a/b/c/d.jsp 而不能察看a/d.jsp。
2)?基于上面的原因,故第一次匹配上的就是當前所需權限,而不是取權限的合集。
3) 可以選用AntPath 或 Perl5 的資源匹配方式,感覺AntPath匹配方式基本足夠。
4) Filter 權限控制比較適合于較粗顆粒度的權限,如設定某個模塊下的頁面是否能訪問等,對于具體某個操作如增刪修改,是否能執行,用Method??Invocation 會更佳些,所以注意兩個方面一起控制效果更好
?
3.4 授權操作
????
RBAC模型中有不少多對多的關系,這些關系都能以一個中間表的形式來存放,而Hibernate中可以不建這中間表對應的hbm.xml , 以資源與權限的配置為例,如下:
<hibernate-mapping package="org.springside.modules.security.domain">
??? <class name="Permission" table="PERMISSIONS" dynamic-insert="true" dynamic-update="true">
??????? <cache usage="nonstrict-read-write"/>
??????? <id name="id" column="ID">
??????????? <generator class="native"/>
??????? </id>
??????? <property name="name" column="NAME" not-null="true"/>
??????? <property name="descn" column="DESCN"/>
??????? <property name="operation" column="OPERATION"/>
??????? <property name="status" column="STATUS"/>
??????? <set name="roles" table="ROLE_PERMIS" lazy="true" inverse="true" cascade="save-update" batch-size="5">
??????????? <key>
??????????????? <column name="PERMIS_ID" not-null="true"/>
??????????? </key>
??????????? <many-to-many class="Role" column="ROLE_ID" outer-join="auto"/>
??????? </set>
??????? <set name="resources" table="PERMIS_RESC" lazy="true" inverse="false" cascade="save-update" batch-size="5">
??????????? <key>
??????????????? <column name="PERMIS_ID" not-null="true"/>
??????????? </key>
??????????? <many-to-many class="Resource" column="RESC_ID"/>
??????? </set>
??? </class>
</hibernate-mapping>
<hibernate-mapping package="org.springside.modules.security.domain">
??? <class name="Resource" table="RESOURCES" dynamic-insert="true" dynamic-update="true">
??????? <cache usage="nonstrict-read-write"/>
??????? <id name="id" column="ID">
??????????? <generator class="native"/>
??????? </id>
??????? <property name="name" column="NAME" not-null="true"/>
??????? <property name="resType" column="RES_TYPE" not-null="true"/>
??????? <property name="resString" column="RES_STRING" not-null="true"/>
??????? <property name="descn" column="DESCN"/>
??????? <set name="permissions" table="PERMIS_RESC" lazy="true" inverse="true" cascade="save-update" batch-size="5">
??????????? <key>
??????????????? <column name="RESC_ID" not-null="true"/>
??????????? </key>
??????????? <many-to-many class="Permission" column="PERMIS_ID" outer-join="auto"/>
??????? </set>
??? </class>
</hibernate-mapping>
配置時注意幾點:
1) 因為是分配某個權限的資源,所以權限是主控方,把inverse設為false,資源是被控方inverse設為true
2) cascade是"save-update",千萬別配成delete
3) 只需要 permission.getResources().add(resource), permission.getResources()..remove(resource) 即可很方便地完成授權和取消授權操作
posted @
2007-09-12 14:44 華夢行 閱讀(229) |
評論 (0) |
編輯 收藏
二 Acegi安全系統的配置?
????? Acegi 的配置看起來非常復雜,但事實上在實際項目的安全應用中我們并不需要那么多功能,清楚的了解Acegi配置中各項的功能,有助于我們靈活的運用Acegi于實踐中。
2.1 在Web.xml中的配置
1)? FilterToBeanProxy
Acegi通過實現了Filter接口的FilterToBeanProxy提供一種特殊的使用Servlet Filter的方式,它委托Spring中的Bean -- FilterChainProxy來完成過濾功能,這好處是簡化了web.xml的配置,并且充分利用了Spring IOC的優勢。FilterChainProxy包含了處理認證過程的filter列表,每個filter都有各自的功能。
? ? <filter>
??????? <filter-name>Acegi Filter Chain Proxy</filter-name>
??????? <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
??????? <init-param>
??????????? <param-name>targetClass</param-name>
??????????? <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
??????? </init-param>
??? </filter>
2) filter-mapping
<filter-mapping>限定了FilterToBeanProxy的URL匹配模式,只有*.do和*.jsp和/j_acegi_security_check 的請求才會受到權限控制,對javascript,css等不限制。
?? <filter-mapping>
????? <filter-name>Acegi Filter Chain Proxy</filter-name>
????? <url-pattern>*.do</url-pattern>
??? </filter-mapping>
???
??? <filter-mapping>
????? <filter-name>Acegi Filter Chain Proxy</filter-name>
????? <url-pattern>*.jsp</url-pattern>
??? </filter-mapping>
???
??? <filter-mapping>
????? <filter-name>Acegi Filter Chain Proxy</filter-name>
????? <url-pattern>/j_acegi_security_check</url-pattern>
</filter-mapping>
3) HttpSessionEventPublisher
<listener>的HttpSessionEventPublisher用于發布HttpSessionApplicationEvents和HttpSessionDestroyedEvent事件給spring的applicationcontext。
??? <listener>
??????? <listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
??? </listener>
2.2 在applicationContext-acegi-security.xml中
2.2.1 FILTER CHAIN
FilterChainProxy會按順序來調用這些filter,使這些filter能享用Spring ioc的功能, CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定義了url比較前先轉為小寫, PATTERN_TYPE_APACHE_ANT定義了使用Apache ant的匹配模式
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
??????? <property name="filterInvocationDefinitionSource">
??????????? <value>
??????????????? CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
??????????????? PATTERN_TYPE_APACHE_ANT
?????????????? /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,
basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,
exceptionTranslationFilter,filterInvocationInterceptor
??????????? </value>
??????? </property>
??? </bean>
2.2.2 基礎認證
1) authenticationManager
起到認證管理的作用,它將驗證的功能委托給多個Provider,并通過遍歷Providers, 以保證獲取不同來源的身份認證,若某個Provider能成功確認當前用戶的身份,authenticate()方法會返回一個完整的包含用戶授權信息的Authentication對象,否則會拋出一個AuthenticationException。
Acegi提供了不同的AuthenticationProvider的實現,如:
??????? DaoAuthenticationProvider 從數據庫中讀取用戶信息驗證身份
??????? AnonymousAuthenticationProvider 匿名用戶身份認證
??????? RememberMeAuthenticationProvider 已存cookie中的用戶信息身份認證
??????? AuthByAdapterProvider 使用容器的適配器驗證身份
??????? CasAuthenticationProvider 根據Yale中心認證服務驗證身份, 用于實現單點登陸
??????? JaasAuthenticationProvider 從JASS登陸配置中獲取用戶信息驗證身份
??????? RemoteAuthenticationProvider 根據遠程服務驗證用戶身份
??????? RunAsImplAuthenticationProvider 對身份已被管理器替換的用戶進行驗證
??????? X509AuthenticationProvider 從X509認證中獲取用戶信息驗證身份
??????? TestingAuthenticationProvider 單元測試時使用
??????? 每個認證者會對自己指定的證明信息進行認證,如DaoAuthenticationProvider僅對UsernamePasswordAuthenticationToken這個證明信息進行認證。
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
??????? <property name="providers">
??????????? <list>
??????????????? <ref local="daoAuthenticationProvider"/>
??????????????? <ref local="anonymousAuthenticationProvider"/>
??????????????? <ref local="rememberMeAuthenticationProvider"/>
??????????? </list>
??????? </property>
</bean>
2) daoAuthenticationProvider
進行簡單的基于數據庫的身份驗證。DaoAuthenticationProvider獲取數據庫中的賬號密碼并進行匹配,若成功則在通過用戶身份的同時返回一個包含授權信息的Authentication對象,否則身份驗證失敗,拋出一個AuthenticatiionException。
??? <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
??????? <property name="userDetailsService" ref="jdbcDaoImpl"/>
??????? <property name="userCache" ref="userCache"/>
??????? <property name="passwordEncoder" ref="passwordEncoder"/>
?? </bean>
3) passwordEncoder
使用加密器對用戶輸入的明文進行加密。Acegi提供了三種加密器:
PlaintextPasswordEncoder—默認,不加密,返回明文.
ShaPasswordEncoder—哈希算法(SHA)加密
Md5PasswordEncoder—消息摘要(MD5)加密
<bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>
4) jdbcDaoImpl
用于在數據中獲取用戶信息。 acegi提供了用戶及授權的表結構,但是您也可以自己來實現。通過usersByUsernameQuery這個SQL得到你的(用戶ID,密碼,狀態信息);通過authoritiesByUsernameQuery這個SQL得到你的(用戶ID,授權信息)
<bean id="jdbcDaoImpl"
class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
???????
<property name="dataSource"
ref="dataSource"/>
??????? <property
name="usersByUsernameQuery">
???????????
<value>select loginid,passwd,1 from users where loginid =
?</value>
???????
</property>
??????? <property
name="authoritiesByUsernameQuery">
???????????
<value>select u.loginid,p.name from users u,roles r,permissions
p,user_role ur,role_permis rp where u.id=ur.user_id and r.id=ur.role_id and
p.id=rp.permis_id
and
???????????????
r.id=rp.role_id and p.status='1' and
u.loginid=?</value>
???????
</property>
</bean>
5) userCache &? resourceCache
緩存用戶和資源相對應的權限信息。每當請求一個受保護資源時,daoAuthenticationProvider就會被調用以獲取用戶授權信息。如果每次都從數據庫獲取的話,那代價很高,對于不常改變的用戶和資源信息來說,最好是把相關授權信息緩存起來。(詳見 2.6.3 資源權限定義擴展 )
userCache提供了兩種實現: NullUserCache和EhCacheBasedUserCache, NullUserCache實際上就是不進行任何緩存,EhCacheBasedUserCache是使用Ehcache來實現緩功能。
??? <bean id="userCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
???????
<property name="cacheManager"
ref="cacheManager"/>
???????
<property name="cacheName" value="userCache"/>
??? </bean>
??? <bean id="userCache"
class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"
autowire="byName">
??????? <property
name="cache" ref="userCacheBackend"/>
???
</bean>
??? <bean id="resourceCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
???????
<property name="cacheManager"
ref="cacheManager"/>
???????
<property name="cacheName" value="resourceCache"/>
??? </bean>
??? <bean id="resourceCache"
class="org.springside.modules.security.service.acegi.cache.ResourceCache"
autowire="byName">
??????? <property
name="cache" ref="resourceCacheBackend"/>
??? </bean>
6) basicProcessingFilter
用于處理HTTP頭的認證信息,如從Spring遠程協議(如Hessian和Burlap)或普通的瀏覽器如IE,Navigator的HTTP頭中獲取用戶信息,將他們轉交給通過authenticationManager屬性裝配的認證管理器。如果認證成功,會將一個Authentication對象放到會話中,否則,如果認證失敗,會將控制轉交給認證入口點(通過authenticationEntryPoint屬性裝配)
??? <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
??????? <property name="authenticationManager" ref="authenticationManager"/>
??????? <property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint"/>
??? </bean>
7) basicProcessingFilterEntryPoint
通過向瀏覽器發送一個HTTP401(未授權)消息,提示用戶登錄。
處理基于HTTP的授權過程, 在當驗證過程出現異常后的"去向",通常實現轉向、在response里加入error信息等功能。
<bean
id="basicProcessingFilterEntryPoint"
class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
???????
<property name="realmName" value="SpringSide Realm"/>
</bean>
8) authenticationProcessingFilterEntryPoint
當拋出AccessDeniedException時,將用戶重定向到登錄界面。屬性loginFormUrl配置了一個登錄表單的URL,當需要用戶登錄時,authenticationProcessingFilterEntryPoint會將用戶重定向到該URL
<bean id="authenticationProcessingFilterEntryPoint"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
???????
<property
name="loginFormUrl">
???????????
<value>/security/login.jsp</value>
???????
</property>
??????? <property
name="forceHttps" value="false"/>
</bean>
2.2.3 HTTP安全請求
1) httpSessionContextIntegrationFilter
每次request前 HttpSessionContextIntegrationFilter從Session中獲取Authentication對象,在request完后, 又把Authentication對象保存到Session中供下次request使用,此filter必須其他Acegi filter前使用,使之能跨越多個請求。
<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"></bean>
??? <bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
??????? <property name="allowIfAllAbstainDecisions" value="false"/>
??????? <property name="decisionVoters">
??????????? <list>
??????????????? <ref bean="roleVoter"/>
??????????? </list>
??????? </property>
</bean>
2) httpRequestAccessDecisionManager
經過投票機制來決定是否可以訪問某一資源(URL或方法)。allowIfAllAbstainDecisions為false時如果有一個或以上的decisionVoters投票通過,則授權通過。可選的決策機制有ConsensusBased和UnanimousBased
??? <bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
??????? <property name="allowIfAllAbstainDecisions" value="false"/>
??????? <property name="decisionVoters">
??????????? <list>
??????????????? <ref bean="roleVoter"/>
??????????? </list>
??????? </property>
??? </bean>
3) roleVoter
? 必須是以rolePrefix設定的value開頭的權限才能進行投票,如AUTH_ , ROLE_
??? <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
??????? <property name="rolePrefix" value="AUTH_"/>
?? </bean>
4)exceptionTranslationFilter
異常轉換過濾器,主要是處理AccessDeniedException和AuthenticationException,將給每個異常找到合適的"去向"?
?? <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
??????? <property name="authenticationEntryPoint" ref="authenticationProcessingFilterEntryPoint"/>
??? </bean>
5) authenticationProcessingFilter
和servlet spec差不多,處理登陸請求.當身份驗證成功時,AuthenticationProcessingFilter會在會話中放置一個Authentication對象,并且重定向到登錄成功頁面
???????? authenticationFailureUrl定義登陸失敗時轉向的頁面
???????? defaultTargetUrl定義登陸成功時轉向的頁面
???????? filterProcessesUrl定義登陸請求的頁面
???????? rememberMeServices用于在驗證成功后添加cookie信息
??? <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
??????? <property name="authenticationManager" ref="authenticationManager"/>
??????? <property name="authenticationFailureUrl">
??????????? <value>/security/login.jsp?login_error=1</value>
??????? </property>
??????? <property name="defaultTargetUrl">
??????????? <value>/admin/index.jsp</value>
??????? </property>
??????? <property name="filterProcessesUrl">
??????????? <value>/j_acegi_security_check</value>
??????? </property>
??????? <property name="rememberMeServices" ref="rememberMeServices"/>
??? </bean>
6) filterInvocationInterceptor
在執行轉向url前檢查objectDefinitionSource中設定的用戶權限信息。首先,objectDefinitionSource中定義了訪問URL需要的屬性信息(這里的屬性信息僅僅是標志,告訴accessDecisionManager要用哪些voter來投票)。然后,authenticationManager掉用自己的provider來對用戶的認證信息進行校驗。最后,有投票者根據用戶持有認證和訪問url需要的屬性,調用自己的voter來投票,決定是否允許訪問。
??? <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
??????? <property name="authenticationManager" ref="authenticationManager"/>
??????? <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
??????? <property name="objectDefinitionSource" ref="filterDefinitionSource"/>
??? </bean>
7) filterDefinitionSource (詳見 2.6.3 資源權限定義擴展)
自定義DBFilterInvocationDefinitionSource從數據庫和cache中讀取保護資源及其需要的訪問權限信息?
<bean id="filterDefinitionSource" class="org.springside.modules.security.service.acegi.DBFilterInvocationDefinitionSource">
??????? <property name="convertUrlToLowercaseBeforeComparison" value="true"/>
??????? <property name="useAntPath" value="true"/>
??????? <property name="acegiCacheManager" ref="acegiCacheManager"/>
</bean>
2.2.4 方法調用安全控制
(詳見 2.6.3 資源權限定義擴展)
1) methodSecurityInterceptor
在執行方法前進行攔截,檢查用戶權限信息
2) methodDefinitionSource
自定義MethodDefinitionSource從cache中讀取權限
?? <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
??????? <property name="authenticationManager" ref="authenticationManager"/>
??????? <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
??????? <property name="objectDefinitionSource" ref="methodDefinitionSource"/>
??? </bean>
??? <bean id="methodDefinitionSource" class="org.springside.modules.security.service.acegi.DBMethodDefinitionSource">
??????? <property name="acegiCacheManager" ref="acegiCacheManager"/>
??? </bean>
2.3 Jcaptcha驗證碼
采用 http://jcaptcha.sourceforge.net?作為通用的驗證碼方案,請參考SpringSide中的例子,或網上的:
http://www.coachthrasher.com/page/blog?entry=jcaptcha_with_appfuse。
差沙在此過程中又發現acegi logout filter的錯誤,進行了修正。
另外它默認提供的圖片比較難認,我們custom了一個美觀一點的版本。
posted @
2007-09-12 14:43 華夢行 閱讀(132) |
評論 (0) |
編輯 收藏
一?Acegi安全系統介紹?
??? Author: cac 差沙
??? Acegi是Spring Framework 下最成熟的安全系統,它提供了強大靈活的企業級安全服務,如完善的認證和授權機制,Http資源訪問控制,Method 調用訪問控制,Access Control List (ACL) 基于對象實例的訪問控制,Yale Central Authentication Service (CAS) 耶魯單點登陸,X509 認證,當前所有流行容器的認證適配器,Channel Security頻道安全管理等功能。
1.1 網站資源
官方網站???? ?http://acegisecurity.sourceforge.net
論壇??????????? http://forum.springframework.org/forumdisplay.php?f=33
Jira????????????? http://opensource.atlassian.com/projects/spring/browse/SEC
1.2 多方面的安全控制粒度
- URL 資源訪問控制
?http://apps:8080/index.htm -> for public
?http://apps:8080/user.htm -> for authorized user
- 方法調用訪問控制
public void getData() -> all user
public void modifyData() -> supervisor only
- 對象實例保護
order.getValue() < $100 -> all user
order.getValue() > $100 -> supervisor only
1.3 非入侵式安全架構
- 基于Servlet Filter和Spring aop,? 使商業邏輯和安全邏輯分開,結構更清晰
- 使用Spring 來代理對象,能方便地保護方法調用
1.4 其它安全架構
??? Acegi只是安全框架之一,其實還存在其它優秀的安全框架可供選擇:
posted @
2007-09-12 14:42 華夢行 閱讀(193) |
評論 (0) |
編輯 收藏
http://wiki.springside.org.cn/display/springside/Acegi+Reference
http://www.springside.org.cn/docs/reference/Acegi2.htm
http://www.springside.org.cn/docs/reference/Acegi3.htm
http://www.springside.org.cn/docs/reference/Acegi4.htm
http://www.springside.org.cn/docs/reference/Acegi5.htm
http://www.springside.org.cn/docs/reference/Acegi6.htm
posted @
2007-09-12 14:39 華夢行 閱讀(164) |
評論 (0) |
編輯 收藏
package org.springframework.samples;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.generic.GenericBeanFactoryAccessor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowCountCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.StatementCallback;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.samples.petclinic.Person;
/**
?*
?* @author worldheart
?*
?*/
public class MainTestForJdbcTemplate {
?private static final Log log = LogFactory.getLog(MainTestForJdbcTemplate.class);
?
?public static void main(String[] args) {??
??ListableBeanFactory cbf = new ClassPathXmlApplicationContext("ac1.xml");??
??GenericBeanFactoryAccessor gbfa = new GenericBeanFactoryAccessor(cbf);
??
??JdbcTemplate jt = gbfa.getBean("jdbcTemplate");
??
??jt.execute(new ConnectionCallback(){
???public Object doInConnection(Connection con) throws SQLException, DataAccessException {
????System.out.println(con.getMetaData().getDriverName());
????return null;
???}
??});
??
??List nameList = (List)jt.execute(new StatementCallback(){
???public Object doInStatement(Statement stmt) throws SQLException, DataAccessException {
????System.out.println(stmt.getConnection().getMetaData().getDatabaseProductVersion());
????List<String> nameList = new ArrayList<String>();
????ResultSet rs = null;
????try{
?????rs = stmt.executeQuery("select name from types");
?????while(rs.next()){
??????nameList.add(rs.getString("name"));
?????}
????}finally{
?????JdbcUtils.closeResultSet(rs);
????}
????return nameList;
???}
??});
??System.out.println(nameList);
??
??List perList = (List)jt.query("select * from vets",
????new ResultSetExtractor(){
??????public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
???????List<Person> personList = new ArrayList<Person>();
???????while(rs.next()){
????????Person per = new Person();
????????per.setId(rs.getInt("id"));
????????per.setFirstName(rs.getString("first_name"));
????????per.setLastName(rs.getString("last_name"));
????????personList.add(per);
???????}
???????return personList;
????}
??});
??for(Iterator iterator = perList.iterator(); iterator.hasNext();){
???Person person = (Person)iterator.next();
???System.out.println(person.getId() + "," + person.getFirstName() + "," + person.getLastName());
??}
??final List<Person> pSonList = new ArrayList<Person>();
??jt.query("select * from vets", new RowCallbackHandler(){
???public void processRow(ResultSet rs) throws SQLException {
????Person per = new Person();
????per.setId(rs.getInt("id"));
????per.setFirstName(rs.getString("first_name"));
????per.setLastName(rs.getString("last_name"));
????pSonList.add(per);
???}
??});
??for(Person pSon: pSonList){
???System.out.println(pSon.getId() + "," + pSon.getFirstName() + "," + pSon.getLastName());
??}
??
??RowCountCallbackHandler rcch = new RowCountCallbackHandler();
??jt.query("select * from vets", rcch);
??for(String colName: rcch.getColumnNames())
???System.out.println(colName);
??for(int colType: rcch.getColumnTypes())
???System.out.println(colType);
??System.out.println(rcch.getColumnCount());
??System.out.println(rcch.getRowCount());
??
??List vetsList = (List)jt.query("select * from vets",
????new RowMapper(){
?????public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
??????Person pers = new Person();
??????pers.setId(rs.getInt("id"));
??????pers.setFirstName(rs.getString("first_name"));
??????pers.setLastName(rs.getString("last_name"));
??????return pers;
?????}
??});
??System.out.println(vetsList);
??
??ColumnMapRowMapper cmrw = new ColumnMapRowMapper();
??List vList = (List)jt.query("select * from vets", cmrw);
??System.out.println(vList);
??
??System.out.println(jt.queryForInt("select count(*) from vets where id = ?",
????new Object[]{3}));
??????
??jt.execute("update owners set address = 'GuangZhou' where telephone = ?",
????new PreparedStatementCallback(){
?????public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
??????ps.setString(1, "16068008");
??????ps.addBatch();
??????ps.setString(1, "6085555487");
??????ps.addBatch();
??????return ps.executeBatch();
?????}
??});
??
??System.out.println(jt.query("select address from owners where first_name = ? and last_name = ?",
????new PreparedStatementSetter(){
?????public void setValues(PreparedStatement ps) throws SQLException {
??????ps.setString(1, "Jeff");
??????ps.setString(2, "Black");
?????}
????},
????new RowMapper(){
?????public Object mapRow(ResultSet rs, int rowNum) throws SQLException {??????
??????return rs.getString("address");
?????}
????}));
??
??System.out.println(jt.execute(
????new PreparedStatementCreator(){
?????public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
??????return con.prepareStatement("select address from owners");
?????}
????},
????new PreparedStatementCallback(){
?????public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
??????List<String> list = new ArrayList<String>();
??????ResultSet rs = null;
??????try{
???????rs = ps.executeQuery();
???????while(rs.next()){
????????list.add(rs.getString("address"));
???????}
??????}finally{
???????JdbcUtils.closeResultSet(rs);
??????}
??????return list;
?????}
????}));
??
?}
}
posted @
2007-09-11 11:22 華夢行 閱讀(297) |
評論 (0) |
編輯 收藏
-
public class BindStatus
- extends Object
Simple adapter to expose the bind status of a field or object. Set as a variable both by the JSP bind tag and Velocity/FreeMarker macros.
? 簡單的適配器獲取域或者是對象綁定狀態, 同樣適用于模板。
Obviously, object status representations (i.e. errors at the object level rather than the field level) do not have an expression and a value but only error codes and messages. For simplicity's sake and to be able to use the same tags and macros, the same status class is used for both scenarios.
顯然,對象的狀態表示(例如錯誤是對象級的而不是域級別的)沒有表達式或者值,而是只有錯誤碼和消息
Method Summary
|
?int
|
doEndTag
()
??????????? |
?void
|
doFinally
()
??????????? |
protected ?int
|
doStartTagInternal
()
??????????Called by doStartTag to perform the actual work. |
?PropertyEditor
|
getEditor
()
??????????Retrieve the PropertyEditor for the property that this tag is currently bound to. |
?Errors
|
getErrors
()
??????????Retrieve the Errors instance that this tag is currently bound to. |
?String
|
getPath
()
??????????Return the path that this tag applies to. |
?String
|
getProperty
()
??????????Retrieve the property that this tag is currently bound to, or null if bound to an object rather than a specific property. |
?boolean
|
isIgnoreNestedPath
()
??????????Return whether to ignore a nested path, if any. |
?void
|
setIgnoreNestedPath
(boolean?ignoreNestedPath)
??????????Set whether to ignore a nested path, if any. |
?void
|
setPath
(String?path)
??????????Set the path that this tag should apply. |
posted @
2007-09-11 11:00 華夢行 閱讀(249) |
評論 (0) |
編輯 收藏
1?TagSupport與BodyTagSupport的區別
?TagSupport與BodyTagSupport的區別主要是標簽處理類是否需要與標簽體交互,如果不需要交互的就用TagSupport,否則如果不需要交互就用BodyTagSupport。
?????交互就是標簽處理類是否要讀取標簽體的內容和改變標簽體返回的內容。
????用TagSupport實現的標簽,都可以用BodyTagSupport來實現,因為BodyTagSupport繼承了TagSupport。
?2?doStartTag(),doEndTag()
???doStartTag()方法是遇到標簽開始時會呼叫的方法,其合法的返回值是EVAL_BODY_INCLUDE與SKIP_BODY,前者表示將顯示標簽間的文字,后者表示不顯示標簽間的文字;doEndTag()方法是在遇到標簽結束時呼叫的方法,其合法的返回值是EVAL_PAGE與?SKIP_PAGE,前者表示處理完標簽后繼續執行以下的JSP網頁,后者是表示不處理接下來的JSP網頁
????doAfterBody(),這個方法是在顯示完標簽間文字之后呼叫的,其返回值有EVAL_BODY_AGAIN與SKIP_BODY,前者會再顯示一次標簽間的文字,后者則繼續執行標簽處理的下一步。
???預定的處理順序是:doStartTag()返回SKIP_BODY,doAfterBodyTag()返回SKIP_BODY,doEndTag()返回EVAL_PAGE.
??如果繼承了TagSupport之后,如果沒有改寫任何的方法,標簽處理的執行順序是:
???doStartTag()?->不顯示文字?->doEndTag()->執行接下來的網頁
??如果您改寫了doStartTag(),則必須指定返回值,如果指定了EVAL_BODY_INCLUDE,則執行順序是
???doStartTag()->顯示文字->doAfterBodyTag()->doEndTag()->執行下面的網頁
?
下面這個程式簡單的示範如何使用自訂標籤來對網頁內容作一些保護:
????*?GuardTag.java
package?onlyfun.caterpillar;?
?
import?java.io.*;?
import?javax.servlet.jsp.*;?
import?javax.servlet.jsp.tagext.*;?
?
public?class?GuardTag?extends?TagSupport?{?
????public?int?doStartTag()?throws?JspException?{?
????????String?valid?=?
????????????pageContext.getRequest().getParameter("valid");?
?
????????//?如果flag設定為key,就顯示本體文字內容?
????????if(valid.equals("valid_user"))?{?
????????????return?EVAL_BODY_INCLUDE;?
????????}?
?
????????return?SKIP_BODY;?
????}?
}?
同樣的,程式編譯完之後,放置在WEB-INF/classes/之下,然後編譯.tld檔案,如下:
????*?guardtag.tld
<?xml?version="1.0"?encoding="UTF-8"??>?
?
<taglib?xmlns="
????xmlns:xsi="
????xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
????????????????????????web-jsptaglibrary_2_0.xsd"?
????version="2.0">?
????
????<description>Tag?Demo</description>?
????<tlib-version>1.0</tlib-version>?
????<jsp-version>2.0</jsp-version>?
????<short-name>TagDemo</short-name>?
????<uri>/TagDemo</uri>?
????<tag>?
????????<description>Cuard?BodyText</description>?
????????<name>guard</name>?
????????<tag-class>onlyfun.caterpillar.GuardTag</tag-class>?
????????<body-content>JSP</body-content>?
????</tag>?
</taglib>?
在<body-content>中設定的是JSP,這表示如果本體中包括JSP網頁的內容,將會被編譯執行,接下來您可以在web.xml中定義uri與.tld的名稱對應關係,方法與前一個主題相同,這邊就不重複說明了,然後撰寫一個測試的JSP網頁:
????*?test.jsp
<%@taglib?prefix="caterpillar"?
???????????uri="
<html>?
<body>?
????這個網頁包括受保護的內容OOOXXXX。。。。。。<p>?
????<caterpillar:guard>?
????????${?param.user?},?您好,幸運密碼是?oooxxx?!?
????</caterpillar:guard>?
</body>?
</html>?
為了要能看到幸運密碼,您必須在請求中包括guard參數,假設請求是:
http://localhost:8080/myjsp/test.jsp?valid=valid_user&user=Justin
這樣就可以看到幸運密碼了:
<html>
<body>
????這個網頁包括受包護的內容OOOXXXX。。。。。。<p>
???
????????Justin,?您好,幸運密碼是:?oooxxx?!
???
</body>
</html>
posted @
2007-09-11 10:09 華夢行 閱讀(722) |
評論 (1) |
編輯 收藏
?public void _jspService(HttpServletRequest request, HttpServletResponse response)
??????? throws java.io.IOException, ServletException {
??? JspFactory _jspxFactory = null;
??? PageContext pageContext = null;
??? HttpSession session = null;
??? ServletContext application = null;
??? ServletConfig config = null;
??? JspWriter out = null;
??? Object page = this;
??? JspWriter _jspx_out = null;
??? PageContext _jspx_page_context = null;
??? try {
????? _jspxFactory = JspFactory.getDefaultFactory();
????? response.setContentType("text/html;charset=UTF-8");
????? pageContext = _jspxFactory.getPageContext(this, request, response,
????? ???null, true, 8192, true);
????? _jspx_page_context = pageContext;
????? application = pageContext.getServletContext();
????? config = pageContext.getServletConfig();
????? session = pageContext.getSession();
????? out = pageContext.getOut();
????? _jspx_out = out;
????? out.write("\n");
????? out.write("\n");
????? out.write("\n");
????? out.write("\n");
????? out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n");
????? out.write("?? \"????? out.write("\n");
????? out.write("<html>\n");
????? out.write("??? <head>\n");
????? out.write("??????? <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
????? out.write("??????? <title>JSP Page</title>\n");
????? out.write("??? </head>\n");
????? out.write("??? <body>\n");
????? out.write("\n");
????? out.write("??? <h1>JSP Page</h1>\n");
????? out.write("\n");
????? out.write("??? \n");
????? out.write("??? </body>\n");
????? out.write("</html>\n");
??? } catch (Throwable t) {
????? if (!(t instanceof SkipPageException)){
??????? out = _jspx_out;
??????? if (out != null && out.getBufferSize() != 0)
????????? out.clearBuffer();
??????? if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
????? }
??? } finally {
????? if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
??? }
? }
posted @
2007-09-10 17:00 華夢行 閱讀(298) |
評論 (0) |
編輯 收藏
經常有朋友問起,JSP和Servlet之間有什么區別,兩者之間又有什么聯系?其實Servlet技術的出現時間很早,是當時為了Java的服務器端應用而開發的。大家都知道Applet是應用小程序,Servlet就是服務器端小程序了。但在Microsoft公司的ASP技術出現后,使用Servlet進行響應輸出時一行行的輸出語句就顯得非常笨拙,對于復雜布局或者顯示頁面更是如此。JSP就是為了滿足這種需求在Servlet技術之上開發的。可見,JSP和Servlet之間有著內在的血緣關系,在學習JSP時,如果能夠抓住這種聯系,就能更深刻地理解JSP的運行機理,達到事半功倍的效果。
本文將通過對一個JSP運行過程的剖析,深入JSP運行的內幕,并從全新的視角闡述一些JSP中的技術要點。
HelloWorld.jsp
我們以Tomcat 4.1.17服務器為例,來看看最簡單的HelloWorld.jsp是怎么運行的。
代碼清單1:HelloWorld.jsp
HelloWorld.jsp
<%
String message = "Hello World!";
%>
<%=message%> |
這個文件非常簡單,僅僅定義了一個String的變量,并且輸出。把這個文件放到Tomcat的webapps\ROOT\目錄下,啟動Tomcat,在瀏覽器中訪問http://localhost:8080/HelloWorld.jsp,瀏覽器中的輸出為“HelloWorld!”
讓我們來看看Tomcat都做了什么。轉到Tomcat的\work\Standalone\localhost\_目錄下,可以找到如下的HelloWorld_jsp.java,這個文件就是Tomcat解析HelloWorld.jsp時生成的源文件:
代碼清單2:HelloWorld_jsp.java
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class HelloWorld_jsp extends HttpJspBase {
......
public void _jspService(HttpServletRequest request,
HttpServletResponse response)throws java.io.IOException, ServletException
{
JspFactory _jspxFactory = null;
javax.servlet.jsp.PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
try {
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
String message = "Hello World!";
out.print(message);
} catch (Throwable t) {
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
} |
從上面可以看出,HelloWorld.jsp在運行時首先解析成一個Java類HelloWorld_jsp.java,該類繼承于org.apache.jasper.runtime.HttpJspBase基類,HttpJspBase實現了HttpServlet接口。可見,JSP在運行前首先將編譯為一個Servlet,這就是理解JSP技術的關鍵。
我們還知道JSP頁面中內置了幾個對象,如pageContext、application、config、page、session、out等,你可能會奇怪,為什么在JSP中的代碼片斷中可以直接使用這些內置對象。觀察_jspService()方法,實際上這幾個內置對象就是在這里定義的。在對JSP文件中的代碼片斷進行解析之前,先對這幾個內置對象進行初始化。
首先,調用JspFactory的getDefaultFactory()方法獲取容器實現(本文中指Tomcat 4.1.17)的一個JspFactory對象的引用。JspFactory是javax.servlet.jsp包中定義的一個抽象類,其中定義了兩個靜態方法set/getDefaultFactory()。set方法由JSP容器(Tomcat)實例化該頁面Servlet(即HelloWorld_jsp類)的時候置入,所以可以直接調用JspFactory.getDefaultFactory()方法得到這個JSP工廠的實現類。Tomcat是調用org.apache.jasper.runtime.JspFactoryImpl類。
然后,調用這個JspFactoryImpl的getPageContext()方法,填充一個PageContext返回,并賦給內置變量pageConext。其它內置對象都經由該pageContext得到。具體過程見上面的代碼,這里不再贅述。該頁面Servlet的環境設置完畢,開始對頁面進行解析。HelloWorld.jsp頁面僅僅定義了一個String變量,然后直接輸出。解析后的代碼如下:
代碼清單3:JSP頁面解析后的代碼片斷
String message = "Hello World!";
out.print(message); |
定制標簽的解析過程
在一個中大型的Web應用中,通常使用JSP定制標簽來封裝頁面顯示邏輯。剖析容器對定制標簽的解析過程,對我們深入理解定制標簽的運行機理非常有幫助。下面我們以Struts1.1b中附帶的struts-example應用的主頁運行為例加以說明。
包含定制標簽的index.jsp
Struts1.1b的下載地址是http://jakarta.apache.org/struts/index.html。將下載的包解壓,在webapps目錄下可以找到struts-example.war。將該War包拷貝到Tomcat的webapps目錄下,Tomcat會自動安裝此應用包。在瀏覽器中通過http://localhost:8080/struts-example訪問struts-example應用,將顯示應用的首頁(見圖1)。

圖一 應用的首頁
代碼清單4:index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html:html locale="true">
<head>
<title><bean:message key="index.title"/></title>
<html:base/>
</head>
<body bgcolor="white">
……
</body>
</html:html> |
我們僅以index.jsp中的<bean:message/>標簽的解析為例進行分析,看容器是怎樣把這個自定義標簽解析成HTML輸出的。上面代碼省略了頁面的其它顯示部分。首先,查看上面瀏覽器中頁面的源文件:
<html lang="zh">
<head>
<title>MailReader Demonstration Application (Struts 1.0)</title>
</head>
<body bgcolor="white">
……
</body>
</html> |
可見,容器已經把<bean:message key="index.title"/>替換成一個字串,顯示為頁面的標題。
解析過程
那么,JSP容器是怎樣完成解析的呢?查看在工作目錄jakarta-tomcat-4.1.17\work\Standalone\localhost\struts-example下解析后的index_jsp.java文件:
代碼清單5:index_jsp.java
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class index_jsp extends HttpJspBase {
//為所有的定制標簽定義處理器池類的引用
private org.apache.jasper.runtime.TagHandlerPool ;
_jspx_tagPool_bean_message_key;
……
//頁面類構造方法
public index_jsp() {
_jspx_tagPool_bean_message_key =
new org.apache.jasper.runtime.TagHandlerPool();
……
}
public void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws java.io.IOException, ServletException {
……
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this,
request, response,null, true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
……
if (_jspx_meth_html_html_0(pageContext))
return;
……
}
//頁面在處理退出時釋放所有定制標簽的屬性
public void _jspDestroy() {
_jspx_tagPool_bean_message_key.release();
……
}
} |
生成的index_jsp.java繼承于org.apache. jasper.runtime.HttpJspBase。研究這個文件為我們了解定制標簽的運行機理提供了途徑。
從上面可以看出,Tomcat在解析一個JSP頁面時,首先為每一個定制標簽定義并實例化了一個TagHandlerPool對象。頁面的處理方法覆蓋父類的_ jspService()方法,_jspService方法首先初始化環境,為內置對象賦值。由于index.jsp頁面整體由一個<html:html/>標簽包裹,Tomcat對每一個標簽都產生一個私有方法加以實現。<html:html/>標簽的處理方法是_jspx_meth_html_html_0()。這個方法的命名規范大家也可以從這里看出,就是“_jspx_meth + 標簽的前綴 + 標簽名 + 該標簽在JSP頁面同類標簽中出現的序號”。其它標簽都被包含在該標簽中,所以其它標簽在_jspx_meth_html_html_0()方法中進行解析。具體的代碼實現請參見賽迪網http://linux.ccidnet.com期刊瀏覽2003年第6期。
在_jspx_meth_html_html_0()方法中,首先從_jspx_tagPool_html_html_locale池中得到一個org.apache.struts.taglib.html.HtmlTag的實例,然后設置這個tag實例的頁面上下文及上級標簽,由于html:html標簽是頁面的最頂層標簽,所以它的parent是null。然后對該標簽的內容進行解析。HTML代碼直接輸出,下面主要看看<html:html></html:html>標簽之間包含的<bean:message key="index.title"/>標簽的解析。對bean:message標簽的解析類似于html:html,Tomcat也將其放入一個單獨的方法_jspx_meth_bean_message_0()中進行。
bean:message標簽的解析
代碼清單7:_jspx_meth_bean_message_0()方法片斷
//對message定制標簽的處理方法
private boolean _jspx_meth_bean_message_0(
javax.servlet.jsp.tagext.Tag _jspx_th_html_html_0,
javax.servlet.jsp.PageContext pageContext) throws Throwable {
JspWriter out = pageContext.getOut();
/* ---- bean:message ---- */
org.apache.struts.taglib.bean.MessageTag
_jspx_th_bean_message_0 =
(org.apache.struts.taglib.bean.MessageTag)
_jspx_tagPool_bean_message_key.get(
org.apache.struts.taglib.bean.MessageTag.class);
_jspx_th_bean_message_0.setPageContext(pageContext);
_jspx_th_bean_message_0.setParent(_jspx_th_html_html_0);
_jspx_th_bean_message_0.setKey("index.title");
int _jspx_eval_bean_message_0 = _jspx_th_bean_message_0.doStartTag();
if (_jspx_th_bean_message_0.doEndTag()== javax.servlet.jsp. tagext.Tag.SKIP_PAGE)
return true;
_jspx_tagPool_bean_message_key.reuse(_jspx_th_bean_message_0);
return false;
} |
同樣,對html:bean也需要從池中得到一個標簽類的實例,然后設置環境。這里不再贅述。我們只專注對MessageTag定制標簽類特殊的處理部分。定制標簽類的開發不在本文討論范圍之內。在index.jsp中定義了一個bean:message標簽,并設置了一個屬性:<bean:message key="index.title"/>。Tomcat在解析時,調用MessageTag對象的key屬性設置方法setKey(),將該屬性置入。然后調用MessageTag的doStartTag()和doEndTag()方法,完成解析。如果doEndTag()方法的返回值為javax.servlet.jsp.tagext.Tag. SKIP_PAGE,表明已經完成解析,返回true,Tomcat將立即停止剩余頁面代碼的執行,并返回。否則把該MessageTag的實例放回池中。
標簽類對象實例的池化
為了提高運行效率,Tomcat對所有的定制標簽類進行了池化,池化工作由org.apache.jasper. runtime.TagHandlerPool類完成。TagHandlerPool類主要有兩個方法,代碼如下:
代碼清單8:TagHandlerPool.java
public class TagHandlerPool {
private static final int MAX_POOL_SIZE = 5;
private Tag[] handlers;
public synchronized Tag get(Class handlerClass) throws JspException {……}
public synchronized void reuse(Tag handler) {……}
} |
TagHandlerPool簡單地實現了對標簽類的池化,其中MAX_POOL_SIZE是池的初始大小,handlers是一個Tag的數組,存儲標簽類的實例。get(Class handlerClass)得到一個指定標簽類的實例,如果池中沒有可用實例,則新實例化一個。reuse(Tag handler)把handler對象放回池中。
至此,我們對JSP在容器中的運行過程已經了然于胸了。雖然每種JSP容器的解析結果會有差異,但其中的原理都雷同。對于編寫JSP應用,我們并不需要干涉容器中的運行過程,但如果你對整個底層的運行機制比較熟悉,就能對JSP/Servlet技術有更深的認識。
|
posted @
2007-09-10 16:53 華夢行 閱讀(196) |
評論 (0) |
編輯 收藏
RowCountCallbackHandler rcch = new RowCountCallbackHandler();
??jt.query("select * from vets", rcch);
??for(String colName: rcch.getColumnNames())
???System.out.println(colName);
??for(int colType: rcch.getColumnTypes())
???System.out.println(colType);
??System.out.println(rcch.getColumnCount());
??System.out.println(rcch.getRowCount());
posted @
2007-09-10 11:35 華夢行 閱讀(1027) |
評論 (0) |
編輯 收藏
for(Person pSon: pSonList){
???System.out.println(pSon.getId() + "," + pSon.getFirstName() + "," + pSon.getLastName());
??}
posted @
2007-09-10 11:31 華夢行 閱讀(114) |
評論 (0) |
編輯 收藏
摘要: /**
* Copyright: Copyright (c) 2005-2005
* Company: JavaResearch(http://www.javaresearch.org)
...
閱讀全文
posted @
2007-09-10 11:24 華夢行 閱讀(2384) |
評論 (0) |
編輯 收藏
-
/**
-
* Copyright: Copyright (c) 2005-2005
-
* Company: JavaResearch(http://www.javaresearch.org)
-
*/
-
package
org.javaresearch.jerch;
-
-
-
-
import
java.sql.PreparedStatement;
-
import
java.sql.SQLException;
-
-
/**
-
* 批量更新時設置PreparedStatement的值的接口定義。
-
* 最后更新日期:2005年3月25日
-
* @author cherami
-
*/
-
public
interface
BatchPreparedStatementSetter
{
- ? /**
-
?? * 得到批次的總次數。
-
?? * @return 批次的總次數
-
?? */
- ? publicint getBatchSize();
-
- ? /**
-
?? * 設置第i次的值。
-
?? * 請注意它是針對每一次設置值的,
-
?? * 如果所有的值不是保存在一個List類型的可以通過索引訪問的集合中,
-
?? * 那么這個方法的實現中可能需要使用switch或者if語句進行判斷。
-
?? * 你可以考慮使用PreparedStatementSetter一次性設置全部的批次。
-
?? * @param ps PreparedStatement
-
?? * @param i 執行批次
-
?? * @throws SQLException
-
?? */
- ? publicvoid setValues(PreparedStatement ps, int i) throwsSQLException;
- }
posted @
2007-09-10 11:21 華夢行 閱讀(1087) |
評論 (0) |
編輯 收藏
所謂的回調就是在執行statement A過程中,用到了statement B,那么先保存以前的執行信息,此時statement?B 可以使用Statement A 的內容,然后再執行 Statement B, Statement B執行完畢后,返回的結果,可以被Statement A 利用,然后繼續執行statementA。這就是傳說中回調
posted @
2007-09-10 11:16 華夢行 閱讀(181) |
評論 (0) |
編輯 收藏
jt.execute("update owners set address = 'GuangZhou' where telephone = ?",
????new PreparedStatementCallback(){
?????public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
??????ps.setString(1, "16068008");
??????ps.addBatch();
??????ps.setString(1, "6085555487");
??????ps.addBatch();
??????return ps.executeBatch();
?????}
??});
/**
- * Copyright: Copyright (c) 2005-2005
- * Company: JavaResearch(http://www.javaresearch.org)
- */
- package org.javaresearch.jerch;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
- /**
- * PreparedStatement回調接口定義。
- * 最后更新日期:2005年3月28日
- * @author cherami
- */
- publicinterfacePreparedStatementCallback {
- ? /**
- ?? * 定義PreparedStatement中的執行內容。
- ?? * 執行前PreparedStatement的參數等內容都已經設置好了。
- ?? * @param stmt PreparedStatement
- ?? * @return 執行的結果
- ?? * @throws SQLException
- ?? */
- ? publicObject doInPreparedStatement(PreparedStatement stmt) throwsSQLException;
- }
posted @
2007-09-10 11:10 華夢行 閱讀(4322) |
評論 (0) |
編輯 收藏
-
/**
-
* Copyright: Copyright (c) 2005-2005
-
* Company: JavaResearch(http://www.javaresearch.org)
-
*/
-
package
org.javaresearch.jerch;
-
-
import
java.sql.ResultSet;
-
import
java.sql.SQLException;
-
-
/**
-
* 提取ResultSet中的全部數據的接口定義。
-
* 最后更新日期:2005年3月28日
-
* @author cherami
-
*/
-
public
interface
ResultSetExtractor
{
- ? /**
-
?? * 提取ResultSet中的全部數據。
-
?? * @param rs ResultSet
-
?? * @return ResultSet中的全部數據
-
?? * @throws SQLException
-
?? */
- ? publicObject extractSet(ResultSet rs) throwsSQLException;
- }
List perList = (List)jt.query("select * from vets",
????new ResultSetExtractor(){
??????public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
???????List<Person> personList = new ArrayList<Person>();
???????while(rs.next()){
????????Person per = new Person();
????????per.setId(rs.getInt("id"));
????????per.setFirstName(rs.getString("first_name"));
????????per.setLastName(rs.getString("last_name"));
????????personList.add(per);
???????}
???????return personList;
????}
??});
posted @
2007-09-10 11:05 華夢行 閱讀(309) |
評論 (0) |
編輯 收藏
-
public
interface
Mappable
{
- ? /**
-
?? * 得到字段對應的填充方法的方法名。
-
?? * @param fieldName 數據庫表的字段名
-
?? * @return 進行填充的方法名。
-
?? */
- ? publicString getMapMethod(String fieldName);
- ? /**
-
?? * 得到字段對應的填充方法的參數類型。
-
?? * 數據庫返回的值的類型和Java中的可能不是匹配的,或者JavaBean中自己定義為原始類型了,因此需要指定。
-
?? * @param fieldName 數據庫表的字段名
-
?? * @param dbType 數據庫返回的類型常量定義
-
?? * @return 進行填充的方法的參數的類型
-
?? */
- ? publicClass getMethodParameterType(String fieldName,int dbType);
- }
posted @
2007-09-10 11:00 華夢行 閱讀(157) |
評論 (0) |
編輯 收藏
-
/**
-
* Copyright: Copyright (c) 2005-2005
-
* Company: JavaResearch(http://www.javaresearch.org)
-
*/
-
package
org.javaresearch.jerch;
-
-
import
java.sql.PreparedStatement;
-
import
java.sql.SQLException;
-
-
/**
-
* 設置PreparedStatement的參數的接口定義。
-
* 最后更新日期:2005年3月28日
-
* @author cherami
-
*/
-
public
interface
PreparedStatementSetter
{
- ? /**
-
?? * 設置PreparedStatement所需的全部參數。
-
?? * @param ps PreparedStatement
-
?? * @throws SQLException
-
?? */
- ? publicvoid setValues(PreparedStatement ps)? throwsSQLException;
- }
-
- jt.query("select address from owners where first_name = ? and last_name = ?",
????new PreparedStatementSetter(){
?????public void setValues(PreparedStatement ps) throws SQLException {
??????ps.setString(1, "Jeff");
??????ps.setString(2, "Black");
?????}
????},
????new RowMapper(){
?????public Object mapRow(ResultSet rs, int rowNum) throws SQLException {??????
??????return rs.getString("address");
?????}
????}));
posted @
2007-09-10 10:54 華夢行 閱讀(4023) |
評論 (0) |
編輯 收藏
-
/**
-
* Copyright: Copyright (c) 2005-2005
-
* Company: JavaResearch(http://www.javaresearch.org)
-
*/
-
package
org.javaresearch.jerch;
-
-
import
java.sql.Connection;
-
import
java.sql.PreparedStatement;
-
import
java.sql.SQLException;
-
-
/**
-
* 創建PreparedStatement的接口定義。
-
* 最后更新日期:2005年3月28日
-
* @author cherami
-
*/
-
public
interface
PreparedStatementCreator
{
- ? /**
-
?? * 從數據庫連接中創建一個PreparedStatement。
-
?? * @param con 數據庫連接
-
?? * @return 創建好的PreparedStatement
-
?? * @throws SQLException
-
?? */
- ? publicPreparedStatement createPreparedStatement(Connection con) throwsSQLException;
- }
-
- 使用實例:
- jt? jdbctemplate:
??System.out.println(jt.execute(
????new PreparedStatementCreator(){
?????public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
??????return con.prepareStatement("select address from owners");
?????}
????},
????new PreparedStatementCallback(){
?????public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
??????List<String> list = new ArrayList<String>();
??????ResultSet rs = null;
??????try{
???????rs = ps.executeQuery();
???????while(rs.next()){
????????list.add(rs.getString("address"));
???????}
??????}finally{
???????JdbcUtils.closeResultSet(rs);
??????}
??????return list;
?????}
????}));
posted @
2007-09-10 10:38 華夢行 閱讀(4167) |
評論 (0) |
編輯 收藏