Jeesite中shiro的用法講解

前言

    Apache Shiro 是一個框架,可用於身份驗證和授權。雖然這兩個術語代表的是不同的含義,但出於它們在應用程序安全性方面各自的角色考慮,它們有時會被交換使用。
身份驗證 指的是驗證用戶的身份。在驗證用戶身份時,需要確認用戶的身份的確如他們所聲稱的那樣。在大多數應用程序中,身份驗證是通過用戶名和密碼的組合完成的。只要用戶選擇了他人很難猜到的密碼,那麼用戶名和密碼的組合通常就足以確立身份。但是,還有其他的身份驗證方式可用,比如指紋、證書和生成鍵。
    一旦身份驗證過程成功地建立起身份,授權 就會接管以便進行訪問的限制或允許。 所以,有這樣的可能性:用戶雖然通過了身份驗證可以登錄到一個系統,但是未經過授權,不準做任何事情。還有一種可能是用戶雖然具有了某種程度的授權,卻並未經過身份驗證。
在爲應用程序規劃安全性模型時,必須處理好這兩個元素以確保系統具有足夠的安全性。身份驗證是應用程序常見的問題(特別是在只有用戶和密碼組合的情況下),所以讓框架來處理這項工作是一個很好的做法。合理的框架可提供經過測試和維護的優勢,讓您可以集中精力處理業務問題,而不是解決其解決方案已經實現的問題。
    Apache Shiro 提供了一個可用的安全性框架,各種客戶機都可將這個框架應用於它們的應用程序。本文中的這些例子旨在介紹 Shiro 並着重展示對用戶進行身份驗證的基本任務。
本文只針對Jeesite中shiro的用法進行整理,不會包括shiro環境配置和搭建等內容。

Jeesite中的shiro

2.1 spring-context-shiro.xml

    spring-context-shiro.xml是shiro的主配置文件,配置信息是重點主要是安全認證過濾器的配置。它規定哪些url需要進行哪些方面的認證和過濾

