熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

Spring Security學習總結一

2013-11-23 20:24:33  來源: Java開源技術 

  在認識Spring Security之前所有的權限驗證邏輯都混雜在業務邏輯中用戶的每個操作以前可能都需要對用戶是否有進行該項操作的權限進行判斷來達到認證授權的目的類似這樣的權限驗證邏輯代碼被分散在系統的許多地方難以維護AOP(Aspect Oriented Programming)和Spring Security為我們的應用程序很好的解決了此類問題正如系統日志事務管理等這些系統級的服務一樣我們應該將它作為系統一個單獨的切面進行管理以達到業務邏輯與系統級的服務真正分離的目的Spring Security將系統的安全邏輯從業務中分離出來

  本文代碼運行環境

  JDK

  springframework

  springsecurity

  JavaEE

  Web容器

  Apache Tomcat

  IDE工具

  Eclipse+MyEclipse

  操作系統

  Linux(Fedora

  這只是我個人的學習總結而已還請高手們指出本文的不足之處

  一  Spring Security 簡介

  這裡提到的Spring Security也就是被大家廣為熟悉的Acegi Security年底Acegi Security正式成為Spring Portfolio項目並更名為Spring SecuritySpring Security是一個能夠為基於Spring的企業應用系統提供描述性安全訪問控制解決方案的安全框架它提供了一組可以在Spring應用上下文中配置的Bean充分利用了Spring IoC(依賴注入也稱控制反轉)和AOP(面向切面編程)功能為應用系統提供聲明式的安全訪問控制功能減少了為企業系統安全控制編寫大量重復代碼的工作

  通過在許多項目中實踐應用以及社區的貢獻如今的Spring Security已經成為Spring Framework下最成熟的安全系統它為我們提供了強大而靈活的企業級安全服務

  Ø         認證授權機制

  Ø          Web資源訪問控制

  Ø         業務方法調用訪問控制

  Ø         領域對象訪問控制Access Control List(ACL)

  Ø         單點登錄(Central Authentication Service)

  Ø         X認證

  Ø         信道安全(Channel Security)管理等功能

  當保護Web資源時Spring Security使用Servlet 過濾器來攔截Http請求進行身份驗證並強制安全性以確保WEB資源被安全的訪問如下圖是Spring Security的主要組件圖(摘自《Spring in Action》)

  

  圖 Spring Security的基本組件

  無論是保護WEB資源還是保護業務方法或者領域對象Spring Security都的通過上圖中的組件來完成的本文主要闡述如何使用Spring Security對WEB應用程序的資源進行安全訪問控制並通過一個簡單的實例來對Spring Security提供的各種過濾器的功能和配置方法進行描述

  二  保護Web資源

  Spring Security提供了很多的過濾器它們攔截Servlet請求並將這些請求轉交給認證處理過濾器和訪問決策過濾器進行處理並強制安全性認證用戶身份和用戶權限以達到保護Web資源的目的對於Web資源我們大約可以只用個過濾器來保護我們的應用系統下表列出了這些安全過濾器的名稱作用以及它們在系統中的執行順序

  

  過 濾 

  作             

  通道處理過濾器

  確保請求是在安全通道(HTTP和HTTPS)之上傳輸的

  認證處理過濾器

  接受認證請求並將它們轉交給認證管理器進行身份驗證

  CAS處理過濾器

  接受CAS服務票據驗證Yale CAS(單點登錄)是否已經對用戶進行了認證

  HTTP基本授權過濾器

  處理使用HTTP基本認證的身份驗證請求

  集成過濾器

  處理認證信息在請求間的存儲(比如在HTTP會話中)

  安全強制過濾器

  確保用戶己經認證並且滿足訪問一個受保護Web資源的權限需求

  接下來通過一個實例來說明它們的具體使用方法和如何在Spring中進行配置

    建立Spring Security項目

  首先在MyEclipse中創建一個Web Project並使用MyEclipse工具導入Spring項目的依賴JAR包並生成默認的這裡暫時不會用到這個文件本文只是通過一個簡單的實例來說明如何配置使用Spring Security不會涉及到數據庫而是使用一個用戶屬性(usersproperties)文件來保存用戶信息(包括用戶名密碼及相應的權限)但在實際的項目中我們很少會這樣做而是應該把用戶信息存在數據庫中下一篇文章中將會詳細介紹並用到這個文件來配置Hibernate這裡我們保留它

  現在還需要為項目導入Spring Security的JAR包它沒有包括在Spring Framework中你可以從下載並將springsecuritycorejar(這是核心代碼庫)和springsecuritycoretigerjar(和annotation有關的比如使用注解對方法進行安全訪問控制在下一篇中會用到)拷貝到項目的lib目錄下其中也包括兩個實例(tutorial和contacts)並且兩個實例中都包括了如何使用Spring 的命名空間來配置Spring Security無論你對Spring 命名空間的使用是否了解它將使我們的配置文件大大縮短簡化開發提高生產效率到此我們的Spring Security項目就建好了項目目錄結構如下圖所示

  

  圖 項目目錄結構

   配置webxml

  Spring Security使用一組過濾器鏈來對用戶進行身份驗證和授權首先在webxml文件中添加FilterToBeanProxy過濾器配置




  <filter>   
        <filtername>springSecurityFilterChain</filtername>
       <filterclass>
          orgspringframeworksecurityutilFilterToBeanProxy
       </filterclass>
       <initparam>       
            <paramname>targetClass</paramname>
           <paramvalue>           
                orgspringframeworksecurityutilFilterChainProxy
         </paramvalue>
      </initparam>
</filter>

  orgspringframeworksecurityutilFilterToBeanProxy實現了Filter接口它通過調用WebapplicationContextUtils類的getWebApplicationnContext(servletContext)方法來獲取Spring的應用上下文句柄並通過getBean(beanName)方法來獲取Spring受管Bean的對象即這裡targetClass參數配置的Bean並通過調用FilterChain Proxy的init()方法來啟動Spring Security過濾器鏈進行各種身份驗證和授權服務(FilterChainProxy類也是實現了Filter接口)從而將過濾功能委托給Spring的FilterChainProxy受管Bean(它維護著一個處理驗證和授權的過濾器列表列表中的過濾器按照一定的順序執行並完成認證過程)這樣即簡化了webxml文件的配置又能充分利用 Spring的IoC功能來完成這些過濾器執行所需要的其它資源的注入

  當用戶發出請求過濾器需要根據webxml配置的請求映射地址來攔截用戶請求這時Spring Security開始工作它會驗證你的身份以及當前請求的資源是否與你擁有的權限相符從而達到保護Web資源的功能下面是本例所要過濾的用戶請求地址




  <filtermapping>
  
         <filtername>springSecurityFilterChain</filtername>
  
         <urlpattern>/j_spring_security_check</urlpattern>
  
      </filtermapping>
  
      <filtermapping>
 
        <filtername>springSecurityFilterChain</filtername>
 
        <urlpattern>/*</urlpattern>
 
</filtermapping>

   提示
/j_spring_security_check是Spring Security默認的進行表單驗證的過濾地址你也可以修改為別的名稱但是需要和
applicationContextsecurityxml中相對應當然還會涉及到其它一些默認值(可能是一個成員變量也可能是別的請
求地址)在下文我們將看到建議你在閱讀此文的同時應該參照Spring Security項目的源代碼便於你更好的理解  

   配置applicationContextsecurityxml

   FilterChainProxy過濾器鏈

  FilterChainProxy會按順序來調用一組filter使這些filter即能完成驗證授權的本質工作又能享用Spring Ioc的功能來方便的得到其它依賴的資源FilterChainProxy配置如下




  <bean id=filterChainProxy   
        class=orgspringframeworksecurityutilFilterChainProxy>
       <property name=filterInvocationDefinitionSource>
          <value><![CDATA[         
                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 
               PATTERN_TYPE_APACHE_ANT         
                /**=httpSessionContextIntegrationFilterlogoutFilter
               authenticationProcessingFiltersecurityContextHolderAwareRequestFilter
               rememberMeProcessingFilteranonymousProcessingFilterexceptionTranslationFilter
               filterSecurityInterceptor 
          ]]></value>
       </property>
 </bean>

  CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 定義URL在匹配之前必須先轉為小寫PATTERN_TYPE_APACHE_ANT 定義了使用Apache ant的匹配模式/**定義的將等號後面的過濾器應用在那些URL上這裡使用全部URL過濾每個過濾器之間都適用逗號分隔它們按照一定的順序排列

   提示
特別需要注意的是即使你配置了系統提供的所有過濾器這個過濾器鏈會很長但是千萬不要使用換行否則它們不會正常工作
容器甚至不能正常啟動  

  下面根據FilterChainProxy的配置來介紹各個過濾器的配置各個過濾器的執行順序如以上配置

  首先是通道處理過濾器如果你需要使用HTTPS這裡我們就使用HTTP進行傳輸所以不需要配置通道處理過濾器然後是集成過濾器配置如下




 <bean id=httpSessionContextIntegrationFilter
 
 class=orgspringfrntextHttpSessionContextIntegrationFilter/>

  httpSessionContextIntegrationFilter是集成過濾器的一個實現在用戶的一個請求過程中用戶的認證信息通過SecurityContextHolder(使用ThreadLoacl實現)進行傳遞的所有的過濾器都是通過SecurityContextHolder來獲取用戶的認證信息從而在一次請求中所有過濾器都能共享Authentication(認證)減少了HttpRequest參數的傳送下面的代碼是從安全上下文的獲取Authentication對象的方法




 SecurityContext context = SecurityContextHoldergetContext()
 
 Authentication authentication = contextgetAuthentication();

  但是ThreadLoacl不能跨越多個請求存在所以集成過濾器在請求開始時從Http會話中取出用戶認證信息並創建一個SecurityContextHolder將Authentication對象保存在其中在請求結束之後在從SecurityContextHolder中獲取Authentication對象並將其放回Http會話中共下次請求使用從而達到了跨越多個請求的目的集成過濾器還有其它的實現可以參考相關文檔

   提示
集成過濾器必須在其它過濾器之前被使用  

  logoutFilter(退出過濾器) 退出登錄操作




  <bean id=logoutFilter
  
      class=orgspringframeworksecurityuilogoutLogoutFilter>
  
      <constructorarg value=/indexjsp/>
  
      <constructorarg>
  
         <list>
 
            <! 實現了LogoutHandler接口(logout方法) >
 
            <ref bean=rememberMeServices/>
 
            <bean class=orgspringframeworksecurityuilogoutSecurityContextLogoutHandler/>
 
        </list>
 
     </constructorarg>
 
 </bean>

  LogoutFilter的構造函數需要兩個參數第一個是退出系統後系統跳轉到的URL第二個是一個LogoutHandler類型的數組這個數組裡的對象都實現了LogoutHandler接口並實現了它的logout方法用戶在發送退出請求後會一次執行LogoutHandler數組的對象並調用它們的 logout方法進行一些後續的清理操作主要是從SecurityContextHolder對象中清楚所有用戶的認證信息(Authentication對象)將用戶的會話對象設為無效這些都時由SecurityContextLogoutHandler來完成LogoutFilter還會清除Cookie記錄它由另外一個Bean來完成(RememberMeServices)

  <ref bean=rememberMeServices/>標記指向了我們另外配置的一個Bean




 <bean id=rememberMeServices    
        class=orgspringframeworksecurityuiremembermeTokenBasedRememberMeServices
     p:key=springsecurity
    p:userDetailsServiceref=userDetailsService/>

  TokenBasedRememberMeServices繼承自系統的AbstractRememberMeServices抽象類(實現了RememberMeServices和 LogoutHandler兩個接口) RememberMeServices接口的loginSuccess方法負責在用戶成功登錄之後將用戶的認證信息存入Cookie中這個類在後續的過濾器執行過程中也會被用到

  另一個userDetailsService屬性也是指向了我們配置的Bean 它負責從數據庫中讀取用戶的信息這個類的詳細配置將在後面的部分詳細介紹這裡只是簡單的認識一下

  過濾器鏈的下個配置的過濾器是authenticationProcessingFilter(認證過程過濾器)我們使用它來處理表單認證當接受到與filterProcessesUrl所定義相同的請求時它開始工作




  <bean id=authenticationProcessingFilter
  
      class=orgspringframeworksecurityuiwebappAuthenticationProcessingFilter
  
      p:authenticationManagerref=authenticationManager
      p:authenticationFailureUrl=/loginjsp?login_error=
      p:defaultTargetUrl=/defaultjsp
      p:filterProcessesUrl=/j_spring_security_check
      p:rememberMeServicesref=rememberMeServices/>

  下面列出了認證過程過濾器配置中各個屬性的功能

  authenticationManager     認證管理器

  authenticationFailureUrl 定義登錄失敗時轉向的頁面

  defaultTargetUrl         定義登錄成功時轉向的頁面

  filterProcessesUrl        定義登錄請求的地址(在webxml中配置過)

  rememberMeServices        在驗證成功後添加cookie信息

  這裡也用到了rememberMeServices如果用戶認證成功將調用RememberMeServices的loginSuccess方法將用戶認證信息寫入Cookie中這裡也可以看到使用IoC的好處

  決定用戶是否有權限訪問受保護資源的第一步就是要確定用戶的身份最常用的方式就是用戶提供一個用戶名和密碼以確認用戶的身份是否合法這一步就是由認證過程過濾器調用authenticationManager(認證管理器)來完成的orgspringframeworksecurityAuthenticationManager接口定義了一個authenticate方法它使用Authentication作為入口參數(只包含用戶名和密碼)並在驗證成功後返回一個完整的Authentication對象(包含用戶的權限信息GrantedAuthority數組對象)authenticationProcessingFilter(認證過程過濾器)會將這個完整的Authentication對象存入SecurityContext中如果認證失敗會拋出一個AuthenticationException並跳轉到authenticationFailureUrl 定義的URL認證管理其配置如下




  <bean id=authenticationManager
  
  class=orgspringframeworksecurityprovidersProviderManager
   p:sessionControllerref=concurrentSessionController>
      <property name=providers>
         <list>
             <ref bean=daoAuthenticationProvider/>
             <bean
  
 class=orgspringframeworksecurityprovidersanonymousAnonymousAuthenticationProvider
               p:key=springsecurity/>
            <bean
 
 class=orgspringframeworksecurityprovidersremembermeRememberMeAuthenticationProvider
               p:key=springsecurity/>
        </list>
     </property>  
 </bean>

  正如在配置中看到的一樣系統使用orgspringframeworksecurityprovidersProviderManager(提供者管理器)類作為認證管理器的一個實現事實上這個類是繼承自實現了AuthenticationManager接口的 AbstractAuthenticationManager類需要注意的是ProviderManager(提供者管理器)自己並不實現身份驗證而是把這項工作交給了多個認證提供者(提供者集合)或者說的多個認證來源

   提示
Spring Security為我們提供的所有認證提供者實現都是orgspringframeworksecurityproviders AuthenticationProvider
接口的實現類它們都實現了此接口的authenticate方法如果你正在看源代碼會發現這個authenticate方法事實上和Authe
nticationManager(認證管理器)接口的authenticate方法完全一樣  

  providers屬性定義了提供者管理器的集合ProviderManager(提供者管理器)逐一遍歷這個認證提供者的集合並調用提供者的authenticate方法如果一個提供者認證失敗會嘗試另外一個提供者直到某一個認證提供者能夠成功的驗證該用戶的身份以保證獲取不同來源的身份認證下面表格列出了系統提供的一些認證提供者

  

  提   供  

  作            

  DaoAuthenticationProvider

  從數據庫中讀取用戶信息驗證身份

  AnonymousAuthenticationProvider

  匿名用戶身份認證

  RememberMeAuthenticationProvider

  已存cookie中的用戶信息身份認證

  AuthByAdapterProvider

  使用容器的適配器驗證身份

  CasAuthenticationProvider

  根據Yale中心認證服務驗證身份 用於實現單點登陸

  JaasAuthenticationProvider

  從JASS登陸配置中獲取用戶信息驗證身份

  RemoteAuthenticationProvider

  根據遠程服務驗證用戶身份

  RunAsImplAuthenticationProvider

  對身份已被管理器替換的用戶進行驗證

  XAuthenticationProvider

  從X認證中獲取用戶信息驗證身份

  TestingAuthenticationProvider

  單元測試時使用















  從上面的表中可以看出系統為我們提供了不同的認證提供者每個認證提供者會對自己指定的證明信息進行認證如DaoAuthenticationProvider僅對UsernamePasswordAuthenticationToken這個證明信息進行認證

  在實際項目中用戶的身份和權限信息可能存儲在不同的安全系統中(如數據庫LDAP服務器CA中心)

  作為程序員我們可以根據需要選擇不同的AuthenticationProvider(認證提供者)來對自己的系統提供認證服務

  這裡我們著重介紹DaoAuthenticationProvider它從數據庫中讀取用戶信息驗證身份配置如下




 <bean id=daoAuthenticationProvider 
        class=orgspringframeworksecurityprovidersdaoDaoAuthenticationProvider
     p:passwordEncoderref=passwordEncoder
    p:userDetailsServiceref=userDetailsService/>
 <bean id=passwordEncoder
    class=orgspringframeworksecurityprovidersencodingMdPasswordEncoder/>

  還記得前面配置的RememberMeServices嗎?它也有一個和DaoAuthenticationProvider同樣的屬性userDetailsService這是系統提供的一個接口(orgspringframeworksecurityuserdetailsUserDetailsService)在這裡我們把它單獨提出來進行介紹

  首先我們需要了解Spring Security為我們提供的另外一個重要的組件orgspringframeworksecurityuserdetails UserDetails接口它代表一個應用系統的用戶該接口定義與用戶安全信息相關的方法

  String getUsername()獲取用戶名

  String getPassword()獲取密碼

  boolean isAccountNonExpired()用戶帳號是否過期

  boolean isAccountNonLocked()用戶帳號是否鎖定

  boolean isCredentialsNonExpired()用戶的憑證是否過期

  boolean isEnabled()用戶是否處於激活狀態

  當以上任何一個判斷用戶狀態的方法都返回false時用戶憑證就被視為無效UserDetails接口還定義了獲取用戶權限信息的getAuthorities()方法該方法返回一個GrantedAuthority[]數組對象GrantedAuthority是用戶權限信息對象這個對象中定義了一個獲取用戶權限描述信息的getAuthority()方法

  UserDetails即可從數據庫中返回也可以從其它如LDAP中返回這取決與你的系統中使用什麼來存儲用戶信息和權限以及相應的認證提供者這裡我們只重點介紹DaoAuthenticationProvider(從數據庫中獲取用戶認證信息的提供者)本人水平有限在項目中還沒有機會用到其它提供者說到這裡這個封裝了用戶詳細信息的UserDetails該從哪兒獲取呢?這就是我們接下來要介紹的UserDetailsService接口這個接口中只定義了唯一的UserDetails loadUserByUsername(String username)方法它通過用戶名來獲取整個UserDetails對象

  看到這裡你可能會有些糊塗因為前面提到的Authentication對象中也存放了用戶的認證信息需要注意Authentication對象才是Spring Security使用的進行安全訪問控制用戶信息安全對象實際上Authentication對象有未認證和已認證兩種狀態在作為參數傳入認證管理器(AuthenticationManager)的authenticate方法時是一個未認證的對象它從客戶端獲取用戶的身份信息(如用戶名密碼)可以是從一個登錄頁面也可以從Cookie中獲取並由系統自動構造成一個Authentication對象而這裡提到的UserDetails代表一個用戶安全信息的源(從數據庫LDAP服務器CA中心返回)Spring Security要做的就是將這個未認證的Authentication對象和UserDetails進行匹配成功後將UserDetails中的用戶權限信息拷貝到Authentication中組成一個完整的Authentication對象共其它組件共享

  這樣我們就可以在系統中獲取用戶的相關信息了需要使用到Authentication對象定義的Object getPrincipal()方法這個方法返回一個Object類型的對象通常可以將它轉換為UserDetails從而可以獲取用戶名密碼以及權限等信息代碼如下




 UserDetails details = (UserDetails)authenticationgetPrincipal();
 
 GrantedAuthority[] authority = detailsgetAuthorities();

  前面介紹了DaoAuthenticationProvider它可以從數據庫中讀取用戶信息同樣也可以從一個用戶屬性文件中讀取下一篇文章中我們在介紹如何從數據庫中讀取用戶信息當然還會涉及到更深入的東西比如根據自己系統的需要自定義UserDetails和UserDetailsService這個只是讓你對整個系統有個簡單的了解所以我們使用用戶屬性文件(usersproperties)來存儲用戶信息




 admin=adminROLE_SUPERVISOR
 
 user=userROLE_USER
 
 user=userROLE_USER
 
 user=userdisabledROLE_USER

  配置userDetailsService




 <bean id=userDetailsService
 
 class=orgspringframeworksecumoryInMemoryDaoImpl>
      <property name=userProperties>
         <bean class=orgspringframewonfigPropertiesFactoryBean
           p:location=/WEBINF/usersproperties/>
      </property>
</bean>

  InMemoryDaoImpl類是UserDetailsService接口的一個實現它從屬性文件裡讀取用戶信息Spring Security使用一個屬性編輯器將用戶信息為我們組織成一個orgspringframeworksecumoryUserMap類的對象我們也可以直接為它提供一個用戶權限信息的列表詳見applicationContextsecurityxml配置文件

  UserMap字符串的每一行都用鍵值對的形式表示前面是用戶名然後是等號後面是賦予該用戶的密碼/權限等信息它們使用逗號隔開比如




 admin=adminROLE_SUPERVISOR

  定義了一個名為admin的用戶登錄密碼為admin該用戶擁有ROLE_SUPERVISOR權限再如usersproperties文件中配置的名為user的用戶登錄密碼為user該用戶擁有ROLE_USER權限disabled定義該用戶不可用為被激活(UserDetails 的isEnabled方法)

  即使是系統的開發者或者說是最終用戶都不應該看到系統中有明文的密碼所以Spring Security考慮的還是很周到的為我們提供的密碼加密的功能正如你在Dao認證提供者(DaoAuthenticationProvider)中看到的passwordEncoder屬性配置的就是一個密碼加密程序(密碼編碼器)這裡我們使用MD加密可以看配置文件中的scott用戶你還能看出他的密碼是什麼嗎?當然這裡只是演示功能其它用戶還是沒有改變你可以自己試試系統為我們提供了一些常用的密碼編碼器(這些編碼器都位於orgspringframeworksecu rityprovidersencoding包下)

  PlaintextPasswordEncoder(默認)——不對密碼進行編碼直接返回未經改變的密碼

  MdPasswordEncoder ——對密碼進行消息摘要(MD)編碼

  MdPasswordEncoder ——對密碼進行消息摘要(MD)編碼

  ShaPasswordEncoder ——對密碼進行安全哈希算法(SHA)編碼

  你可以根據需要選擇合適的密碼編碼器你也可以設置編碼器的種子源(salt source)一個種子源為編碼提供種子(salt)或者稱編碼的密鑰這裡不再贅述

  這裡附加介紹了不少東西希望你還沒有忘記在AuthenticationManager(認證管理器)中還配置了一個名為sessionController的Bean這個Bean可以阻止用戶在進行了一次成功登錄以後在進行一次成功的登錄在 applicationContextsecurityxml配置文件添加sessionController的配置




 <bean id=concurrentSessionController
 
 class=orgspringfrncurrentConcurrentSessionControllerImpl
     p:maximumSessions=
     p:exceptionIfMaximumExceeded=true
     p:sessionRegistryref=sessionRegistry/>
 <bean id=sessionRegistry
 
 class=orgspringfrncurrentSessionRegistryImpl/>

  maximumSessions屬性配置了只允許同一個用戶登錄系統一次exceptionIfMaximumExceeded屬性配置了在進行第二次登錄是是否讓第一次登錄失效這裡設置為true不允許第二次登錄要讓此功能生效我們還需要在webxml文件中添加一個監聽器以讓Spring Security能獲取Session的生命周期事件配置如下




 <listener>
      <listenerclass>
         orgspringframeworksecurityuisessionHttpSessionEventPublisher
      </listenerclass>
</listener>

  HttpSessionEventPublisher類實現javaxservlethttpHttpSessionListener接口在Session被創建的時候通過調用ApplicationContext的publishEvent(ApplicationEvent event)發布HttpSessionCreatedEvent類型的事件HttpSessionCreatedEvent類繼承自orgntextApplicationEvent類的子類 HttpSessionApplicationEvent抽象類

  concurrentSessionController使用sessionRegistry來完成對發布的Session的生命周期事件的處理orgspringfrncurrentSessionRegistryImpl(實現了SessionRegistry接口) SessionRegistryImpl類還實現了Spring Framework 的事件監聽orgntextApplication Listener接口並實現了該接口定義的onApplicationEvent(ApplicationEvent event)方法用於處理Applic ationEvent類型的事件如果你了解Spring Framework的事件處理那麼這裡你應該可以很好的理解

  認證管理器到此介紹完畢了認證過程過濾器也介紹完了接下來我們繼續介紹過濾器鏈的下一個過濾器securityContextHolderAwareRequestFilter




 <bean id=securityContextHolderAwareRequestFilter
    class=orgspringframeworksecuritywrapperSecurityContextHolderAwareRequestFilter/>

  這個過濾器使用裝飾模式(Decorate Model)裝飾的HttpServletRequest對象其Wapper是ServletRequest包裝類HttpServletRequestWrapper的子類(如SavedRequestAwareWrapper或SecurityContextHolderAwareRequestWrapper)附上獲取用戶權限信息request參數headers 和 cookies 的方法

  rememberMeProcessingFilter過濾器配置

  <bean id=rememberMeProcessingFilter

  class=orgspringframeworksecurityuiremembermeRememberMeProcessingFilter

  pauthenticationManagerref=authenticationManager

  prememberMeServicesref=rememberMeServices/>

  當SecurityContextHolder中不存在Authentication用戶授權信息時rememberMeProcessingFilter就會調用rememberMeServices 的autoLogin()方法從cookie中獲取用戶信息自動登錄

  anonymousProcessingFilter過濾器配置




 <bean id=anonymousProcessingFilter
    class=orgspringframeworksecurityprovidersanonymousAnonymousProcessingFilter
     p:key=springsecurity
    p:userAttribute=anonymousUserROLE_ANONYMOUS/>

  如果不存在任何授權信息時自動添加匿名用戶身份至SecurityContextHolder中就是這裡配置的userAttribute系統為用戶分配一個ROLE_ANONYMOUS權限

  exceptionTranslationFilter(異常處理過濾器)該過濾器用來處理在系統認證授權過程中拋出的異常主要是處理AccessDeniedException和AuthenticationException兩個異常並根據配置跳轉到不同URL




 <bean id=exceptionTranslationFilter
    class=orgspringframeworksecurityuiExceptionTranslationFilter
    p:accessDeniedHandlerref=accessDeniedHandler
      p:authenticationEntryPointref=authenticationEntryPoint/>
   <! 處理AccessDeniedException >
<bean id=accessDeniedHandler
    class=orgspringframeworksecurityuiAccessDeniedHandlerImpl
     p:errorPage=/accessDeniedjsp/>
<bean id=authenticationEntryPoint
     class=orgspringframeworksecurityuiwebappAuthenticationProcessingFilterEntryPoint
    p:loginFormUrl=/loginjsp
    p:forceHttps=false/>

  accessDeniedHandler用於處理AccessDeniedException異常當用戶沒有權限訪問當前請求的資源時拋出此異常並跳轉自這裡配置的/accessDeniedjsp頁面

  authenticationEntryPoint(認證入口點)這裡定義了用戶登錄的頁面系統為我們提供了個認證入口點的實現

  

  認 證 入 口 點

  作           用

  BasicProcessingFilterEntryPoint

  通過向浏覽器發送一個HTTP (未授權)消息由浏覽器彈出登錄對話框提示用戶登錄

  AuthenticationProcessingFilterEntryPoint

  將用戶重定向到一個基於HTML表單的登錄頁面

  CasProcessingFilterEntryPoint

  將用戶重定向至一個Yale CAS登錄頁面

  這裡我們使用AuthenticationProcessingFilterEntryPoint認證入口點提供給用戶一個友好的登錄界面只是為了給用戶更好的體驗

  filterSecurityInterceptor(過濾器安全攔截器)該過濾器首先調用認證管理器來判斷用戶是否已被成功驗證如果沒有被驗證則重定向到登錄界面否則從Authentication獲取用戶的權限信息然後從objectDefinitionSource中獲取URL所對應的權限最後調用accessDecisionManager(訪問決策管理器)來判斷用戶當前擁有的權限是否與當前受保護的URL資源對應的權限匹配如果匹配就可以訪問該URL資源否則將拋出AccessDeniedException異常並返回客戶端浏覽器一個錯誤(如果用戶定義了accessDenied頁面則會被重定向到該頁異常處理過濾器exceptionTranslationFilter中配置的accessDeniedHandler Bean)訪問決策管理的的工作機制將在隨後更詳細介紹這裡先給出過濾器安全攔截器的配置如下




  <bean id=filterSecurityInterceptor
  
      class=orgspringframeworksecurityinterceptwebFilterSecurityInterceptor
  
      p:authenticationManagerref=authenticationManager
  
      p:accessDecisionManagerref=accessDecisionManager>
       <property name=objectDefinitionSource>
          <value><![CDATA[
 
      CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
            PATTERN_TYPE_APACHE_ANT
            /admins/**=ROLE_SUPERVISOR      
             /user/**=ROLE_USERIS_AUTHENTICATED_REMEMBERED       
             /defaultjsp=ROLE_USERIS_AUTHENTICATED_REMEMBERED
           /**=IS_AUTHENTICATED_ANONYMOUSLY
       ]]></value>
     </property>
</bean>

  從配置可以看出來過濾器安全攔截器用到了我們前面配置的認證管理器過濾器安全攔截器使用authenticationManager並調用它的providers(提供者列表)來對用戶的身份進行驗證並獲取用戶擁有的權限如果用戶被成功認證過濾器安全攔截器將會使用accessDecisionManager(訪問決策管理器)來判斷已認證的用戶是否有權限訪問受保護的資源這些受保護的資源由objectDefinitionSource屬性定義

  訪問決策管理器(accessDecisionManager)




  <bean id=accessDecisionManager
      class=orgspringframeworksecurityvoteAffirmativeBased
       p:allowIfAllAbstainDecisions=false>
       <property name=decisionVoters>
          <list>
             <bean class=orgspringframeworksecurityvoteRoleVoter/>
             <bean class=orgspringframeworksecurityvoteAuthenticatedVoter/>
          </list>
       </property>
 </bean>

  身份驗證只是Spring Security安全機制的第一步訪問決策管理器驗證用戶是否有權限訪問相應的資源(filterSecurityInterceptor中objectDefinitionSource屬性定義的訪問URL需要的屬性信息)

  orgspringframeworksecurityAccessDecisionManager接口定義了用於驗證用戶是否有權限訪問受保護資源的decide方法另一個supports方法根據受保護資源的配置屬性(即訪問這些資源所需的權限)來判斷該訪問決策管理器是否能做出針對該資源的訪問決策decide方法最終決定用戶有無訪問權限如果沒有則拋出AccessDeniedException異常(面前也提到過你應該在回過頭去看看)

  與認證管理器類似訪問決策管理器也不是由自己來實現訪問控制的而是通過一組投票者來投票決定(通過調用投票者的vote方法)訪問決策管理器統計投票結果並最終完成決策工作下表列出了系統提供的個訪問決策管理器的實現

  

  訪問決策管理器

  如 何 決 策

  AffirmativeBased

  當至少有一個投票者投允許訪問票時允許訪問

  ConsensusBased

  當所有投票者都投允許訪問票時允許訪問

  UnanimousBased

  當沒有投票者投拒絕訪問票時允許訪問

  decisionVoters屬性為訪問決策管理器定義了一組進行投票工作的投票者那麼這些投票者是如何進行投票的呢?這就需要提orgspringframeworksecurityvoteAccessDecisionVoter接口所有的投票者都實現了這個接口並實現了其中的vote方法該接口中還定義了個int類型的常量

  int ACCESS_GRANTED = (投贊成票)

  int ACCESS_ABSTAIN = (投棄權票)

  int ACCESS_DENIED = (投反對票)

  每個決策投票者都返回這個常量中一個這取決與用戶是否有權限訪問當前請求的資源訪問決策管理器再對這些投票結果進行統計認證投票者的配置如上面所示

  loggerListener是一個可選項它和我們前面配置的Bean或者過濾器沒有關系只是監聽系統的一些事件(實現了ApplicationListener監聽接口)被它監聽的事件包括AuthenticationCredentialsNotFoundEvent事件AuthorizationFailureEvent事件AuthorizedEvent事件PublicInvocationEvent事件相信你從他們的名字就能看出來是一些什麼樣的事件除非你的e文比我還差勁loggerListener配置如下




 <bean id=loggerListener class=orgspringframeworksecurityeventauthenticationLoggerListener/>

  到此本例所涉及到的所有配置都介紹完了在下一篇中會介紹方法安全攔截器以及如何使用它來保護我們的方法調用以及前面提到過的會在下一篇中介紹的這裡不在一一列出

  接下來就是JSP頁面了首先是loginjsp




  <c:if test=${not empty paramlogin_error}>
      登錄失敗請重試錯誤原因:<br/>
      <font color=red>
          <c:if test=${not empty SPRING_SECURITY_LAST_EXCEPTION}>
              <c:out value=${SPRING_SECURITY_LAST_EXCEPTION}></c:out>
          </c:if>
      </font>
  </c:if>
  <form action=<c:url value=/j_spring_security_check/> method=post>
     <table>
         <tr>
             <td><label for=username>username:</label></td>
             <td><input type=text id=username name=j_username
                    value=<c:out value=${SPRING_SECURITY_LAST_USERNAME}/>/></td>
         </tr>
         <tr>
             <td><label for=password>password:</label></td>
             <td><input type=password id=password name=j_password value=/></td>
         </tr>
         <tr><td></td>
             <td><input type=checkbox name=_spring_security_remember_me>兩周內記住我</td>
         </tr>
         <tr><td colspan=><input type=submit value=提交/>
         <input type=reset value=重置/></td></tr>
     </table>
 </form>

  如果你有看源代碼上面的某些參數以及本文所有提及的東西你都不應該感到陌生其它頁面也不在列出了還有就是如何讓它運行起來這些我相信你都能自己搞定

  附件linux/springsecurityrar>springsecurityrar(不包括JAR包)

  補上使用命名空間配置實現的代碼命名空間的詳細資料請參考Spring Security中文參考文檔翻譯得很好這裡就不在累述了配置文件中也有比較詳細的注釋另外例子中還包括了自定義UserDetailService的實現已經如何Ehcache緩存用戶信息詳細的信息將在下一篇中講述

  附件linux/springsecuritynamespacerar>springsecuritynamespacerar(包括部分JAR包)


From:http://tw.wingwit.com/Article/program/Java/ky/201311/28455.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.