久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

在 Web 項目中應(yīng)用 Apache Shiro

 時間要去哪 2014-02-08

用戶權(quán)限模型

在揭開 Shiro 面紗之前,,我們需要認知用戶權(quán)限模型。本文所提到用戶權(quán)限模型,,指的是用來表達用戶信息及用戶權(quán)限信息的數(shù)據(jù)模型,。即能證明“你是誰,?”、“你能訪問多少受保護資源,?”,。為實現(xiàn)一個較為靈活的用戶權(quán)限數(shù)據(jù)模型,通常把用戶信息單獨用一個實體表示,,用戶權(quán)限信息用兩個實體表示,。

  1. 用戶信息用 LoginAccount 表示,最簡單的用戶信息可能只包含用戶名 loginName 及密碼 password 兩個屬性,。實際應(yīng)用中可能會包含用戶是否被禁用,,用戶信息是否過期等信息。
  2. 用戶權(quán)限信息用 Role 與 Permission 表示,,Role 與 Permission 之間構(gòu)成多對多關(guān)系,。Permission 可以理解為對一個資源的操作,Role 可以簡單理解為 Permission 的集合,。
  3. 用戶信息與 Role 之間構(gòu)成多對多關(guān)系,。表示同一個用戶可以擁有多個 Role,一個 Role 可以被多個用戶所擁有,。
圖 1. 用戶權(quán)限模型
圖 1. 用戶權(quán)限模型

回頁首

認證與授權(quán)

Shiro 認證與授權(quán)處理過程

  • 被 Shiro 保護的資源,,才會經(jīng)過認證與授權(quán)過程。使用 Shiro 對 URL 進行保護可以參見“與 Spring 集成”章節(jié),。
  • 用戶訪問受 Shiro 保護的 URL,;例如 http://host/security/action.do。
  • Shiro 首先檢查用戶是否已經(jīng)通過認證,,如果未通過認證檢查,,則跳轉(zhuǎn)到登錄頁面,否則進行授權(quán)檢查,。認證過程需要通過 Realm 來獲取用戶及密碼信息,通常情況我們實現(xiàn) JDBC Realm,,此時用戶認證所需要的信息從數(shù)據(jù)庫獲取,。如果使用了緩存,除第一次外用戶信息從緩存獲取,。
  • 認證通過后接受 Shiro 授權(quán)檢查,,授權(quán)檢查同樣需要通過 Realm 獲取用戶權(quán)限信息。Shiro 需要的用戶權(quán)限信息包括 Role 或 Permission,,可以是其中任何一種或同時兩者,,具體取決于受保護資源的配置。如果用戶權(quán)限信息未包含 Shiro 需要的 Role 或 Permission,,授權(quán)不通過,。只有授權(quán)通過,,才可以訪問受保護 URL 對應(yīng)的資源,否則跳轉(zhuǎn)到“未經(jīng)授權(quán)頁面”,。

Shiro Realm

在 Shiro 認證與授權(quán)處理過程中,,提及到 Realm。Realm 可以理解為讀取用戶信息,、角色及權(quán)限的 DAO,。由于大多 Web 應(yīng)用程序使用了關(guān)系數(shù)據(jù)庫,因此實現(xiàn) JDBC Realm 是常用的做法,,后面會提到 CAS Realm,,另一個 Realm 的實現(xiàn)。

