Apache shiro學習筆記+ spring整合shiro (一)


Apache Shiro官網:http://shiro.apache.org/

概念:

shiro是一個強大靈活的開源安全框架,可以乾淨利落的處理認證、授權、企業會話管理和加密。

shiro的首要目標是簡單和易於理解。安全有時候是非常複雜令人頭疼的問題,shiro隱藏了複雜

的細節,暴露了簡介直觀的API來簡化開發者使他們的應用程序更安全的努力。

 

功能:進行用戶認證、執行訪問控制、單點登錄、Remember Me服務

 

安全的一些概念:身份認證、授權、會話管理、加密被認爲是構成應用安全的基礎要素。

身份認證,就是驗證用戶身份,典型的“登錄”過程就是身份認證的過程。

授權:也就是訪問控制(ac),也即某用戶可以可以使用應用的哪些功能?

會議管理:管理用戶特定的會話,即使在非Web或EJB應用。

加密:保持數據的安全使用的加密算法,同時仍然易於使用。

對於Session,在WEB應用中它基於HttpSession,而一般應用中默認使用企業會話管理。shiro的Session

用起來和HttpSession差不多,並且使用它不需要Http環境。

 

在最高的層次上講,shiro主要有三個主要的概念,它們分別是Subject、SecurityManager和Realms。

 

1、shiro使用Subject這一概念來描述當前用戶,‘User’這個詞通常隱含的指一個人,但是Subject可以是一個

人也可以代表第三方服務,守護賬戶,定時作業,或者其他類似的與軟件交互的事物。

 

2、所有Subject實例都與一個SecurityManager綁定,當你和一個Subject交互時,所有交互都會通過SecurityManager

轉換爲特定Subject的交互。

SecurityManager是shiro架構的核心,它扮演‘保護傘’對象的角色,與其他內部安全組件一起構成對象圖的核心。 

SecurityManager及其內貿部對象圖一旦被應用配置,它基本上就被放在一邊不再被程序員使用了,

開發人員基本上總是在與Subject的相關API打交道。它在幕後支持Subject的安全操作。

 

3、Realms在shiro與你的應用程序的安全數據之間充當着橋樑或者說是連接器的作用。

從這種意義上講,Reaml就是一個特定的安全DAO,它爲shiro提供所需的數據。

在配置shiro時,你必須爲認證、授權指定至少一個可用的Realm。

 

shiro提供了現成的Realm連接到LDAP、關係數據庫(JDBC)、ini和properties文件等。

用戶可以使用自己的Realm



Shiro 是一個用 Java 語言實現的框架,通過一個簡單易用的 API 提供身份驗證和授權。使用 Shiro,您就能夠爲您的應用程序提供安全性而又無需從頭編寫所有代碼。


Shiro 的 
Session 對象允許無需 HttpSession 即可使用一個用戶會話。通過使用一個通用的 Session 對象,即便該代碼沒有在一個 Web 應用程序中運行,仍可以使用相同的代碼。沒有對應用服務器或 Web 應用服務器會話管理的依賴,您甚至可以在命令行環境中使用 Shiro。換言之,使用 Shiro 的 API 編寫的代碼讓您可以構建連接到 LDAP 服務器的命令行應用程序並且與 web 應用程序內用來訪問 LDAP 服務器的代碼相同。由於 Shiro 提供具有諸多不同數據源的身份驗證,以及 Enterprise Session Management,所以是實現單點登錄(SSO)的理想之選 — 大型企業內的一個理想特性,因爲在大型企業內,用戶需要在一天內經常登錄到並使用不同系統。這些數據源包括 JDBC、LDAP、 Kerberos 和 Microsoft® Active Directory® Directory Services (AD DS)。

Shiro 是預構建的二進制發行版。您可以下載 Shiro JAR 文件或將各項放入到 Apache Maven 或 Apache Ivy 來自動安裝這些文件。本例使用 Ivy 下載 Shiro JAR 文件以及其他所需要的庫,腳本很簡單,如清單 1 所示。


