安全認證框架-Apache Shiro研究心得(轉)

最近因爲項目需要,研究了一下Apache Shiro安全認證框架,把心得記錄下來。

(原創by:西風吹雨)

     Apache Shrio是一個安全認證框架,和Spring Security相比,在於他使用了和比較簡潔易懂的認證和授權方式。其提供的native-session(即把用戶認證後的授權信息保存在其自身提供Session中)機制,這樣就可以和HttpSession、EJB Session Bean的基於容器的Session脫耦,到到和客戶端應用、Flex應用、遠程方法調用等都可以使用它來配置權限認證。 

 1、sessionMode

    在普通的WEB項目中,我們可以選擇使用native session或者是HttpSession,通過設置securityManager的sessionMode參數爲http或native即可。

 2、realm

    我們可以基於jdbc,ldap,text,activeDirectory,jndi等多種方式來獲取用戶基本信息,角色信息,權限信息等。只需要在 securityManager中指定使用相應的realm實現即可,其在這各方面都提供了對應的缺省實現,比如我們常用的基於數據庫表的形式來配置用戶 權限信息,就可以使用其缺省實現的jdbcRealm(org.apache.shiro.realm.jdbc.JdbcRealm)。當然,如果認證 信息來自於多方面,多個不同的來源(比如來自兩個庫中,或者一個數據庫,一個是ldap,再配上一個缺省的基於文本的測試用等等),我們可以爲 securityManager指定realms參數,即把這一組安全配置都配置上。各個具體的realm實現提供了方法來獲取用戶基本信息、角色、權限 等。

    realm的授權信息可以存放在Cache中,Cache的名稱可以通過設置其authorizationCacheName參數指定。

3、緩存    

   目前Shrio缺省提供了基於ehCache來緩存用戶認證信息和授權信息的實現。只需要配置

org.apache.shiro.web.mgt.DefaultWebSecurityManager 這個 cacheManager並設置給SecurityManager即可。如果項目中已經存在使用的ehCacheManager配置(org.springframework.cache.ehcache.EhCacheManagerFactoryBean),DefaultWebSecurityManager則可以指定使用現有的ehCacheManager,如果不指定,它將自行使用缺省配置創建一個。同時,也可以設置cacheManagerConfigFile參數來指定ehCache的配置文件。

 

下例中的shiro.authorizationCache是用來存放授權信息的Cache,我們在配置realm(如myRealm或 jdbcReaml)時,把authorizationCacheName屬性設置shiro.authorizationCache來對應。

 

ehcache.xml

<ehcache>

    <diskStore path="java.io.tmpdir/tuan-oauth"/>

 


    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />

    <!-- We want eternal="true" (with no timeToIdle or timeToLive settings) because Shiro manages session
expirations explicitly.  If we set it to false and then set corresponding timeToIdle and timeToLive properties,
ehcache would evict sessions without Shiro's knowledge, which would cause many problems
(e.g. "My Shiro session timeout is 30 minutes - why isn't a session available after 2 minutes?"
Answer - ehcache expired it due to the timeToIdle property set to 120 seconds.)

diskPersistent=true since we want an enterprise session management feature - ability to use sessions after
even after a JVM restart.  -->
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           eternal="true"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>

    <cache name="shiro.authorizationCache"
           maxElementsInMemory="100"
           eternal="false"
           timeToLiveSeconds="600"
           overflowToDisk="false"/>

</ehcache>

 

當我們把securityManager的sessionMode參數設置爲native時,那麼shrio就將用戶的基本認證信息保存到缺省名稱爲shiro-activeSessionCache 的Cache中

org.apache.shiro.web.mgt.DefaultWebSecurityManager 在sessionMode參數設置爲native時,缺省使用的是DefaultWebSessionManager來管理Session,該 管理類缺省使用的是使用MemorySessionDAO基於內存來保存和操作用戶基本認證信息。如果系統內的用戶數特別多,我們需要使用 CacheSessionDao來基於Cache進行操作,因此,這裏需要顯示配置一個 sessionManager(org.apache.shiro.web.session.mgt.DefaultWebSessionManager), 並配置該sessionManager的sessionDao爲 CacheSessionDao(org.apache.shiro.session.mgt.eis.CachingSessionDAO,需用其實現 類org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO)。配置CacheSessionDao時,我們可以指定屬性activeSessionsCacheName的名稱來替換掉缺省名 shiro-activeSessionCache。

我們再把該sessionManager配置給DefaultWebSecurityManager就可以了。

 

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="sessionMode" value="native"/>
        <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
        <property name="realm" ref="myRealm"/>
        <property name="sessionManager" ref="sessionManager"/>
    </bean>

    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="sessionDAO" ref="sessionDAO"/>
    </bean>

    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
    </bean>

 

從以上我們可以看出

   a、我們可以指定sessionManager的sessionDao,在某些情況下,我們也可以通過實現自定義的sessionDao來把用戶認證信息 保存在memcache,mongodb,ldap,database中,達到和其他應用共享用戶認證信息的目的,以此達到SSO的目的(當 然,sessionId得一致,這個屬於我們可以在應用商定怎麼設定一致的sessionId的問題)。

   b、cacheManager我們也可以自己實現一個,可以根據應用情況來考慮,比如存放在memcache中之類。

 

 

4、配置

    Web項目中,普通的web項目可以採用ini文件來對shiro進行配置。基於spring的項目可以採用和Spring集成的方式配置。

 

