<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    tangtb

    Hadoop,SpringMVC,ExtJs,Struts2,Spring,SpringSecurity,Hibernate,Struts
    posts - 25, comments - 88, trackbacks - 0, articles - 0
      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    Spring Security學(xué)習(xí)總結(jié)二

        背景知識(shí)Spring Security 學(xué)習(xí)總結(jié)一

                  SpringSide(你可以去官網(wǎng)了解更多信息,我也是從這里開始了解和學(xué)習(xí)Spring Security的)

    前一篇文章里介紹了Spring Security的一些基礎(chǔ)知識(shí),相信你對(duì)Spring Security的工作流程已經(jīng)有了一定的了解,如果你同時(shí)在讀源代碼,那你應(yīng)該可以認(rèn)識(shí)的更深刻。在這篇文章里,我們將對(duì)Spring Security進(jìn)行一些自定義的擴(kuò)展,比如自定義實(shí)現(xiàn)UserDetailsService,保護(hù)業(yè)務(wù)方法以及如何對(duì)用戶權(quán)限等信息進(jìn)行動(dòng)態(tài)的配置管理。

    說(shuō)明:
    如果你通過(guò)Google搜索,可以找到很 多類似主題的文章,本文的目的在于通過(guò)這些實(shí)例來(lái)介紹的工作
    原理,我覺得這些才是最重要的。相信你在讀完本文之后應(yīng)該可以按照自己的想法去擴(kuò)展Spring Secu
    rity,這也是我寫這兩篇文章的目的。希望能對(duì) 初學(xué)者們有所幫助。
     

    廢話少說(shuō),咱們直接進(jìn)入正題。

    一 自定義UserDetailsService實(shí)現(xiàn)

    UserDetailsService接口,這個(gè)接口中只定義了唯一的UserDetails loadUserByUsername(String username)方法,它通過(guò)用戶名來(lái)獲取整個(gè)UserDetails對(duì)象。

    前一篇文章已經(jīng)介紹了系統(tǒng)提供的默認(rèn)實(shí)現(xiàn)方式InMemoryDaoImpl,它從配置文件中讀取用戶的身份信息(用戶名,密碼等),如果你的客戶想修改用戶信息,就需要直接修改配置文件(你需要告訴用戶配置文件的路徑,應(yīng)該在什么地方修改,如何把明文密碼通過(guò)MD5加密以及如何重啟服務(wù)器等)。聽起來(lái)是不是很費(fèi)勁啊!

    在實(shí)際應(yīng)用中,我們可能需要提供動(dòng)態(tài)的方式來(lái)獲取用戶身份信息,最常用的莫過(guò)于數(shù)據(jù)庫(kù)了,當(dāng)然也可以是LDAP服務(wù)器等。本文首先介紹系統(tǒng)提供的一個(gè)默認(rèn)實(shí)現(xiàn)類JdbcDaoImplorg.springframework.security.userdetails.jdbc. JdbcDaoImpl),它通過(guò)用戶名從數(shù)據(jù)庫(kù)中獲取用戶身份信息,修改配置文件,將userDetailsService Bean的配置修改如下:

    1 <bean id="userDetailsService"
    2 class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl"
    3 p:dataSource-ref="dataSource"
    4 p:usersByUsernameQuery="select userName, passWord, enabled, from users where userName=?"
    5 p:authoritiesByUsernameQuery="select 
    6 u.userName,r.roleName from users u,roles 
    7 r,users_roles ur where u.userId=ur.userId and 
    8 r.roleId=ur.roleId and u.userName=?"/>

    JdbcDaoImpl類繼承自Spring Framework的JdbcDaoSupport類并實(shí)現(xiàn)了UserDetailsService接口,因?yàn)閺臄?shù)據(jù)庫(kù)中讀取信息,所以首先需要一個(gè)數(shù)據(jù)源對(duì)象,這里不在多說(shuō),這里需要重點(diǎn)介紹的是usersByUsernameQueryauthoritiesByUsernameQuery,屬性,它們的值都是一條SQL語(yǔ)句,JdbcDaoImpl類通過(guò)SQL從數(shù)據(jù)庫(kù)中檢索相應(yīng)的信息,usersByUsernameQuery屬性定義了通過(guò)用戶名檢索用戶信息的SQL語(yǔ)句,包括用戶名,密碼以及用戶是否可用,authoritiesByUsernameQuery屬性定義了通過(guò)用戶名檢索用戶權(quán)限信息的SQL語(yǔ)句,這兩個(gè)屬性都引用一個(gè)MappingSqlQuery(請(qǐng)參考Spring Framework相關(guān)資料)實(shí)例,MappingSqlQuerymapRow()方法將一個(gè)ResultSet(結(jié)果集)中的字段映射一個(gè)領(lǐng)對(duì),Spring Security為我們提供了默認(rèn)的數(shù)據(jù)庫(kù)表,如下圖所示(摘自《Spring in Action》)

    1 JdbcDaoImp數(shù)據(jù)庫(kù)表

    如果我們需要獲取用戶的其它信息就需要自己來(lái)擴(kuò)展系統(tǒng)的默認(rèn)實(shí)現(xiàn),首先應(yīng)該了解一下UserDetailsService實(shí)現(xiàn)的原理,還是要回到源代碼,以下是JdbcDaoImpl類的部分代碼:

     1 private class UsersByUsernameMapping extends MappingSqlQuery {
     2 
     3      protected UsersByUsernameMapping(DataSource ds) {
     4 
     5             super(ds, usersByUsernameQuery);
     6 
     7             declareParameter(new SqlParameter(Types.VARCHAR));
     8 
     9             compile();
    10 
    11      }
    12 
    13      protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
    14 
    15             String username = rs.getString(1);
    16 
    17             String password = rs.getString(2);
    18 
    19             boolean enabled = rs.getBoolean(3);
    20 
    21             UserDetails user = new User(username, password, enabled, true,
    22 
    23                truetruenew GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});
    24 
    25             return user;
    26      }
    27 
    28 }

    也許你已經(jīng)看出什么來(lái)了,對(duì)了,系統(tǒng)返回的UserDetails對(duì)象就是從這里來(lái)的,這就是讀源代碼的好處,DaoAuthenticationProvider提供者通過(guò)調(diào)用自己的authenticate(Authentication authentication)方法將用戶在登錄頁(yè)面輸入的用戶信息與這里從數(shù)據(jù)庫(kù)獲取的用戶信息進(jìn)行匹配,如果匹配成功則將用戶的權(quán)限信息賦給Authentication對(duì)象并將其存放在SecurityContext中,供其它請(qǐng)求使用。

    那么我們要擴(kuò)展獲得更多的用戶信息,就要從這里下手了(數(shù)據(jù)庫(kù)表這里不在列出來(lái),可以參考項(xiàng)目的WebRoot/db目錄下的schema.sql文件)。比如我們自己的數(shù)據(jù)庫(kù)設(shè)計(jì)中是通過(guò)一個(gè)loginId和用戶名來(lái)登錄或者我們需要額外ID,EMAIL地址等信息,MySecurityJdbcDaoImpl實(shí)現(xiàn)如下:


     1 protectedclass UsersByUsernameMapping extends MappingSqlQuery {
     2 
     3        protected UsersByUsernameMapping(DataSource ds) {
     4 
     5            super(ds, usersByUsernameQuery);
     6 
     7            declareParameter(new SqlParameter(Types.VARCHAR));
     8 
     9            compile();
    10 
    11        }
    12 
    13        protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
    14 
    15            // TODO Auto-generated method stub
    16 
    17            String userName = rs.getString(1);
    18 
    19            String passWord = rs.getString(2);
    20 
    21            boolean enabled = rs.getBoolean(3);
    22 
    23            Integer userId = rs.getInt(4);
    24 
    25            String email = rs.getString(5);
    26 
    27        MyUserDetails user = new MyUser(userName, passWord, enabled, true
    28                                                true,truenew GrantedAuthority[]{new 
    29                                                GrantedAuthorityImpl("HOLDER")});
    30 
    31            user.setEmail(email);
    32 
    33            user.setUserId(userId);
    34 
    35            return user;
    36 
    37        }
    38 
    39 }

    如果你已經(jīng)看過(guò)源代碼,你會(huì)發(fā)現(xiàn)這里只是其中的一部分代碼 ,具體的實(shí)現(xiàn)請(qǐng)看項(xiàng)目的MySecurityJdbcDaoImpl類實(shí)現(xiàn),以及MyUserDetailsMyUser類,這里步在一一列出。

    如果使用Hibernate來(lái)操作數(shù)據(jù)庫(kù),你也可以從你的DAO中獲取用戶信息,最后你只要將存放了用戶身份信息和權(quán)限信息的列表(List)返回給系統(tǒng)就可以。

    提示:
    這里沒有介紹更多的細(xì)節(jié)問題,主要還是想你自己能通過(guò)讀源代碼來(lái)加深理解,本人水平也有限,
    相信你從源代碼中能悟出更多的有價(jià)值的東西。
     

        每當(dāng)用戶請(qǐng)求一個(gè)受保護(hù)的資源時(shí),就會(huì)調(diào)用認(rèn)證管理器以獲取用戶認(rèn)證信息,但是如果我們的用戶信息保存在數(shù)據(jù)庫(kù)中,那么每次請(qǐng)求都從數(shù)據(jù)庫(kù)中獲取信息將會(huì)影響系統(tǒng)性能,那么將用戶信息進(jìn)行緩存就有必要了,下面就介紹如何在Spring Security中使用緩存。

    緩存用戶信息

    查看AuthenticationProvider接口的實(shí)現(xiàn)類AbstractUserDetailsAuthenticationProvider抽象類(我們配置文件中配置的DaoAuthenticationProvider類繼承了該類)的源代碼,會(huì)有一行代碼:

    1 UserDetails user = this.userCache.getUserFromCache(username);

    DaoAuthenticationProvider認(rèn)證提供者使用UserCache接口的實(shí)現(xiàn)來(lái)實(shí)現(xiàn)對(duì)用戶信息的緩存,修改DaoAuthenticationProvider的配置如下:

    1 <bean id="daoAuthenticationProvider"
    2 class="org.springframework.security.providers.dao.DaoAuthenticationProvider"
    3     p:userCache-ref="userCache"
    4     p:passwordEncoder-ref="passwordEncoder"
    5     p:userDetailsService- ref="userDetailsService"/>

    這里我們加入了對(duì)userCache Bean的引用,userCache使用Ehcache來(lái)實(shí)現(xiàn)對(duì)用戶信息的緩存。userCache配置如下:

     1 <bean id="userCache"
     2 class="org.springframework.security.providers.dao.cache.EhCacheBasedUserCache"
     3     p:cache-ref="cache"/>
     4 <bean id="cache"
     5 class="org.springframework.cache.ehcache.EhCacheFactoryBean"
     6     p:cacheManager-ref="cacheManager"
     7     p:cacheName="userCache"/>
     8 <bean id="cacheManager"
     9 class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
    10     p:configLocation="classpath:ehcache.xml">
    11 </bean>

        我們這里使用的是EhCacheBasedUserCache,也就是用EhCache實(shí)現(xiàn)緩存的,另外系統(tǒng)還提供了一個(gè)默認(rèn)的實(shí)現(xiàn)類NullUserCache類,我們可以通過(guò)源代碼了解到,無(wú)論上面使用這個(gè)類都返回一個(gè)null值,也就是不使用緩存。

    保護(hù)業(yè)務(wù)方法

    從第一篇文章中我們已經(jīng)了解到,Spring Security使用Servlet過(guò)濾器來(lái)攔截用戶的請(qǐng)求來(lái)保護(hù)WEB資源,而這里卻是使用Spring 框架的AOP來(lái)提供對(duì)方法的聲明式保護(hù)。它通過(guò)一個(gè)攔截器來(lái)攔截方法調(diào)用,并調(diào)用方法安全攔截器來(lái)保護(hù)方法。

    在介紹之前,我們先回憶一下過(guò)濾器安全攔截器是如何工作的。過(guò)濾器安全攔截器首先調(diào)用AuthenticationManager認(rèn)證管理器認(rèn)證用戶信息,如果用過(guò)認(rèn)證則調(diào)用AccessDecisionManager訪問決策管理器來(lái)驗(yàn)證用戶是否有權(quán)限訪問objectDefinitionSource中配置的受保護(hù)資源。

    首先看看如何配置方法安全攔截器,它和過(guò)濾器安全攔截器一方繼承自AbstractSecurityInterceptor抽象類(請(qǐng)看源代碼),如下:

    1 <bean id="methodSecurityInterceptor"
    2 class="org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor"
    3     p:authenticationManager-ref="authenticationManager"
    4     p:accessDecisionManager-ref="accessDecisionManager">
    5     <property name="objectDefinitionSource">
    6        <value>   
                  com.test.service.UserService.get*=ROLE_SUPERVISOR
    7        </value>
    8     </property>
    9 </bean>

    這段代碼是不是很眼熟啊,哈哈~,這和我們配置的過(guò)濾器安全攔截器幾乎完全一樣,方法安全攔截器的處理過(guò)程實(shí)際和過(guò)濾器安全攔截器的實(shí)現(xiàn)機(jī)制是相同的,這里就在累述,詳細(xì)介紹請(qǐng)參考< Spring Security 學(xué)習(xí)總結(jié)一>中相關(guān)部分。但是也有不同的地方,那就是這里的objectDefinitionSource的配置,在等號(hào)前面的不在是URL資源,而是需要保護(hù)的業(yè)務(wù)方法,等號(hào)后面還是訪問該方法需要的用戶權(quán)限。我們這里配置的com.test.service.UserService.get*表示對(duì)com.test.service包下UserService類的所有以get開頭的方法都需要ROLE_SUPERVISOR權(quán)限才能調(diào)用。這里使用了提供的實(shí)現(xiàn)方法MethodSecurityInterceptor,系統(tǒng)還給我們提供了aspectj的實(shí)現(xiàn)方式,這里不在介紹(我也正在學(xué)),讀者可以參考其它相關(guān)資料。

    之前已經(jīng)提到過(guò)了,Spring Security使用Spring 框架的AOP來(lái)提供對(duì)方法的聲明式保護(hù),即攔截方法調(diào)用,那么接下來(lái)就是創(chuàng)建一個(gè)攔截器,配置如下:

     1 <bean id="autoProxyCreator"
     2 class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
     3     <property name="interceptorNames">
     4        <list>
     5        <value>methodSecurityInterceptor</value>
     6        </list>
     7     </property>
     8     <property name="beanNames">
     9        <list>
    10            <value>userService</value>
    11        </list>
    12     </property>
    13 </bean>

    userService是我們?cè)赼pplicationContext.xml中配置的一個(gè)Bean,AOP的知識(shí)不是本文介紹的內(nèi)容。到這里保護(hù)業(yè)務(wù)方法的配置就介紹完了。

    將資源放在數(shù)據(jù)庫(kù)中

    現(xiàn)在,你的用戶提出了新的需求,它們需要自己可以給系統(tǒng)用戶分配或者取消權(quán)限。其實(shí)這個(gè)并不是什么新鮮事,作為開發(fā)者,你也應(yīng)該為用戶提供這樣的功能。那么我們就需要這些受保護(hù)的資源和用戶權(quán)限等信息都是動(dòng)態(tài)的,你可以選擇把它們存放在數(shù)據(jù)庫(kù)中或者LDAP服務(wù)器上,本文以數(shù)據(jù)庫(kù)為例,介紹如何實(shí)現(xiàn)用戶權(quán)限的動(dòng)態(tài)控制。

    通過(guò)前面的介紹,你可能也注意到了,不管是MethodSecurityInterceptor還是FilterSecurityInterceptor都使用authenticationManageraccessDecisionManager屬性用于驗(yàn)證用戶,并且都是通過(guò)使用objectDefinitionSource屬性來(lái)定義受保護(hù)的資源。不同的是過(guò)濾器安全攔截器將URL資源與權(quán)限關(guān)聯(lián),而方法安全攔截器將業(yè)務(wù)方法與權(quán)限關(guān)聯(lián)。

    你猜對(duì)了,我們要做的就是自定義這個(gè)objectDefinitionSource的實(shí)現(xiàn),首先讓我們來(lái)認(rèn)識(shí)一下系統(tǒng)為我們提供的ObjectDefinitionSource接口,objectDefinitionSource屬性正是指向此接口的實(shí)現(xiàn)類。該接口中定義了3個(gè)方法,ConfigAttributeDefinition getAttributes(Object object)方法用戶獲取保護(hù)資源對(duì)應(yīng)的權(quán)限信息,該方法返回一個(gè)ConfigAttributeDefinition對(duì)象(位于org.springframework.security包下),通過(guò)源代碼我們可以知道,該對(duì)象中實(shí)際就只有一個(gè)List列表,我們可以通過(guò)使用ConfigAttributeDefinition類的構(gòu)造函數(shù)來(lái)創(chuàng)建這個(gè)List列表,這樣,安全攔截器就通過(guò)調(diào)用getAttributes(Object object)方法來(lái)獲取ConfigAttributeDefinition對(duì)象,并將該對(duì)象和當(dāng)前用戶擁有的Authentication對(duì)象傳遞給accessDecisionManager(訪問決策管理器,請(qǐng)查看org.springframework.security.vote包下的AffirmativeBased類,該類是訪問決策管理器的一個(gè)實(shí)現(xiàn)類,它通過(guò)一組投票者來(lái)決定用戶是否有訪問當(dāng)前請(qǐng)求資源的權(quán)限),訪問決策管理器在將其傳遞給AffirmativeBased類維護(hù)的投票者,這些投票者從ConfigAttributeDefinition對(duì)象中獲取這個(gè)存放了訪問保護(hù)資源需要的權(quán)限信息的列表,然后遍歷這個(gè)列表并與Authentication對(duì)象中GrantedAuthority[]數(shù)據(jù)中的用戶權(quán)限信息進(jìn)行匹配,如果匹配成功,投票者就會(huì)投贊成票,否則就投反對(duì)票,最后訪問決策管理器來(lái)統(tǒng)計(jì)這些投票決定用戶是否能訪問該資源。是不是又覺得亂了,還是那句話,如果你結(jié)合源代碼你現(xiàn)在一定更明白了。

    說(shuō)了這么些,那我們到底應(yīng)該如何來(lái)實(shí)現(xiàn)這個(gè)ObjectDefinitionSource接口呢?

    首先還是說(shuō)說(shuō)Acegi Seucrity 1.x版本,org.acegisecurity.intercept.weborg.acegisecurity.intercept.method包下AbstractFilterInvocationDefinitionSourceAbstractMethodDefinitionSource兩個(gè)抽象類,這兩個(gè)類分別實(shí)現(xiàn)了FilterInvocationDefinitionSourceMethodDefinitionSource接口,而這兩個(gè)接口都繼承自O(shè)bjectDefinitionSource接口并實(shí)現(xiàn)了其中的方法。兩個(gè)抽象類都使用方法模板模式來(lái)實(shí)現(xiàn),將具體的實(shí)現(xiàn)方法交給了子類。

    提示:
    兩個(gè)抽象類實(shí)現(xiàn)了各自接口的 getAttributes(Object object)方法并在此方法中調(diào)用lookupAttributes
    (Method method)
    方法,而實(shí)際該方法在抽象類中并沒有具體的實(shí)現(xiàn),而是留給了子類去實(shí)現(xiàn)。
     

    在Acegi Seucrity 1.x版本中,系統(tǒng)為我們提供了默認(rèn)的實(shí)現(xiàn),MethodDefinitionMap類用于返回方法的權(quán)限信息,而PathBasedFilterInvocationDefinitionMap類和RegExpBasedFilterInvocationDefinitionMap類用于返回URL資源對(duì)應(yīng)的權(quán)限信息,也就是ConfigAttributeDefinition對(duì)象,現(xiàn)在也許明白一點(diǎn)兒了吧,我們只要按照這三個(gè)類的實(shí)現(xiàn)方式(也就是”模仿”,從后面的代碼中你可以看到)從數(shù)據(jù)庫(kù)中獲取用戶信息和權(quán)限信息然后封裝成一個(gè)ConfigAttributeDefinition對(duì)象返回即可(其實(shí)就是一個(gè)List列表,前面已經(jīng)介紹過(guò)了),相信通過(guò)Hibernate從數(shù)據(jù)庫(kù)中獲取一個(gè)列表應(yīng)該是再容易不過(guò)的了。

    回到Spring Security,系統(tǒng)為我們提供的默認(rèn)實(shí)現(xiàn)有些變化,DefaultFilterInvocationDefinitionSourceDelegatingMethodDefinitionSource兩個(gè)類,從名字也可以看出來(lái)它們分別是干什么的了。這兩個(gè)類分別實(shí)現(xiàn)了FilterInvocationDefinitionSourceMethodDefinitionSource接口,而這兩個(gè)接口都繼承自O(shè)bjectDefinitionSource接口并實(shí)現(xiàn)了其中的方法,這和1.x版本中一樣。它們都是從配置文件中得到資源和相應(yīng)權(quán)限的信息。

    通過(guò)上面的介紹,你或許更名白了一些,那我們下面要做的就是實(shí)現(xiàn)系統(tǒng)的FilterInvocationDefinitionSourceMethodDefinitionSource接口,只是數(shù)據(jù)源不是從配置文件中讀取配置信息是數(shù)據(jù)庫(kù)而已。

    我們這里對(duì)比著Acegi Seucrity 1.x版本中的實(shí)現(xiàn),我個(gè)人認(rèn)為它更好理解,還是請(qǐng)你好好看看源代碼。

    1 自定義FilterInvocationDefinitionSource

    在2.0中,系統(tǒng)沒有在系統(tǒng)抽象類,所以我們還是使用1.x中的實(shí)現(xiàn)方式,首先通過(guò)一個(gè)抽象類來(lái)實(shí)現(xiàn)ObjectDefinitionSource接口。代碼如下:

     1 public ConfigAttributeDefinition getAttributes(Object object)
     2 
     3            throws IllegalArgumentException {
     4 
     5        if (object == null || !(this.supports(object.getClass()))) {
     6 
     7            thrownew IllegalArgumentException("Object must be a FilterInvocation");
     8 
     9        }
    10 
    11        String url = ((FilterInvocation)object).getRequestUrl();
    12 
    13        returnthis.lookupAttributes(url);
    14 
    15     }
    16 
    17 publicabstract ConfigAttributeDefinition lookupAttributes(String url);
    18 
    19 @SuppressWarnings("unchecked")
    20 
    21 publicabstract Collection getConfigAttributeDefinitions();
    22 
    23 @SuppressWarnings("unchecked")
    24 
    25 publicboolean supports(Class clazz) {
    26 
    27     return FilterInvocation.class.isAssignableFrom(clazz);
    28 
    29 }

    這段代碼你也可以在1.0中找到,getAttributes方法的入口參數(shù)是一個(gè)Object對(duì)象,這是由系統(tǒng)傳給我們的,因?yàn)槭荱RL資源的請(qǐng)求,所有可以將這個(gè)Object對(duì)象強(qiáng)制轉(zhuǎn)換為FilterInvocation對(duì)象,并通過(guò)調(diào)用它的getRequestUrl()方法來(lái)獲取用戶當(dāng)前請(qǐng)求的URL地址,然后調(diào)用子類需要實(shí)現(xiàn)的lookupAttributes方法并將該URL地址作為參數(shù)傳給該方法,下面是具體的實(shí)現(xiàn)類DataBaseFilterInvocationDefinitionSource類的代碼,也就是我們需要實(shí)現(xiàn)抽象父類的lookupAttributes方法:


      1 @Override
      2 
      3 public ConfigAttributeDefinition lookupAttributes(String url) {
      4 
      5        // TODO Auto-generated method stub
      6 
      7        //初始化數(shù)據(jù),從數(shù)據(jù)庫(kù)讀取
      8 
      9     cacheManager.initResourceInCache();
     10 
     11     if (isUseAntPath()) {
     12 
     13        int firstQuestionMarkIndex = url.lastIndexOf("?");
     14 
     15        if (firstQuestionMarkIndex != -1) {
     16 
     17            url = url.substring(0, firstQuestionMarkIndex);
     18 
     19        }
     20 
     21     }
     22 
     23     //將URL在比較前都轉(zhuǎn)換為小寫
     24 
     25     if (isConvertUrlToLowercaseBeforeComprison()) {
     26 
     27        url = url.toLowerCase();
     28 
     29     }
     30 
     31     //獲取所有的URL
     32 
     33     List<String> urls = cacheManager.getUrlResources();
     34 
     35     //倒敘排序--如果不進(jìn)行排序,如果用戶使用瀏覽器的導(dǎo)航工具訪問頁(yè)面可能出現(xiàn)問題
     36 
     37     //例如:訪問被拒絕后用戶刷新頁(yè)面
     38 
     39     Collections.sort(urls);
     40 
     41     Collections.reverse(urls);
     42 
     43     GrantedAuthority[] authorities = new GrantedAuthority[0];
     44 
     45     //將請(qǐng)求的URL與配置的URL資源進(jìn)行匹配,并將正確匹配的URL資源對(duì)應(yīng)的權(quán)限
     46 
     47     //取出
     48 
     49     for (String resourceName_url : urls) {
     50 
     51        boolean matched = false;
     52 
     53        //使用ant匹配URL
     54 
     55        if (isUseAntPath()) {
     56 
     57            matched = pathMatcher.match(resourceName_url, url);
     58 
     59        } else {//perl5編譯URL
     60 
     61            Pattern compliedPattern = null;
     62 
     63            Perl5Compiler compiler = new Perl5Compiler();
     64 
     65            try {
     66 
     67               compliedPattern = compiler.compile(resourceName_url, Perl5Compiler.READ_ONLY_MASK);
     68 
     69            } catch (MalformedPatternException e) {
     70 
     71               e.printStackTrace();
     72 
     73            }
     74 
     75            matched = matcher.matches(url, compliedPattern);
     76 
     77        }
     78 
     79        //匹配正確,獲取響應(yīng)權(quán)限
     80 
     81        if (matched) {
     82 
     83            //獲取正確匹配URL資源對(duì)應(yīng)的權(quán)限
     84 
     85            ResourcDetail detail = cacheManager.getResourcDetailFromCache(resourceName_url);
     86 
     87            authorities = detail.getAuthorities();
     88 
     89            break;
     90 
     91        }
     92 
     93 }
     94 
     95        //將權(quán)限封裝成ConfigAttributeDefinition對(duì)象返回(使用ConfigAttributeEditor)
     96 
     97        if (authorities.length > 0) {
     98 
     99            String authTemp = "";
    100 
    101            for (GrantedAuthority grantedAuthority : authorities) {
    102 
    103               authTemp += grantedAuthority.getAuthority() + ",";
    104 
    105            }
    106 
    107            String authority = authTemp.substring(0, (authTemp.length() - 1));
    108 
    109            System.out.println(authority);
    110 
    111            ConfigAttributeEditor attributeEditor = new ConfigAttributeEditor();
    112 
    113            attributeEditor.setAsText(authority.trim());
    114 
    115            return (ConfigAttributeDefinition)attributeEditor.getValue();
    116 
    117        }
    118 
    119        returnnull;
    120 
    121  }

    我們這里同樣使用了緩存,它參考自系統(tǒng)的UseCache接口的實(shí)現(xiàn),這里不在介紹,你可以查看本例的源代碼和系統(tǒng)的實(shí)現(xiàn)和本例的配置文件。這里將用戶請(qǐng)求的URL地址與從數(shù)據(jù)庫(kù)中獲取的受保護(hù)的URL資源使用ant和perl5匹配(這取決與你的配置),如果匹配成功則從緩存中獲取訪問該資源需要的權(quán)限信息,并將其封裝成ConfigAttributeDefinition對(duì)象返回,這里使用org.springframework.security.ConfigAttributeEditor類,該類提供了一個(gè)setAsText(String s),該方法收取一個(gè)字符串作為參數(shù),在該方法中創(chuàng)建ConfigAttributeDefinition對(duì)象并將字符串參數(shù)傳遞給ConfigAttributeDefinition類的構(gòu)造函數(shù)來(lái)初始化該對(duì)象。詳細(xì)的實(shí)現(xiàn)還是請(qǐng)你看源代碼。現(xiàn)在我們?cè)谂渲梦募砑幼约旱膶?shí)現(xiàn),如下:

    1 <bean id="objectDefinitionSource"
    2      class="org.security.intercept.web.DataBaseFilterInvocationDefinitionSource"
    3      p:convertUrlToLowercaseBeforeComprison="true"
    4      p:useAntPath="true"
    5      p:cacheManager-ref="securityCacheManager"/>

        convertUrlToLowercaseBeforeComprison屬性定義了在匹配之前將URL都轉(zhuǎn)換為小寫,useAntPath屬性定義使用Ant方式匹配URL,cacheManager屬性定義了指向另一個(gè)Bean的引用,我們使用它從緩存中獲取相應(yīng)的信息。

    2 自定義MethodDefinitionSource

    將方法資源存放在數(shù)據(jù)庫(kù)中的實(shí)現(xiàn)與URL資源類似,這里不在累述,下面是DataBaseMethodInvocationDefinitionSource源代碼,讀者可以參考注釋進(jìn)行閱讀(該類也是繼承自一個(gè)自定義的抽象類AbstractMethodDefinitionSource):

     1 public ConfigAttributeDefinition lookupAttributes(Method method, Class targetClass) {
     2 
     3        // TODO Auto-generated method stub
     4 
     5        //初始化資源并緩存
     6 
     7        securityCacheManager.initResourceInCache();
     8 
     9        //獲取所有方法資源
    10 
    11        List<String> methods = securityCacheManager.getMethodResources();
    12 
    13        //權(quán)限集合
    14 
    15        Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
    16 
    17        //遍歷方法資源,并獲取匹配的資源名稱,然后從緩存中獲取匹配正確
    18 
    19        //的資源對(duì)應(yīng)的權(quán)限(ResourcDetail對(duì)象的GrantedAuthority[]對(duì)象數(shù)據(jù))
    20 
    21        for (String resourceName_method : methods) {
    22 
    23            if (isMatch(targetClass, method, resourceName_method)) {
    24 
    25               ResourcDetail detail = securityCacheManager.getResourcDetailFromCache(resourceName_method);
    26 
    27               if (detail == null) {
    28 
    29                   break;
    30 
    31               }
    32 
    33               GrantedAuthority[] authorities = detail.getAuthorities();
    34 
    35               if (authorities == null || authorities.length == 0) {
    36 
    37                   break;
    38 
    39               }
    40 
    41               authSet.addAll(Arrays.asList(authorities));
    42 
    43            }
    44 
    45        }
    46 
    47        if (authSet.size() > 0) {
    48 
    49            String authString = "";
    50 
    51            for (GrantedAuthority grantedAuthority : authSet) {
    52 
    53               authString += grantedAuthority.getAuthority() + ",";
    54 
    55            }
    56 
    57            String authority = authString.substring(0, (authString.length() - 1));
    58 
    59            System.out.println(">>>>>>>>>>>>>>>" + authority);
    60 
    61            ConfigAttributeEditor attributeEditor = new ConfigAttributeEditor();
    62 
    63            attributeEditor.setAsText(authority.trim());
    64 
    65            return (ConfigAttributeDefinition)attributeEditor.getValue();
    66 
    67        }
    68 
    69        returnnull;
    70 
    71 }

        isMatch方法用于對(duì)用戶當(dāng)前調(diào)用的方法與受保護(hù)的方法進(jìn)行匹配,與URL資源類似,請(qǐng)參考代碼。下面是applicationContext-security.xml文件中的配置,請(qǐng)查看該配置文件。

    1 <bean id="methodDefinitionSource"
    2     class="org.security.intercept.method.DataBaseMethodInvocationDefinitionSource"
    3     p:securityCacheManager-ref="securityCacheManager"/>

    securityCacheManager屬性定義了指向另一個(gè)Bean的引用,我們使用它從緩存中獲取相應(yīng)的信息。這個(gè)Bean和前一節(jié)中介紹的一樣。只是這里我們獲取的是方法保護(hù)定義資源。

        本文到此也結(jié)束了,還請(qǐng)各位多指教。

        附件:springsecurity2.rar


    評(píng)論

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2008-09-02 01:32 by 達(dá)達(dá)尼奧
    正好用到acegi,非常感謝博主的文章,價(jià)值很大

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2008-09-02 21:09 by 藍(lán)劍
    我看了源碼,和2.0的配置以及代碼大不一樣,還是1.0的風(fēng)格。
    特別是資源存儲(chǔ)在數(shù)據(jù)庫(kù)的部分,在2.0中已經(jīng)不需要Cache了,
    因?yàn)?.0的存放在一個(gè)內(nèi)存Map中。另外你說(shuō)的那個(gè)Editor好像在2.0中
    已經(jīng)過(guò)期不再使用了。

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2008-10-08 21:15 by ss2
    作者的內(nèi)容很out

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二[未登錄]  回復(fù)  更多評(píng)論   

    2008-10-10 17:23 by 呵呵
    你們老說(shuō)ACEGI好,我真不明白
    配置了老半天,一大堆類和轉(zhuǎn)來(lái)轉(zhuǎn)去,就只做了個(gè)身份認(rèn)證,然后自定義鑒權(quán),又復(fù)雜,細(xì)粒度鑒權(quán)更麻煩,福勒。

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二[未登錄]  回復(fù)  更多評(píng)論   

    2008-10-10 17:27 by 呵呵
    補(bǔ)充下:一個(gè)請(qǐng)求頁(yè)面經(jīng)過(guò)那么多個(gè)過(guò)濾器,性能好不了哪里,做辦公軟件沒所謂,大型網(wǎng)站恐怕不行

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2008-10-16 16:59 by cwfmaker
    樓主你好,我將你的這套機(jī)制移成功植到了我的系統(tǒng)中,但是在對(duì)方法進(jìn)行攔截的時(shí)候會(huì)有出現(xiàn)很奇怪的現(xiàn)象,沒有進(jìn)行攔截設(shè)置的方法在不管以什么身份進(jìn)行訪問的時(shí)候都會(huì)提示“cannot invoke method **”
    這個(gè)是否跟org.springframework.transaction.interceptor.TransactionProxyFactoryBean
    這個(gè)類對(duì)應(yīng)的攔截器有關(guān)系?
    如果方便的話請(qǐng)指教一下,謝謝!
    我的QQ:250996858

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2008-12-15 22:27 by 冰河快狼
    不錯(cuò),學(xué)習(xí)了

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2009-03-05 14:31 by wxy
    能把全部代碼傳上來(lái)嗎,要不發(fā)郵箱里,547685189@qq.com 謝謝,我沒調(diào)通,笨點(diǎn)。里面有錯(cuò)。

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2009-04-16 11:02 by 劉俊杰
    能把代碼全部傳給我嗎?我的email:ljh0242@163.com,QQ:178070373

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2010-01-25 11:17 by exile
    email: fordpppp@126.com

    大哥大哥 給我一份資料~~~~謝謝~

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2012-03-26 13:44 by martin1988
    一直覺得相比技術(shù)牛人,一個(gè)技術(shù)過(guò)硬并授人以漁的人更值得尊敬,尤其是影響甚遠(yuǎn)的!
    博主算其一!

    # re: 【總結(jié)-含源碼】Spring Security學(xué)習(xí)總結(jié)二  回復(fù)  更多評(píng)論   

    2012-09-27 14:42 by ww
    "一直覺得相比技術(shù)牛人,一個(gè)技術(shù)過(guò)硬并授人以漁的人更值得尊敬,尤其是影響甚遠(yuǎn)的!
    博主算其一!
    "
    ss3已經(jīng)出現(xiàn)了,學(xué)習(xí)中。。。等我弄明白了 也授之以漁
    主站蜘蛛池模板: 岛国岛国免费V片在线观看| 亚洲冬月枫中文字幕在线看| 亚洲精品色在线网站| 67194国产精品免费观看| 亚洲精品无码久久久久去q| 最新亚洲成av人免费看| 亚洲AV午夜成人片| 中文字幕成人免费高清在线| 亚洲色偷拍另类无码专区| 久久久国产精品福利免费| 亚洲色欲色欲综合网站| av无码国产在线看免费网站| 亚洲人xxx日本人18| 免费黄网在线观看| 国产成人 亚洲欧洲| 久久亚洲精品无码观看不卡| 可以免费观看的毛片| 91亚洲精品自在在线观看| 成年男女男精品免费视频网站 | 亚洲专区在线视频| 在线看无码的免费网站| 日本亚洲免费无线码| 日本免费的一级v一片| 黄色视屏在线免费播放| 狠狠色伊人亚洲综合成人| 久久免费看黄a级毛片| 亚洲av日韩aⅴ无码色老头| 亚洲无码日韩精品第一页| 久久国产精品一区免费下载| 亚洲小视频在线播放| 大胆亚洲人体视频| 日韩精品极品视频在线观看免费| 中国亚洲呦女专区| 国产亚洲精品看片在线观看| 69av免费观看| 性生大片视频免费观看一级 | 亚洲精品一卡2卡3卡四卡乱码| 亚洲毛片不卡av在线播放一区| 最近中文字幕mv免费高清在线 | 亚洲夂夂婷婷色拍WW47| 中文字幕第13亚洲另类|