<!-- 安全認證過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <property name="p" value="${adminPath}/login" />
    <property name="successUrl" value="${adminPath}" />
    <property name="filters">
        <map>
            <entry key="authc" value-ref="formAuthenticationFilter"/>
        </map>
    </property>
    <property name="filterChainDefinitions">
        <value>
            /static/** = anon
            /userfiles/** = anon
            ${adminPath}/login = authc
            ${adminPath}/logout = logout
            ${adminPath}/** = user
        </value>
    </property>
</bean>

    shiroFilter是shiro的安全認證過濾器,其中,

  • securityManager:指定一個負責管理的bean,這個新的bean在接下來會定義,其中包含了認證的主要邏輯。

  • loginUrl:沒有登錄的用戶請求需要登錄的頁面時自動跳轉到登錄頁面,不是必須的屬性,不輸入地址的話會自動尋找項目web項目的根目錄下的”/login.jsp”頁面。

  • successUrl:登錄成功默認跳轉頁面,不配置則跳轉至”/”。如果登陸前點擊的一個需要登錄的頁面,則在登錄自動跳轉到那個需要登錄的頁面。不跳轉到此。

  • unauthorizedUrl:沒有權限默認跳轉的頁面。

  • map中的entry指定了authc權限所對應的過濾器實體

而屬性中的filterChainDefinitions則詳細規定啦不同的url的對應權限

  • anon:例子/admins/**=anon 沒有參數,表示可以匿名使用。

  • authc:例如/admins/user/**=authc表示需要認證(登錄)才能使用,沒有參數

  • roles:例子/admins/user/=roles[admin],參數可以寫多個,多個時必須加上引號,並且參數之間用逗號分割,當有多個參數時,例如admins/user/=roles["admin,guest"],每個參數通過纔算通過,相當於hasAllRoles()方法。

  • perms:例子/admins/user/**=perms[user:add:],參數可以寫多個,多個時必須加上引號,並且參數之間用逗號分割,例如/admins/user/=perms["user:add:,user:modify:*"],當有多個參數時必須每個參數都通過才通過,想當於isPermitedAll()方法。

  • rest:例子/admins/user/=rest[user],根據請求的方法,相當於/admins/user/=perms[user:method] ,其中method爲post,get,delete等。

  • port:例子/admins/user/**=port[8081],當請求的url的端口不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置裏port的端口,queryString
    是你訪問的url裏的?後面的參數。

  • authcBasic:例如/admins/user/**=authcBasic沒有參數表示httpBasic認證

  • ssl:例子/admins/user/**=ssl沒有參數,表示安全的url請求,協議爲https

  • user:例如/admins/user/**=user沒有參數表示必須存在用戶,當登入操作時不做檢查

  • 注:anon,authcBasic,auchc,user是認證過濾器,perms,roles,ssl,rest,port是授權過濾器

    

<!-- 定義 Shiro 主要業務對象 -->
<bean id="securityManager" 
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- <property name="sessionManager" ref="sessionManager" /> -->
<property name="realm" ref="systemAuthorizingRealm" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean>

    這部分代碼定義了securitymanager的主要屬性的實體,systemAuthorizingRealm和shiroCacheManager都是自己實現的,分別用於進行驗證管理和cache管理。從配置文件能看出,配置文件指定了作爲安全邏輯的幾個結構,除了這兩部分,還包括formAuthenticationFilter。其中realm和filter在com.thinkgem.jeesite.modules.sys.security包裏實現。

2.2 FormAuthenticationFilter

    從可見都邏輯上講,FormAuthenticationFilter類是用戶驗證時所接觸的第一個類。
    當用戶登錄任意界面時,shiro會對當前狀態進行檢查。如果發現需要登錄,則會自動跳轉到配置文件裏loginUrl屬性所指定的url中。
    而這一url又被指定爲authc權限,即需要驗證。接着,authc的filter被指定爲formAuthenticationFilter,因此login頁面所提交的信息被改filter截獲進行處理。
    其中的核心邏輯是createToken函數:

protected AuthenticationToken createToken(S2ervletRequest request, ServletResponse response) {
    String username = getUsername(request);
    String password = getPassword(request);
    if (password==null){
        password = "";
    }
    boolean rememberMe = isRememberMe(request);
    String host = getHost(request);
    String captcha = getCaptcha(request);
    return new U

    UsernamePasswordToken是security包裏的第四個類,它繼承自shiro的同名類,用於shiro處理中的參數傳遞。createtoken函數接受到login網頁所接受的表單,生成一個token傳給下一個類處理。
    函數中的rememberme以及captcha分別表示的是記住用戶功能和驗證碼功能,這部分也是shiro自身攜帶,我們並不需要修改。filter是通過aop的方式結合到系統裏的,因此並沒有具體的接口實現。

2.3 SystemAuthorizingRealm

    shiro的最終處理都將交給Real進行處理。因爲在Shiro中,最終是通過Realm來獲取應用程序中的用戶、角色及權限信息的。通常情況下,在Realm中會直接從我們的數據源中獲取Shiro需要的驗證信息。可以說,Realm是專用於安全框架的DAO.
Realm中有個參數是systemService,這個便是spring的具體業務邏輯,其中也包含了具體的DAO,正是在這個部分,shiro與spring的接口具體的結合了起來。
realm當中有兩個函數特別重要,分別是用戶認證函數和授權函數。

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
     
    if (LoginController.isValidateCodeLogin(token.getUsername(), false, false)){
        // 判斷驗證碼
        Session session = SecurityUtils.getSubject().getSession();
        String code = (String)session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);
        if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)){
            throw new CaptchaException("驗證碼錯誤.");
        }
    }
 
    User user = getSystemService().getUserByLoginName(token.getUsername());
    if (user != null) {
        byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16));
        return new SimpleAuthenticationInfo(new Principal(user), 
                user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());
    } else {
        return null;
    }
}

    以上便是用戶認證函數,中間關於驗證碼檢測的部分我們暫且不表,其最核心的語句是


    函數的參數AuthenticationToken便是filter所截獲並生成的token實例,其內容是login表單裏的內容,通常是用戶名和密碼。在前一句話裏,getSystemService取到了systemService實例,該實例中又含有配置好的userDAO,能夠直接與數據庫交互。因此將用戶id傳入,取出數據庫裏所存有的user實例,接着在SimpleAuthenticationInfo生成過程中分別以user實例的用戶名密碼,以及token裏的用戶名密碼做爲參數,驗證密碼是否相同,以達到驗證的目的。
    doGetAuthorizationInfo函數是授權的函數,其具體的權限是在實現類中以annotation的形式指派的,它負責驗證用戶是否有權限訪問。詳細的細節容我之後添加。

2.3 LoginController

     LoginController是本該實現登錄認證的部分。由於shiro的引入和AOP的使用,jeesite中的LoginController只處理驗證之後的部分。
如果通過驗證,系統中存在user實例,則返回對應的主頁。否則重新定位於login頁面。

2.4 annotation

    常用的annotation主要如下:

  • @RequiresAuthentication
        要求當前Subject 已經在當前的session 中被驗證通過才能被註解的類/實例/方法訪問或調用。
        驗證用戶是否登錄,等同於方法subject.isAuthenticated() 結果爲true時。

  • @RequiresUser
        需要當前的Subject 是一個應用程序用戶才能被註解的類/實例/方法訪問或調用。要麼是通過驗證被確認,或者在之前session 中的'RememberMe'服務被記住。
        驗證用戶是否被記憶,user有兩種含義:一種是成功登錄的(subject.isAuthenticated() 結果爲true);另外一種是被記憶的(subject.isRemembered()結果爲true)。

  • @RequiresGuest
        要求當前的Subject 是一個“guest”,也就是他們必須是在之前的session中沒有被驗證或記住才能被註解的類/實例/方法訪問或調用。
        驗證是否是一個guest的請求,與@RequiresUser完全相反。
        換言之,RequiresUser == !RequiresGuest。此時subject.getPrincipal() 結果爲null.

  • @RequiresRoles
        要求當前的Subject 擁有所有指定的角色。如果他們沒有,則該方法將不會被執行,而且AuthorizationException 異常將會被拋出。例如:@RequiresRoles("administrator")
    或者@RequiresRoles("aRoleName");
    void someMethod();
        如果subject中有aRoleName角色纔可以訪問方法someMethod。如果沒有這個權限則會拋出異常AuthorizationException。

  • @RequiresPermissions
        要求當前的Subject 被允許一個或多個權限,以便執行註解的方法,比如:
        @RequiresPermissions("account:create")
        或者@RequiresPermissions({"file:read", "write:aFile.txt"} )
        void someMethod();



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章