基於Spring集成的Web項目的基本配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans "
       xmlns:context="http://www.springframework.org/schema/context "
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
       xmlns:util="http://www.springframework.org/schema/util "
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd ">

    <!-- =========================================================
         Shiro Core Components - Not Spring Specific
         ========================================================= -->

    <!-- Shiro's main business-tier object for web-enabled applications
         (use DefaultSecurityManager instead when there is no web environment)-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
        <property name="sessionMode" value="native"/>
        <property name="realm" ref="myRealm"/>
    </bean>

    <!-- Let's use some enterprise caching support for better performance.  You can replace this with any enterprise
         caching framework implementation that you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
             will be creaed with a default config:                -->
        <property name="cacheManager" ref="ehCacheManager"/>
        <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
             a specific Ehcache configuration to be used, specify that here.  If you don't, a default
             will be used.:
        <property name="cacheManagerConfigFile" value="classpath:some/path/to/ehcache.xml"/> -->
    </bean>

    <!-- Used by the SecurityManager to access security data (users, roles, etc).
         Many other realm implementations can be used too (PropertiesRealm,
         LdapRealm, etc. -->
    <bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
        <property name="name" value="jdbcRealm"/>
        <property name="dataSource" ref="dataSource"/>
        <property name="credentialsMatcher">
            <!-- The 'bootstrapDataPopulator' Sha256 hashes the password
                 (using the username as the salt) then base64 encodes it: -->
            <bean class="org.apache.shiro.authc.credential.Sha256CredentialsMatcher">
                <!-- true means hex encoded, false means base64 encoded -->
                <property name="storedCredentialsHexEncoded" value="false"/>
                <!-- We salt the password using the username, the most common practice: -->
                <property name="hashSalted" value="true"/>
            </bean>
        </property>
        <property name="authorizationCacheName" value="shiro.authorizationCache"/>
    </bean>

    <bean id="myRealm" class="org.apache.shiro.realm.text.IniRealm" init-method="init">
        <property name="name" value="myRealm"/>
        <property name="authorizationCacheName" value="shiro.authorizationCache"/>
        <property name="resourcePath" value="classpath:config/myRealm.ini"/>

    </bean>

    <!-- =========================================================
         Shiro Spring-specific integration
         ========================================================= -->
    <!-- Post processor that automatically invokes init() and destroy() methods
         for Spring-configured Shiro objects so you don't have to
         1) specify an init-method and destroy-method attributes for every bean
            definition and
         2) even know which Shiro objects require these methods to be
            called. -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
         the lifecycleBeanProcessor has run: -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>


    <!-- Secure Spring remoting:  Ensure any Spring Remoting method invocations can be associated
         with a Subject for security checks. -->
    <bean id="secureRemoteInvocationExecutor" class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml -
         web.xml uses the DelegatingFilterProxy to access this bean.  This allows us
         to wire things with more control as well utilize nice Spring things such as
         PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login"/>
        <property name="successUrl" value="/index"/>
        <property name="unauthorizedUrl" value="/unauthorized"/>
        <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean
             defined will be automatically acquired and available via its beanName in chain
             definitions, but you can perform overrides or parent/child consolidated configuration
             here if you like: -->
        <!-- <property name="filters">
            <util:map>
                <entry key="aName" value-ref="someFilterPojo"/>
            </util:map>
        </property> -->
        <property name="filterChainDefinitions">
            <value>
                /login = authc
                /account = user
                /manage = user,roles[admin]
            </value>
        </property>
    </bean>

</beans>

5、基於url資源的權限管理

    我們可以簡單配置在shiroFilter的filterChainDefinitions中,也可以考慮通過一個文本文件,我們讀入內容後設置進去。或者通過Ini類來裝入Ini文件內容,到時取出urls的部分來設置給shiroFilter的filterChainDefinitions。也可以把這部分數據存入數據庫表中,到時讀出一個Map來設置給shiroFilter的filterChainDefinitionsMap屬性。

 

6、url的配置

   authc是認證用戶(rememberMe的用戶也必須再次登錄才能訪問該url),配置成user才能讓rememberMe用戶也可以訪問。

 

7、rememberMe Cookie的處理

    Shiro有一套缺省機制,由CookieRememberMeManager實現。

其有一個SimpleCookie類,保存對應的用戶信息等。每次保存時,系統把SimpleCookie的信息設置好之後,先用DefaultSerializer把其用jvm缺省序列化方式序列化成byte[],然後再用cipherService(缺省是aes加密算法)來加密該byte[],最後用Base64.encodeToString(serialized)壓縮成一個字符串,再寫入名稱爲rememberMe的Cookie中。

 

讀取時,通過把該rememberMe Cookie的內容用byte[] decoded = Base64.decode(base64)解壓出該byte[],再用cipherService解密,最後用DefaultSerializer反序列化出來該SimpleCookie類。

 

如果我們有自定義的rememberMe Cookie需要處理,特別是在和其他網站一起SSO,通過訪問主域的Cookie來獲取記錄的用戶信息時,我們需要重新實現rememberMeManager(可以考慮繼承AbstractRememberMeManager), 和根據實際用的序列化方式Serializer來實現一個(比如考慮通用性,用json方式序列化)。在Spring配置中,配置好 RememberMeManager,裝配上sericerlizer和cipherService(根據實際情況選用適當的加密算法),最後把 rememberMeManager設置給DefaultWebSecurityManager即可。如果非常簡單的cookie,可以直接實現RememberMeManager的幾個接口方法也行。

 

說到這裏, Apache Shiro的一些基本配置和擴展情況應該差不多了。關於它的授權和使用等模型,其官網有基本的說明,也有相關的例子。這裏主要是針對其實際應用中的一些使用和擴展方式。

 

 

原文鏈接:http://blog.sina.com.cn/s/blog_6638b10d0100pd88.html

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