清單 1. 實現(xiàn)自己的 JDBC Realm
 public class MyShiroRealm extends AuthorizingRealm{ 
   
   // 用于獲取用戶信息及用戶權(quán)限信息的業(yè)務(wù)接口
   private BusinessManager businessManager; 
    
   // 獲取授權(quán)信息
   protected AuthorizationInfo doGetAuthorizationInfo( 
      PrincipalCollection principals) { 
      String username = (String) principals.fromRealm( 
         getName()).iterator().next(); 
      
      if( username != null ){ 
      // 查詢用戶授權(quán)信息
         Collection<String> pers=businessManager.queryPermissions(username); 
         if( pers != null && !pers.isEmpty() ){ 
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 
            for( String each:pers ) 
               info.addStringPermissions( each ); 
            
            return info; 
         } 
      } 
      
      return null; 
   } 
   
   // 獲取認證信息
   protected AuthenticationInfo doGetAuthenticationInfo( 
      AuthenticationToken authcToken ) throws AuthenticationException { 
      UsernamePasswordToken token = (UsernamePasswordToken) authcToken; 
      // 通過表單接收的用戶名
      String username = token.getUsername(); 
      
      if( username != null && !"".equals(username) ){ 
         LoginAccount account = businessManager.get( username ); 
         
         if( account != null ){ 
            return new SimpleAuthenticationInfo( 
               account.getLoginName(),account.getPassword(),getName() ); 
         } 
      } 
      
      return null; 
   } 
 }

代碼說明:

  1. businessManager 表示從數(shù)據(jù)庫獲取用戶信息及用戶權(quán)限信息的業(yè)務(wù)類,,實際情況中可能因用戶權(quán)限模型設(shè)計不同或持久化框架選擇不同,,這里沒給出示例代碼。
  2. doGetAuthenticationInfo 方法,,取用戶信息,。對照用戶權(quán)限模型來說,,就是取 LoginAccount 實體,。最終我們需要為 Shiro 提供 AuthenticationInfo 對象。
  3. doGetAuthorizationInfo 方法,,獲取用戶權(quán)限信息,。代碼給出了獲取用戶 Permission 的示例,獲取用戶 Role 的代碼類似,。為 Shiro 提供的用戶權(quán)限信息以 AuthorizationInfo 對象形式返回,。

回頁首

為何對 Shiro 情有獨鐘

或許有人要問,我一直在使用 Spring,,應(yīng)用程序的安全組件早已選擇了 Spring Security,,為什么還需要 Shiro ?當然,,不可否認 Spring Security 也是一款優(yōu)秀的安全控制組件,。本文的初衷不是讓您必須選擇 Shiro 以及必須放棄 Spring Security,秉承客觀的態(tài)度,,下面對兩者略微比較:

  1. 簡單性,,Shiro 在使用上較 Spring Security 更簡單,更容易理解,。
  2. 靈活性,,Shiro 可運行在 Web,、EJB,、IoC、Google App Engine 等任何應(yīng)用環(huán)境,,卻不依賴這些環(huán)境。而 Spring Security 只能與 Spring 一起集成使用,。
  3. 可插拔,Shiro 干凈的 API 和設(shè)計模式使它可以方便地與許多的其它框架和應(yīng)用進行集成,。Shiro 可以與諸如 Spring,、Grails,、Wicket,、Tapestry、Mule,、Apache Camel,、Vaadin 這類第三方框架無縫集成。Spring Security 在這方面就顯得有些捉衿見肘,。

回頁首

與 Spring 集成

在 Java Web Application 開發(fā)中,,Spring 得到了廣泛使用;與 EJB 相比較,,可以說 Spring 是主流,。Shiro 自身提供了與 Spring 的良好支持,在應(yīng)用程序中集成 Spring 十分容易,。

有了前面提到的用戶權(quán)限數(shù)據(jù)模型,并且實現(xiàn)了自己的 Realm,,我們就可以開始集成 Shiro 為應(yīng)用程序服務(wù)了,。

Shiro 的安裝

Shiro 的安裝非常簡單,在 Shiro 官網(wǎng)下載 shiro-all-1.2.0.jar,、shiro-cas-1.2.0.jar(單點登錄需要),,及 SLF4J 官網(wǎng)下載 Shiro 依賴的日志組件 slf4j-api-1.6.1.jar。Spring 相關(guān)的 JAR 包這里不作列舉,。這些 JAR 包需要放置到 Web 工程 /WEB-INF/lib/ 目錄。至此,,剩下的就是配置了,。

配置過濾器

首先,配置過濾器讓請求資源經(jīng)過 Shiro 的過濾處理,,這與其它過濾器的使用類似,。

