1. 程式人生 > >Java:Shiro+SpringMVC的整合實踐

Java:Shiro+SpringMVC的整合實踐

原帖位於IT老兵部落格,沉澱著一個IT老兵對於這個行業的認知。

Java:Shiro+SpringMVC的整合實踐。

前言

個人感覺,Shiro的官網有一個問題,講的不夠清楚,儘管看上去好像講的挺明白,但是我總是感覺很多地方不夠清楚,事實上,在閱讀了很多帖子之後,發現很多人都對這一點存在疑問,那就不是我一個人的問題了。

Shiro的官網缺乏完整的例子,而且我所處理的專案是Spring的專案,如何清楚地整合在一起,似乎還沒有看到,很多地方都需要摸索,看了張開濤的部落格,下面一樣有很多人存有疑問。

之前研究這個,花了幾天的時間研究理論,感覺自己已經明白了(這個感覺在另外一篇帖子《Java:Shiro的架構學習筆記》裡面有提到),實際上是,紙上得來終覺淺,絕知此事要躬行。

這篇文章結合著自己的例子,把所理解到的東西做一個總結,以備日後檢視,也給需要的朋友們一個參考。

正文

專案用的是XML配置,至於註解如何配置,暫時還沒有時間去研究。

專案中定義一個spring-shiro.xml檔案,配置在classpath裡面,可以被系統讀取到,這塊涉及Spring讀取配置檔案的功能,官網是寫在了applicationContext.xml檔案裡面,然後在web.xml裡面定義filter,現在做專案,似乎已經很少用到這個web.xml檔案,基本都是定義在spring-mvc.xml這個檔案裡面,這裡給shiro單獨定義了一個配置檔案,原理是一樣的。

先定義filter:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
</bean>

這個將會構造一個shiroFilter,引數是securityManager。

定義securityManager:

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="tokenRealm" />
    <property name="cacheManager" ref="cacheManager"></property>
    <property name="sessionManager" ref="sessionManager" />
    <property name="subjectFactory" ref="subjectFactory"/> 
    <property name="subjectDAO.sessionStorageEvaluator.sessionStorageEnabled" value="true"/>
    <!-- By default the servlet container sessions will be used. Uncomment 
        this line to use shiro's native sessions (see the JavaDoc for more): -->
    <property name="sessionMode" value="http"/>
</bean>

這裡構造了securityManager,並且傳遞了6個引數給它,每個引數可以是自己寫的繼承類,也可以是預設的類,這裡涉及一些業務隱私的問題,不能都貼出來了。

第一個tokenRealm是用於進行認證的元件。

<bean id="tokenRealm" class="xx.xx.xx.TokenRealm">
    <property name="credentialsMatcher" ref="credentialsMatcher"/>  
</bean>

引數是自定義的一個憑證匹配器。
這裡需要覆寫兩個方法:
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException 返回認證資訊。

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) 返回授權資訊。
這個地方之前一直沒有搞明白,是最讓我困惑的地方,doGetAuthenticationInfo的第一個引數就是login方法送過來的token,一般這個token帶有username和password,這裡根據這個使用者名稱去把資料庫把使用者的密碼取出來,然後構造一個SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, getName());返回,然後會交由匹配器去匹配,匹配器主要匹配第二個引數(原型是:SimpleAuthenticationInfo(Object principal, Object credentials, String realmName)),即憑證是否相等。

而自定義的匹配器大體是下面這樣,覆寫匹配的函式(增加了快取來儲存嘗試次數):

@Override  
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {  
    String username = (String) token.getPrincipal();  
    AtomicInteger retryCount = loginRetryCache.get(username);  
    System.out.println("重試次數:" + retryCount);
    if (retryCount != null && retryCount.intValue() >= maxRetryCount) {
        throw new ExcessiveAttemptsException("username: " + username + " tried to login more than 5 times in period"); 
    }
    
    boolean matches = super.doCredentialsMatch(token, info);  
    if (matches) {  
        //clear retry data  
        System.out.println("清除重試次數快取");
        if (retryCount != null) {
            loginRetryCache.remove(username);  
        }
        return true;
    } else {
        if (null == retryCount) {  
            retryCount = new AtomicInteger(1);  
            loginRetryCache.put(username, retryCount);  
            System.out.println("插入快取,失敗次數:" + retryCount);
        } else if (retryCount.incrementAndGet() >= maxRetryCount) {  
            log.warn("username: " + username + " tried to login more than 5 times in period");  
            throw new ExcessiveAttemptsException("username: " + username + " tried to login more than 5 times in period");  
        }  
        
        retryCount = loginRetryCache.get(username);
        System.out.println("認證失敗,失敗次數:" + retryCount);
        return false;
    }
      
}  

在login完成後,Shiro其實會返回給客戶端一個JSESSIONID,並且會在快取中儲存關於這個會話的一些資訊,這些會話資訊會被定期清理(由排程任務15分鐘或者是下一次訪問時判斷是否過期)或者是由logout方法主動登出掉。

總結

初步總結了一下Shiro的用法,實踐了一天,總結了一天,終於感覺搞明白了,使用Shiro的難度主要在於牽扯的類比較多,而且文件說的不是太清楚,需要自己反覆地實踐。這也可能說明它設計得很靈活,一般設計得很靈活的東西,都是不容易掌握,但是,一旦掌握了,就非常得方便。
這篇帖子還會不斷更新,直到把這個地方的概念全部梳理清楚。

參考

https://shiro.apache.org/10-minute-tutorial.html
https://shiro.apache.org/static/1.3.0/apidocs/org/apache/shiro/authc/SimpleAuthenticationInfo.html
https://shiro.apache.org/architecture.html