1. Apache Ivy 文件和 Apache Ant 腳本
				
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
    <info organisation="com.nathanagood.examples" module="shirotest" />
    <configurations>
        <conf name="dist" description="Dependency configuration for distribution." />
    </configurations>
    <dependencies>
        <dependency org="commons-logging" name="commons-logging"
            rev="1.1.1" conf="dist->default" />
        <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.8"
            conf="dist->default" />
        <dependency org="org.apache.shiro" name="shiro-core" rev="1.0.0-incubating"
            conf="dist->default" />
        <dependency org="org.apache.shiro" name="shiro-web" rev="1.0.0-incubating"
            conf="dist->default" />
    </dependencies>
</ivy-module>

<project name="shiroTestApp" default="usage" basedir="." 
    xmlns:ivy="antlib:org.apache.ivy.ant">
    <property name="project.lib" value="lib" />
    <path id="ivy.task.path">
        <fileset dir="${basedir}/ivy-lib">
            <include name="**/*.jar" />
        </fileset>
    </path>

    <target name="resolve">
        <taskdef resource="org/apache/ivy/ant/antlib.xml" 
            uri="antlib:org.apache.ivy.ant" classpathref="ivy.task.path" />
        <ivy:resolve />
        <ivy:retrieve pattern="${project.lib}/[conf]/[artifact].[ext]" sync="true" />
    </target>

    <target name="usage">
        <echo message="Use --projecthelp to learn more about this project" />
    </target>
</project>

有關使用 Ivy 的更多信息,參見 參考資料。如果不使用 Maven 或 Ivy,可用從本文 參考資料 部分提供的下載站點下載這些 Shiro JAR 文件。

下載了這些庫後,只需將它們添加到 CLASSPATH。編寫 清單 2 內所示的簡單代碼,它獲得對當前用戶的一個引用並報告說用戶未經身份驗證。(使用 Subject 類來代表此用戶。)


清單 2. ShiroTest Java 類
				
