Jeesite中shiro的用法講解

前言

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

Jeesite中的shiro

2.1 spring-context-shiro.xml

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

<!-- 安全認證過濾器 -->
<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的安全認證過濾器,其中,spring

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

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

  • successUrl:登陸成功默認跳轉頁面,不配置則跳轉至」/」。若是登錄前點擊的一個須要登陸的頁面,則在登陸自動跳轉到那個須要登陸的頁面。不跳轉到此。安全

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

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

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

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

  • 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();

相關文章
相關標籤/搜索