清單 2. web.xml 配置
 <filter> 
   <filter-name>shiroFilter</filter-name> 
   <filter-class> 
      org.springframework.web.filter.DelegatingFilterProxy 
   </filter-class> 
 </filter> 
 <filter-mapping> 
   <filter-name>shiroFilter</filter-name> 
   <url-pattern>/*</url-pattern> 
 </filter-mapping>

Spring 配置

接下來僅僅配置一系列由 Spring 容器管理的 Bean,,集成大功告成。各個 Bean 的功能見代碼說明,。

清單 3. Spring 配置
 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 
   <property name="securityManager" ref="securityManager"/> 
   <property name="loginUrl" value="/login.do"/> 
   <property name="successUrl" value="/welcome.do"/> 
   <property name="unauthorizedUrl" value="/403.do"/> 
   <property name="filters"> 
      <util:map> 
         <entry key="authc" value-ref="formAuthenticationFilter"/> 
      </util:map> 
   </property> 
   <property name="filterChainDefinitions"> 
      <value> 
         /=anon 
         /login.do*=authc 
         /logout.do*=anon 
         
         # 權(quán)限配置示例
         /security/account/view.do=authc,perms[SECURITY_ACCOUNT_VIEW] 
         
         /** = authc 
      </value> 
   </property> 
 </bean> 

 <bean id="securityManager" 
   class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 
   <property name="realm" ref="myShiroRealm"/> 
 </bean> 

 <bean id="myShiroRealm" class="xxx.packagename.MyShiroRealm"> 
   <!-- businessManager 用來實現(xiàn)用戶名密碼的查詢 --> 
   <property name="businessManager" ref="businessManager"/> 
   <property name="cacheManager" ref="shiroCacheManager"/> 
 </bean> 

 <bean id="lifecycleBeanPostProcessor" 
    class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> 

 <bean id="shiroCacheManager" 
   class="org.apache.shiro.cache.ehcache.EhCacheManager"> 
   <property name="cacheManager" ref="cacheManager"/> 
 </bean> 

 <bean id="formAuthenticationFilter" 
   class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"/>

代碼說明:

  1. shiroFilter 中 loginUrl 為登錄頁面地址,,successUrl 為登錄成功頁面地址(如果首先訪問受保護 URL 登錄成功,則跳轉(zhuǎn)到實際訪問頁面),,unauthorizedUrl 認證未通過訪問的頁面(前面提到的“未經(jīng)授權(quán)頁面”),。
  2. shiroFilter 中 filters 屬性,formAuthenticationFilter 配置為基于表單認證的過濾器,。
  3. shiroFilter 中 filterChainDefinitions 屬性,,anon 表示匿名訪問(不需要認證與授權(quán)),authc 表示需要認證,,perms[SECURITY_ACCOUNT_VIEW] 表示用戶需要提供值為“SECURITY_ACCOUNT_VIEW”Permission 信息,。由此可見,連接地址配置為 authc 或 perms[XXX] 表示為受保護資源,。
  4. securityManager 中 realm 屬性,,配置為我們自己實現(xiàn)的 Realm。關(guān)于 Realm,,參見前面“Shiro Realm”章節(jié),。
  5. myShiroRealm 為我們自己需要實現(xiàn)的 Realm 類,為了減小數(shù)據(jù)庫壓力,,添加了緩存機制,。
  6. shiroCacheManager 是 Shiro 對緩存框架 EhCache 的配置。

回頁首

實現(xiàn)驗證碼認證

驗證碼是有效防止暴力破解的一種手段,,常用做法是在服務(wù)端產(chǎn)生一串隨機字符串與當前用戶會話關(guān)聯(lián)(我們通常說的放入 Session),,然后向終端用戶展現(xiàn)一張經(jīng)過“擾亂”的圖片,只有當用戶輸入的內(nèi)容與服務(wù)端產(chǎn)生的內(nèi)容相同時才允許進行下一步操作,。

產(chǎn)生驗證碼

作為演示,,我們選擇開源的驗證碼組件 kaptcha。這樣,,我們只需要簡單配置一個 Servlet,,頁面通過 IMG 標簽就可以展現(xiàn)圖形驗證碼。

清單 4. web.xml 配置
 <!-- captcha servlet--> 
 <servlet> 
   <servlet-name>kaptcha</servlet-name> 
   <servlet-class> 
      com.google.code.kaptcha.servlet.KaptchaServlet 
   </servlet-class> 
 </servlet> 
 <servlet-mapping> 
 <servlet-name>kaptcha</servlet-name> 
 <url-pattern>/images/kaptcha.jpg</url-pattern> 
 </servlet-mapping>

擴展 UsernamePasswordToken

Shiro 表單認證,,頁面提交的用戶名密碼等信息,,用 UsernamePasswordToken 類來接收,很容易想到,,要接收頁面驗證碼的輸入,,我們需要擴展此類:

清單 5. CaptchaUsernamePasswordToken
 public class CaptchaUsernamePasswordToken extends UsernamePasswordToken{ 

 private String captcha; 

 // 省略 getter 和 setter 方法

 public CaptchaUsernamePasswordToken(String username, char[] password, 
 boolean rememberMe, String host,String captcha) { 
 super(username, password, rememberMe, host); 
 this.captcha = captcha; 
 } 
 }

擴展 FormAuthenticationFilter

接下來我們擴展 FormAuthenticationFilter 類,首先覆蓋 createToken 方法,,以便獲取 CaptchaUsernamePasswordToken 實例,;然后增加驗證碼校驗方法 doCaptchaValidate,;最后覆蓋 Shiro 的認證方法 executeLogin,在原表單認證邏輯處理之前進行驗證碼校驗,。

清單 6. CaptchaUsernamePasswordToken
 public class CaptchaFormAuthenticationFilter extends FormAuthenticationFilter{ 

   public static final String DEFAULT_CAPTCHA_PARAM = "captcha"; 
   
   private String captchaParam = DEFAULT_CAPTCHA_PARAM; 
   
   public String getCaptchaParam() { 
      return captchaParam; 
   } 
   
   public void setCaptchaParam(String captchaParam) { 
      this.captchaParam = captchaParam; 
   } 
   
   protected String getCaptcha(ServletRequest request) { 
      return WebUtils.getCleanParam(request, getCaptchaParam()); 
   } 
   
   // 創(chuàng)建 Token 
   protected CaptchaUsernamePasswordToken createToken( 
      ServletRequest request, ServletResponse response) { 
   
      String username = getUsername(request); 
      String password = getPassword(request); 
      String captcha = getCaptcha(request); 
      boolean rememberMe = isRememberMe(request); 
      String host = getHost(request); 
                   
      return new CaptchaUsernamePasswordToken( 
         username, password, rememberMe, host,captcha); 
   } 
   
   // 驗證碼校驗
   protected void doCaptchaValidate( HttpServletRequest request 
      ,CaptchaUsernamePasswordToken token ){ 

      String captcha = (String)request.getSession().getAttribute( 
         com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY); 
      
      if( captcha!=null && 
         !captcha.equalsIgnoreCase(token.getCaptcha()) ){ 
         throw new IncorrectCaptchaException ("驗證碼錯誤,!"); 
      } 
   } 
    
   // 認證
   protected boolean executeLogin(ServletRequest request, 
      ServletResponse response) throws Exception { 
      CaptchaUsernamePasswordToken token = createToken(request, response); 
       
      try { 
         doCaptchaValidate( (HttpServletRequest)request,token ); 
                 
         Subject subject = getSubject(request, response); 
         subject.login(token); 
                     
         return onLoginSuccess(token, subject, request, response); 
      } catch (AuthenticationException e) { 
         return onLoginFailure(token, e, request, response); 
      } 
   } 

 }

代碼說明:

  1. 添加 captchaParam 變量,為的是頁面表單提交驗證碼的參數(shù)名可以進行靈活配置,。
  2. doCaptchaValidate 方法中,,驗證碼校驗使用了框架 KAPTCHA 所提供的 API。

添加 IncorrectCaptchaException

前面驗證碼校驗不通過,,我們拋出一個異常 IncorrectCaptchaException,,此類繼承 AuthenticationException,之所以需要擴展一個新的異常類,,為的是在頁面能更精準顯示錯誤提示信息,。

清單 7. IncorrectCaptchaException
 public class IncorrectCaptchaException extends AuthenticationException{ 
   
   public IncorrectCaptchaException() { 
      super(); 
   } 
   
   public IncorrectCaptchaException(String message, Throwable cause) { 
      super(message, cause); 
   } 
   
   public IncorrectCaptchaException(String message) { 
      super(message); 
   } 
   
   public IncorrectCaptchaException(Throwable cause) { 
      super(cause); 
   } 
 }

頁面展現(xiàn)驗證碼錯誤提示信息

清單 8. 頁面認證錯誤信息展示
 Object obj=request.getAttribute( 
   org.apache.shiro.web.filter.authc.FormAuthenticationFilter 
      .DEFAULT_ERROR_KEY_ATTRIBUTE_NAME); 
 AuthenticationException authExp = (AuthenticationException)obj; 
 if( authExp != null ){ 
   String expMsg=""; 
   
   if(authExp instanceof UnknownAccountException || 
      authExp instanceof IncorrectCredentialsException){ 
      expMsg="錯誤的用戶賬號或密碼!"; 
   }else if( authExp instanceof IncorrectCaptchaException){ 
      expMsg="驗證碼錯誤,!"; 
   }else{ 
      expMsg="登錄異常 :"+authExp.getMessage() ; 
   } 
    
   out.print("<div class=\"error\">"+expMsg+"</div>"); 
 }

回頁首

實現(xiàn)單點登錄

前面章節(jié),,我們認識了 Shiro 的認證與授權(quán),并結(jié)合 Spring 作了集成實現(xiàn)?,F(xiàn)實中,,有這樣一個場景,我們擁有很多業(yè)務(wù)系統(tǒng),,按照前面的思路,,如果訪問每個業(yè)務(wù)系統(tǒng),都要進行認證,,這樣是否有點難讓人授受,。有沒有一種機制,讓我們只認證一次,,就可以任意訪問目標系統(tǒng)呢,?

上面的場景,就是我們常提到的單點登錄 SSO,。Shiro 從 1.2 版本開始對 CAS 進行支持,,CAS 就是單點登錄的一種實現(xiàn)。

Shiro CAS 認證流程

  • 用戶首次訪問受保護的資源,;例如 http://casclient/security/view.do
  • 由于未通過認證,,Shiro 首先把請求地址(http://casclient/security/view.do)緩存起來。
  • 然后跳轉(zhuǎn)到 CAS 服務(wù)器進行登錄認證,,在 CAS 服務(wù)端認證完成后需要返回到請求的 CAS 客戶端,,因此在請求時,必須在參數(shù)中添加返回地址 ( 在 Shiro 中名為 CAS Service)。 例如 http://casserver/login?service=http://casclient/shiro-cas
  • 由 CAS 服務(wù)器認證通過后,,CAS 服務(wù)器為返回地址添加 ticket。例如 http://casclient/shiro-cas?ticket=ST-4-BWMEnXfpxfVD2jrkVaLl-cas
  • 接下來,,Shiro 會校驗 ticket 是否有效,。由于 CAS 客戶端不提供直接認證,所以 Shiro 會向 CAS 服務(wù)端發(fā)起 ticket 校驗檢查,,只有服務(wù)端返回成功時,,Shiro 才認為認證通過。
  • 認證通過,,進入授權(quán)檢查,。Shiro 授權(quán)檢查與前面提到的相同。
  • 最后授權(quán)檢查通過,,用戶正常訪問到 http://casclient/security/view.do,。

CAS Realm

Shiro 提供了一個名為 CasRealm 的類,與前面提到的 JDBC Realm 相似,,該類同樣包括認證和授權(quán)兩部分功能,。認證就是校驗從 CAS 服務(wù)端返回的 ticket 是否有效;授權(quán)還是獲取用戶權(quán)限信息,。

實現(xiàn)單點登錄功能,,需要擴展 CasRealm 類。

清單 9. Shiro CAS Realm
 public class MyCasRealm extends CasRealm{ 
   
   // 獲取授權(quán)信息
   protected AuthorizationInfo doGetAuthorizationInfo( 
      PrincipalCollection principals) { 
      //... 與前面 MyShiroRealm 相同
   } 
   
    public String getCasServerUrlPrefix() { 
      return "http://casserver/login"; 
   } 
   
   public String getCasService() { 
      return "http://casclient/shiro-cas"; 
   } 
   16 
 }

代碼說明:

  1. doGetAuthorizationInfo 獲取授權(quán)信息與前面章節(jié)“實現(xiàn)自己的 JDBC Realm”相同,。
  2. 認證功能由 Shiro 自身提供的 CasRealm 實現(xiàn),。
  3. getCasServerUrlPrefix 方法返回 CAS 服務(wù)器地址,實際使用一般通過參數(shù)進行配置,。
  4. getCasService 方法返回 CAS 客戶端處理地址,,實際使用一般通過參數(shù)進行配置。
  5. 認證過程需 keystore,,否則會出現(xiàn)異常,。可以通過設(shè)置系統(tǒng)屬性的方式來指定,,例如 System.setProperty("javax.net.ssl.trustStore","keystore-file");

CAS Spring 配置

實現(xiàn)單點登錄的 Spring 配置與前面類似,,不同之處參見代碼說明。

清單 10. Shiro CAS Spring 配置
 <bean id="shiroFilter" 
   class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 
   <property name="securityManager" ref="securityManager"/> 
   <property name="loginUrl" 
      value="http://casserver/login?service=http://casclient/shiro-cas"/> 
   <property name="successUrl" value="/welcome.do"/> 
   <property name="unauthorizedUrl" value="/403.do"/> 
   <property name="filters"> 
      <util:map> 
         <entry key="authc" value-ref="formAuthenticationFilter"/> 
         <entry key="cas" value-ref="casFilter"/> 
      </util:map> 
   </property> 
   <property name="filterChainDefinitions"> 
      <value> 
         /shiro-cas*=cas 
         /logout.do*=anon 
         /casticketerror.do*=anon 
          
         # 權(quán)限配置示例
         /security/account/view.do=authc,perms[SECURITY_ACCOUNT_VIEW] 
          
         /** = authc 
      </value> 
   </property> 
 </bean> 
 
 <bean id="securityManager" 
   class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 
   <property name="realm" ref="myShiroRealm"/> 
 </bean> 

 <bean id="lifecycleBeanPostProcessor" 
   class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> 

 <!-- CAS Realm --> 
 <bean id="myShiroRealm" class="xxx.packagename.MyCasRealm"> 
   <property name="cacheManager" ref="shiroCacheManager"/> 
 </bean> 

 <bean id="shiroCacheManager" 
   class="org.apache.shiro.cache.ehcache.EhCacheManager"> 
   <property name="cacheManager" ref="cacheManager"/> 
 </bean> 

 <bean id="formAuthenticationFilter" 
   class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"/> 

 <!-- CAS Filter --> 
 <bean id="casFilter" class="org.apache.shiro.cas.CasFilter"> 
   <property name="failureUrl" value="casticketerror.do"/> 
 </bean>

代碼說明:

  1. shiroFilter 中 loginUrl 屬性,,為登錄 CAS 服務(wù)端地址,,參數(shù) service 為服務(wù)端的返回地址。
  2. myShiroRealm 為上一節(jié)提到的 CAS Realm,。
  3. casFilter 中 failureUrl 屬性,,為 Ticket 校驗不通過時展示的錯誤頁面。

回頁首

總結(jié)

至此,我們對 Shiro 有了較為深入的認識,。Shiro 靈活,,功能強大,幾乎能滿足我們實際應(yīng)用中的各種情況,,還等什么呢,?讓我開始使用 Shiro 為應(yīng)用程序護航吧!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多