package com.nathanagood.examples.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ShiroTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

    public static void main(String[] args) {
        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("auth.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager 
            = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

添加了此代碼後,創建一個名爲 auth.ini 的文件。此時,這個文件是空白的;它的作用只是爲了能夠在這裏運行這個示例來檢查代碼是否正常工作。

創建文件後,運行這個示例。應該會看到包含了一個 INFO 登錄消息的輸出,報告說用戶沒有登錄。

SecurityUtils 對象是一個 singleton,這意味着不同的對象可以使用它來獲得對當前用戶的訪問。一旦成功地設置了這個SecurityManager,就可以在應用程序不同部分調用 SecurityUtils.getSubject() 來獲得當前用戶的信息。

用戶令牌

在 Shiro 術語中,令牌 指的是一個鍵,可用它登錄到一個系統。最基本和常用的令牌是 UsernamePasswordToken,用以指定用戶的用戶名和密碼。

UsernamePasswordToken 類實現了 AuthenticationToken 接口,它提供了一種獲得憑證和用戶的主體(帳戶身份)的方式。UsernamePasswordToken 適用於大多數應用程序,並且您還可以在需要的時候擴展 AuthenticationToken 接口來將您自己獲得憑證的方式包括進來。例如,可以擴展這個接口來提供您應用程序用來驗證用戶身份的一個關鍵文件的內容。

簡單的身份驗證

至此,這個簡單的示例涵蓋的內容包括:啓動 Shiro SecurityManager、獲得當前用戶並記錄下用戶未經身份驗證。接下來的這個例子將會使用 UsernamePasswordToken 和存儲在 INI 文件內的一個用戶記錄來展示如何通過用戶名和密碼進行身份驗證。

清單 3 內所示的 auth.ini 文件現在包含了一個用戶記錄;這個記錄包含了用戶名和密碼。您可以在這個記錄內定義角色以及爲應用程序提供授權。


清單 3. auth.ini 文件
				
[users]
bjangles = dance

現在,創建 前一節 中介紹的這個 UsernamePasswordToken 對象,如 清單 4 所示。


清單 4. 使用 UsernamePasswordToken 類
				
// snipped... same as before.
public class ShiroTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

    public static void main(String[] args) {
        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("auth.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());
        
        UsernamePasswordToken token = new UsernamePasswordToken("bjangles", "dance");
        
        user.login(token);
        
        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

UsernamePasswordToken 對象由用戶名和密碼的組合實例化。隨後,令牌被傳遞至 Subject 類的 login() 方法。

再次運行這個示例。注意到登錄消息現在報告說此用戶已經身份驗證。

要確保代碼正常工作且獲得的不是一個誤報,在代碼內更改密碼或更改這個 INI 文件並再次運行此示例。login() 方法現在會拋出一個 IncorrectCredentialsException。在生產代碼內這個異常應被明確捕獲以便應用程序在用戶提供了不正確的代碼時能夠進行恰當的響應。

如果用戶不正確,login() 方法就會拋出一個 UnknownAccountException。我們既要考慮如何處理這個異常,但又不應向用戶提供太多信息。一種常見的做法是不要向用戶提示用戶名有效、只有密碼不正確。這是因爲如果有人試圖通過猜測獲得訪問,那麼您絕對不會想要暗示此人他所猜測的用戶名是正確的。

用 LDAP 進行身份驗證

LDAP 是用來在 TCP/IP 上查詢目錄的一種協議。這些目錄可保存有關用戶的任意數量的信息,包括用戶 ID、聯繫信息以及組成員等。LDAP 目錄對於公司的通訊簿很有用並被廣泛使用。

AD DS 是一種用於用戶和組管理的常見目錄,它支持 LDAP。 Shiro 不包含通用的 LDAP 安全域,但它卻包含了一個ActiveDirectoryRealm 對象,允許針對 LDAP 進行用戶的身份驗證。本例使用了在 INI 文件內配置的 ActiveDirectoryRealm 對象來驗證用戶的身份。雖然 AD DS 與 LDAP 不同,但本文中使用的 Shiro 的這個版本並沒有自帶通用的 LDAP 對象。

讓一個 LDAP 服務器來測試本例要比編寫並運行示例本身需要更多工作。如果您不能訪問一個 AD DS 服務器,那麼可以考慮下載並安裝 Apache Directory 來提供一個 LDAP 服務器的樣例實現。Apache Directory 是用 Java 語言編寫的。同樣地,Apache Active Directory Studio 是一個 Eclipse 插件,可用來瀏覽 LDAP 數據。它還具有一些樣例數據,爲您提供了一種快捷方式來針對已知值編寫代碼,而又無需疑惑您遇到的問題是代碼問題還是數據問題。

清單 5 顯示了用來對存儲在 Apache Directory 內的一個用戶進行身份驗證所需的代碼。


清單 5. 使用 LDAP 進行身份驗證
				
// snipped...
public class ShiroLDAPTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroLDAPTest.class);

    /**
     * @param args
     */
    public static void main(String[] args) {

        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("actived.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());

        UsernamePasswordToken token = 
        new UsernamePasswordToken(
            "cn=Cornelius Buckley,ou=people,o=sevenSeas", "argh");

        user.login(token);

        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

除了 INI 文件名和用戶名及密碼之外,代碼與之前用 INI 文件內的記錄進行身份驗證的代碼相同。這種類似性的出現是因爲您可以使用 INI 文件來配置 Shiro。用來設置 Shiro 針對 Apache Directory 進行身份驗證的這些 INI 記錄如 清單 6 所示。


清單 6. actived.ini 文件
				
[main]
activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
activeDirectoryRealm.systemUsername = uid=admin,ou=system
activeDirectoryRealm.systemPassword = secret
activeDirectoryRealm.searchBase = o=sevenSeas,ou=people
activeDirectoryRealm.url = ldap://localhost:10389

注意: 我使用 Apache Directory Studio 來將用戶的密碼更改爲一個能放入測試代碼以確保它工作的值。

運行一個 Grails web 應用程序

您可以使用兩種基本技巧將 Shiro 應用到 web 應用程序。首先,您可以使用這個 API 來將這裏所展示的的代碼併入一個基礎 servlet。其次,您可以使用 Shiro 自帶的 HTTP 過濾器。本例展示了第二種技巧,因爲使用過濾器充分利用了內置 web 應用服務器技術以及來自 Shiro 項目的預先編寫好的代碼。

本例顯示瞭如何使用 Grails 內的這些過濾器。Grails 是一個項目,旨在讓您通過使用一種慣例優先原則(convention-over-configuration)的方式儘快地編寫 Groovy web 應用程序。有關 Grails 的更多信息,請參見 參考資料

對於 Shiro 過濾器,您通常會手動地向 web.xml 文件添加必需的過濾器項。但是,Grails 會在您每次啓動應用程序時生成 web.xml 文件,因此不必手動修改 web.xml。

幸運的是,Grails 提供了插件,可集成到 web.xml 生成過程並會讓您也可以參與在 web.xml 文件內編寫這些項。如今,Grails 的可用插件很多,包括面向 Shiro 的這個插件。建議嘗試使用這個 Shiro Grails 插件,它提供了幾個新的腳本,運行這些腳本可以創建不同的域和控制器。

或者,如果您更願意自己添加這些項並進行配置,您也可以編寫您自己的插件。對於 Grails,編寫一個新的插件很容易。要創建能將必需的 Shiro 過濾器項添加到 web.xml 文件的 Grails 插件,可以使用如下命令:

> grails create-plugin ShiroWebXml

創建了這個插件項目後,編輯 ShiroWebXmlPlugin.groovy 文件,並添加如 清單 7 所示的代碼。


清單 7. 示例插件
				
class ShiroWebXmlPlugin {
   
   // snipped plugin details... 

   def doWithWebDescriptor = { xml ->

       def filterElement = xml.'filter'
       def lastFilter = filterElement[filterElement.size() - 1]

       lastFilter + {
           'filter' {
               'filter-name'("ShiroFilter")
               'filter-class'("org.apache.shiro.web.servlet.IniShiroFilter")
               'init-param' {
                   'param-name'("config")
                   'param-value'("\n#config")
               }
           }
       }

       def filterMappingElement = xml.'filter-mapping'
       def lastFilterMappingElement = 
           filterMappingElement[filterMappingElement.size() - 1]

       lastFilterMappingElement + {
           'filter-mapping' {
               'filter-name'("ShiroFilter")
               'url-pattern'("/*")
               }
           }
       }
}

當 Grails 執行這個 web.xml 文件時,此代碼就會運行。

在啓動這個插件應用程序測試它之前,先將用 Ivy 下載的 Shiro JAR 文件複製到此插件的 lib 文件夾。有了 JAR 文件後,用以下命令測試此插件是否能正常工作:

grails run-app

如果此插件應用程序成功啓動,您可以將它打包以便用在一個示例項目中。要打包這個插件,使用如下命令:

grails package-plugin

可以用如下命令安裝這個新的 ShiroWebXmlPlugin

cd myapp
grails install-plugin /path/to/shiro-web-xml-1.0.zip

故障排除

如果出現了 UnavailableSecurityManagerException,那麼可能是 SecurityManager 未被正確設置。在調用 getSubject() 方法之前,請確保它設在 SecurityUtils 對象上。

連接到 LDAP 服務器可能會有些困難。如果得到一個 javax.naming.CommunicationException,那麼請檢查此 LDAP 服務器的主機名和端口。即便您沒有使用 Apache Directory,Apache Directory Studio(可單獨安裝)也可以幫助您排除連接故障以及名稱問題。

如果您沒有使用 Ant Ivy 腳本來初始化您的環境,那麼有可能會得到丟失類的錯誤。即便沒有 Apache Commons Logging library (commons-logging),這個 INI 文件示例仍可運行,但是運行這個 LDAP 示例會導致一個異常。這時,可以使用 Apace CommonsBeanUtils 來解析這個 INI 文件並設置對象上的值。

結束語

Shiro 是 Apache Incubator 內的一個框架,可讓您嚮應用程序中添加身份驗證和授權。它支持不同的身份驗證存儲方式,比如 LDAP



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