<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    paulwong

    讓Apache Shiro保護(hù)你的應(yīng)用

    什么是Apache Shiro?

    Apache Shiro(發(fā)音為“shee-roh”,日語“堡壘(Castle)”的意思)是一個(gè)強(qiáng)大易用的Java安全框架,提供了認(rèn)證、授權(quán)、加密和會(huì)話管理功能,可為任何應(yīng)用提供安全保障 - 從命令行應(yīng)用、移動(dòng)應(yīng)用到大型網(wǎng)絡(luò)及企業(yè)應(yīng)用。 Shiro為解決下列問題(我喜歡稱它們?yōu)閼?yīng)用安全的四要素)提供了保護(hù)應(yīng)用的API:
    • 認(rèn)證 - 用戶身份識(shí)別,常被稱為用戶“登錄”;
    • 授權(quán) - 訪問控制;
    • 密碼加密 - 保護(hù)或隱藏?cái)?shù)據(jù)防止被偷窺;
    • 會(huì)話管理 - 每用戶相關(guān)的時(shí)間敏感的狀態(tài)。
    Shiro還支持一些輔助特性,如Web應(yīng)用安全、單元測(cè)試和多線程,它們的存在強(qiáng)化了上面提到的四個(gè)要素。

    為何要?jiǎng)?chuàng)建Apache Shiro?

    對(duì)于一個(gè)框架來講,使其有存在價(jià)值的最好例證就是有讓你去用它的原因,它應(yīng)該能完成一些別人無法做到的事情。要理解這一點(diǎn),需要了解Shiro的歷史以及創(chuàng)建它時(shí)的其他替代方法。

    在2008年加入Apache軟件基金會(huì)之前,Shiro已經(jīng)5歲了,之前它被稱為JSecurity項(xiàng)目,始于2003年初。當(dāng)時(shí),對(duì)于Java應(yīng)用開發(fā)人員而言,沒有太多的通用安全替代方案 - 我們被Java認(rèn)證/授權(quán)服務(wù)(或稱為JAAS)緊緊套牢了。

    JAAS有太多的缺點(diǎn) - 盡管它的認(rèn)證功能尚可忍受,但授權(quán)方面卻顯得拙劣,用起來令人沮喪。

    此外,JAAS跟虛擬機(jī)層面的安全問題關(guān)系非常緊密,如判斷JVM中是否允許裝入一個(gè)類。作為應(yīng)用開發(fā)者,我更關(guān)心應(yīng)用最終用戶能做什么,而不是我的代碼在JVM中能做什么。

    由于當(dāng)時(shí)正從事應(yīng)用開發(fā),我也需要一個(gè)干凈、容器無關(guān)的會(huì)話機(jī)制。在當(dāng)時(shí),“這場(chǎng)游戲”中唯一可用的會(huì)話是HttpSessions,它需要Web容器;或是EJB 2.1里的有狀態(tài)會(huì)話Bean,這又要EJB容器。而我想要的一個(gè)與容器脫鉤、可用于任何我選擇的環(huán)境中的會(huì)話。

    最后就是加密問題。有時(shí),我們需要保證數(shù)據(jù)安全,但是Java密碼架構(gòu)(Java Cryptography Architecture)讓人難以理解,除非你是密碼學(xué)專家。

    API里到處都是Checked Exception,用起來很麻煩。我需要一個(gè)干凈、開箱即用的解決方案,可以在需要時(shí)方便地對(duì)數(shù)據(jù)加密/解密。于是,縱觀2003年初的安全狀況,你會(huì)很快意識(shí)到還沒有一個(gè)大一統(tǒng)的框架滿足所有上述需求。

    有鑒于此,JSecurity(即之后的Apache Shiro)誕生了。

    今天,你為何愿意使用Apache Shiro?

    從2003年至今,框架選擇方面的情況已經(jīng)改變了不少,但今天仍有令人信服的理由讓你選擇Shiro。其實(shí)理由相當(dāng)多,Apache Shiro:
    • 易于使用 - 易用性是這個(gè)項(xiàng)目的最終目標(biāo)。應(yīng)用安全有可能會(huì)非常讓人糊涂,令人沮喪,并被認(rèn)為是“必要之惡”【譯注:比喻應(yīng)用安全方面的編程?!?。若是能讓它簡(jiǎn)化到新手都能很快上手,那它將不再是一種痛苦了。
    • 廣泛性 - 沒有其他安全框架可以達(dá)到Apache Shiro宣稱的廣度,它可以為你的安全需求提供“一站式”服務(wù)。
    • 靈活性 - Apache Shiro可以工作在任何應(yīng)用環(huán)境中。雖然它工作在Web、EJB和IoC環(huán)境中,但它并不依賴這些環(huán)境。Shiro既不強(qiáng)加任何規(guī)范,也無需過多依賴。
    • Web能力 - Apache Shiro對(duì)Web應(yīng)用的支持很神奇,允許你基于應(yīng)用URL和Web協(xié)議(如REST)創(chuàng)建靈活的安全策略,同時(shí)還提供了一套控制頁面輸出的JSP標(biāo)簽庫。
    • 可插拔 - Shiro干凈的API和設(shè)計(jì)模式使它可以方便地與許多的其他框架和應(yīng)用進(jìn)行集成。你將看到Shiro可以與諸如Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin這類第三方框架無縫集成。
    • 支持 - Apache Shiro是Apache軟件基金會(huì)成員,這是一個(gè)公認(rèn)為了社區(qū)利益最大化而行動(dòng)的組織。項(xiàng)目開發(fā)和用戶組都有隨時(shí)愿意提供幫助的友善成員。像Katasoft這類商業(yè)公司,還可以給你提供需要的專業(yè)支持和服務(wù)。

    誰在用Shiro?

    Shiro及其前身JSecurity已被各種規(guī)模和不同行業(yè)的公司項(xiàng)目采用多年。

    自從成為Apache軟件基金會(huì)的頂級(jí)項(xiàng)目后,站點(diǎn)流量和使用呈持續(xù)增長(zhǎng)態(tài)勢(shì)。

    許多開源社區(qū)也正在用Shiro,這里有些例子如Spring,Grails,Wicket,Tapestry,Tynamo,Mule和Vaadin。如Katasoft,Sonatype,MuleSoft這樣的商業(yè)公司,一家大型社交網(wǎng)絡(luò)和多家紐約商業(yè)銀行都在使用Shiro來保護(hù)他們的商業(yè)軟件和站點(diǎn)。

    核心概念:Subject,SecurityManager和Realms

    既然已經(jīng)描述了Shiro的好處,那就讓我們看看它的API,好讓你能夠有個(gè)感性認(rèn)識(shí)。Shiro架構(gòu)有三個(gè)主要概念 - Subject,SecurityManager和Realms。

    Subject

    在考慮應(yīng)用安全時(shí),你最常問的問題可能是“當(dāng)前用戶是誰?”或“當(dāng)前用戶允許做X嗎?”。

    當(dāng)我們寫代碼或設(shè)計(jì)用戶界面時(shí),問自己這些問題很平常:應(yīng)用通常都是基于用戶故事構(gòu)建的,并且你希望功能描述(和安全)是基于每個(gè)用戶的。所以,對(duì)于我們而言,考慮應(yīng)用安全的最自然方式就是基于當(dāng)前用戶。

    Shiro的API用它的Subject概念從根本上體現(xiàn)了這種思考方式。

    Subject一詞是一個(gè)安全術(shù)語,其基本意思是“當(dāng)前的操作用戶”。

    稱之為“用戶”并不準(zhǔn)確,因?yàn)?#8220;用戶”一詞通常跟人相關(guān)。

    在安全領(lǐng)域,術(shù)語“Subject”可以是人,也可以是第三方進(jìn)程、后臺(tái)帳戶(Daemon Account)或其他類似事物。

    它僅僅意味著“當(dāng)前跟軟件交互的東西”。

    但考慮到大多數(shù)目的和用途,你可以把它認(rèn)為是Shiro的“用戶”概念。在代碼的任何地方,你都能輕易的獲得Shiro Subject,參見如下代碼: 清單1. 獲得Subject
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.SecurityUtils;

    Subject currentUser 
    = SecurityUtils.getSubject();
    一旦獲得Subject,你就可以立即獲得你希望用Shiro為當(dāng)前用戶做的90%的事情,如登錄、登出、訪問會(huì)話、執(zhí)行授權(quán)檢查等 - 稍后還會(huì)看到更多。這里的關(guān)鍵點(diǎn)是Shiro的API非常直觀,因?yàn)樗从沉碎_發(fā)者以‘每個(gè)用戶’思考安全控制的自然趨勢(shì)。同時(shí),在代碼的任何地方都能很輕松地訪問Subject,允許在任何需要的地方進(jìn)行安全操作。

    SecurityManager

    Subject的“幕后”推手是SecurityManager。Subject代表了當(dāng)前用戶的安全操作,SecurityManager則管理所有用戶的安全操作。

    它是Shiro框架的核心,充當(dāng)“保護(hù)傘”,引用了多個(gè)內(nèi)部嵌套安全組件,它們形成了對(duì)象圖。

    但是,一旦SecurityManager及其內(nèi)部對(duì)象圖配置好,它就會(huì)退居幕后,應(yīng)用開發(fā)人員幾乎把他們的所有時(shí)間都花在Subject API調(diào)用上。

    那么,如何設(shè)置SecurityManager呢?嗯,這要看應(yīng)用的環(huán)境。

    例如,Web應(yīng)用通常會(huì)在Web.xml中指定一個(gè)Shiro Servlet Filter,這會(huì)創(chuàng)建SecurityManager實(shí)例,如果你運(yùn)行的是一個(gè)獨(dú)立應(yīng)用,你需要用其他配置方式,但有很多配置選項(xiàng)。一個(gè)應(yīng)用幾乎總是只有一個(gè)SecurityManager實(shí)例。

    它實(shí)際是應(yīng)用的Singleton(盡管不必是一個(gè)靜態(tài)Singleton)。跟Shiro里的幾乎所有組件一樣,SecurityManager的缺省實(shí)現(xiàn)是POJO,而且可用POJO兼容的任何配置機(jī)制進(jìn)行配置 - 普通的Java代碼、Spring XML、YAML、.properties和.ini文件等。

    基本來講,能夠?qū)嵗惡驼{(diào)用JavaBean兼容方法的任何配置形式都可使用。為此,Shiro借助基于文本的INI配置提供了一個(gè)缺省的“公共”解決方案。

    INI易于閱讀、使用簡(jiǎn)單并且需要極少依賴。你還能看到,只要簡(jiǎn)單地理解對(duì)象導(dǎo)航,INI可被有效地用于配置像SecurityManager那樣簡(jiǎn)單的對(duì)象圖。

    注意,Shiro還支持Spring XML配置及其他方式,但這里只我們只討論INI。下列清單2列出了基于INI的Shiro最簡(jiǎn)配置:

    清單2. 用INI配置Shiro
    [main]
    cm 
    = org.apache.shiro.authc.credential.HashedCredentialsMatcher
    cm.hashAlgorithm 
    = SHA-512
    cm.hashIterations 
    = 1024
    # Base64 encoding (less text):
    cm.storedCredentialsHexEncoded 
    = false

    iniRealm.credentialsMatcher 
    = $cm

    [users] 
    jdoe 
    = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2 
    asmith 
    = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB
      在清單2中,我們看到了用于配置SecurityManager實(shí)例的INI配置例子。

    有兩個(gè)INI段落:[main]和[users]. [main]段落是配置SecurityManager對(duì)象及其使用的其他任何對(duì)象(如Realms)的地方。

    在示例中,我們看到配置了兩個(gè)對(duì)象:
    1. cm對(duì)象,是Shiro的HashedCredentialsMatcher類實(shí)例。如你所見,cm實(shí)例的各屬性是通過“嵌套點(diǎn)”語法進(jìn)行配置的 - 在清單3中可以看到IniSecurityManagerFactory使用的慣例,這種方法代表了對(duì)象圖導(dǎo)航和屬性設(shè)置。
    2. iniRealm對(duì)象,它被SecurityManager用來表示以INI格式定義的用戶帳戶。
    [users]段落是指定用戶帳戶靜態(tài)列表的地方 - 為簡(jiǎn)單應(yīng)用或測(cè)試提供了方便。

    就介紹而言,詳細(xì)了解每個(gè)段落的細(xì)節(jié)并不是重點(diǎn)。
    相反,看到INI配置是一種配置Shiro的簡(jiǎn)單方式才是關(guān)鍵。關(guān)于INI配置的更多細(xì)節(jié),請(qǐng)參見Shiro文檔
     
    清單3. 裝入shiro.ini配置文件  
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.util.Factory;



    //1.裝入INI配置 
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

    //2. 創(chuàng)建SecurityManager 
    SecurityManager securityManager = factory.getInstance();

    //3. 使其可訪問 
    SecurityUtils.setSecurityManager(securityManager);
    在清單3的示例中,我們看到有三步:
    1. 裝入用來配置SecurityManager及其構(gòu)成組件的INI配置文件;
    2. 根據(jù)配置創(chuàng)建SecurityManager實(shí)例(使用Shiro的工廠概念,它表述了工廠方法設(shè)計(jì)模式);
    3. 使應(yīng)用可訪問SecurityManager Singleton。在這個(gè)簡(jiǎn)單示例中,我們將它設(shè)置為VM靜態(tài)Singleton,但這通常不是必須的 - 你的應(yīng)用配置機(jī)制可以決定你是否需要使用靜態(tài)存儲(chǔ)。

    Realms

    Shiro的第三個(gè)也是最后一個(gè)概念是Realm

    Realm充當(dāng)了Shiro與應(yīng)用安全數(shù)據(jù)間的“橋梁”或者“連接器”。也就是說,當(dāng)切實(shí)與像用戶帳戶這類安全相關(guān)數(shù)據(jù)進(jìn)行交互,執(zhí)行認(rèn)證(登錄)和授權(quán)(訪問控制)時(shí),Shiro會(huì)從應(yīng)用配置的Realm中查找很多內(nèi)容。

    從這個(gè)意義上講,Realm實(shí)質(zhì)上是一個(gè)安全相關(guān)的DAO:它封裝了數(shù)據(jù)源的連接細(xì)節(jié),并在需要時(shí)將相關(guān)數(shù)據(jù)提供給Shiro。

    當(dāng)配置Shiro時(shí),你必須至少指定一個(gè)Realm,用于認(rèn)證和(或)授權(quán)。配置多個(gè)Realm是可以的,但是至少需要一個(gè)。 Shiro內(nèi)置了可以連接大量安全數(shù)據(jù)源(又名目錄)的Realm,如LDAP、關(guān)系數(shù)據(jù)庫(JDBC)、類似INI的文本配置資源以及屬性文件等。如果缺省的Realm不能滿足需求,你還可以插入代表自定義數(shù)據(jù)源的自己的Realm實(shí)現(xiàn)。

    下面的清單4是通過INI配置Shiro使用LDAP目錄作為應(yīng)用Realm的示例。

    清單4. Realm配置示例片段:連接存儲(chǔ)用戶數(shù)據(jù)的LDAP
    [main]
    ldapRealm 
    = org.apache.shiro.realm.ldap.JndiLdapRealm
    ldapRealm.userDnTemplate 
    = uid={0},ou=users,dc=mycompany,dc=com
    ldapRealm.contextFactory.url 
    = ldap://ldapHost:389
    ldapRealm.contextFactory.authenticationMechanism 
    = DIGEST-MD5 
    既然已經(jīng)了解如何建立一個(gè)基本的Shiro環(huán)境,下面讓我們來討論,作為一名開發(fā)者該如何使用這個(gè)框架。

    認(rèn)證

    認(rèn)證是核實(shí)用戶身份的過程。也就是說,當(dāng)用戶使用應(yīng)用進(jìn)行認(rèn)證時(shí),他們就在證明他們就是自己所說的那個(gè)人。有時(shí)這也理解為“登錄”。它是一個(gè)典型的三步驟過程。
    1. 收集用戶的身份信息,稱為當(dāng)事人(principal),以及身份的支持證明,稱為證書(Credential)。
    2. 將當(dāng)事人和證書提交給系統(tǒng)。
    3. 如果提交的證書與系統(tǒng)期望的該用戶身份(當(dāng)事人)匹配,該用戶就被認(rèn)為是經(jīng)過認(rèn)證的,反之則被認(rèn)為未經(jīng)認(rèn)證的。
    這個(gè)過程的常見例子是大家都熟悉的“用戶/密碼”組合。多數(shù)用戶在登錄軟件系統(tǒng)時(shí),通常提供自己的用戶名(當(dāng)事人)和支持他們的密碼(證書)。

    如果存儲(chǔ)在系統(tǒng)中的密碼(或密碼表示)與用戶提供的匹配,他們就被認(rèn)為通過認(rèn)證。

    Shiro以簡(jiǎn)單直觀的方式支持同樣的流程。

    正如我們前面所說,Shiro有一個(gè)以Subject為中心的API - 幾乎你想要用Shiro在運(yùn)行時(shí)完成的所有事情都能通過與當(dāng)前執(zhí)行的Subject進(jìn)行交互而達(dá)成。

    因此,要登錄Subject,只需要簡(jiǎn)單地調(diào)用它的login方法,傳入表示被提交當(dāng)事人和證書(在這種情況下,就是用戶名和密碼)的AuthenticationToken實(shí)例。示例如清單5中所示:

    清單5. Subject登錄
    //1. 接受提交的當(dāng)事人和證書:
    AuthenticationToken token =
    new UsernamePasswordToken(username, password);

    //2. 獲取當(dāng)前Subject:
    Subject currentUser = SecurityUtils.getSubject();
    //3. 登錄: 
    currentUser.login(token);
      你可以看到,Shiro的API很容易地就反映了這個(gè)常見流程。

    你將會(huì)在所有的Subject操作中繼續(xù)看到這種簡(jiǎn)單風(fēng)格。

    在調(diào)用了login方法后,SecurityManager會(huì)收到AuthenticationToken,并將其發(fā)送給已配置的Realm,執(zhí)行必須的認(rèn)證檢查。每個(gè)Realm都能在必要時(shí)對(duì)提交的AuthenticationTokens作出反應(yīng)。

    但是如果登錄失敗了會(huì)發(fā)生什么?如果用戶提供了錯(cuò)誤密碼又會(huì)發(fā)生什么?

    通過對(duì)Shiro的運(yùn)行時(shí)AuthenticationException做出反應(yīng),你可以控制失敗,參見清單6。

    清單6. 控制失敗的登錄
    //3. 登錄:
    try {
        currentUser.login(token);
    }
     catch (IncorrectCredentialsException ice) {
        …
    }
     catch (LockedAccountException lae) {
        …
    }


    catch (AuthenticationException ae) {…
    }
     
      你可以選擇捕獲AuthenticationException的一個(gè)子類,作出特定的響應(yīng),或者對(duì)任何AuthenticationException做一般性處理(例如,顯示給用戶普通的“錯(cuò)誤的用戶名或密碼”這類消息)。

    選擇權(quán)在你,可以根據(jù)應(yīng)用需要做出選擇。 Subject登錄成功后,他們就被認(rèn)為是已認(rèn)證的,通常你會(huì)允許他們使用你的應(yīng)用。但是僅僅證明了一個(gè)用戶的身份并不意味著他們可以對(duì)你的應(yīng)用為所欲為。

    這就引出了另一個(gè)問題,“我如何控制用戶能做或不能做哪些事情?”,決定用戶允許做哪些事情的過程被稱為授權(quán)。下面我們將談?wù)凷hiro如何進(jìn)行授權(quán)。

    授權(quán)

    授權(quán)實(shí)質(zhì)上就是訪問控制 - 控制用戶能夠訪問應(yīng)用中的哪些內(nèi)容,比如資源、Web頁面等等。

    多數(shù)用戶執(zhí)行訪問控制是通過使用諸如角色和權(quán)限這類概念完成的。

    也就是說,通常用戶允許或不允許做的事情是根據(jù)分配給他們的角色或權(quán)限決定的。

    那么,通過檢查這些角色和權(quán)限,你的應(yīng)用程序就可以控制哪些功能是可以暴露的。

    如你期望的,Subject API讓你可以很容易的執(zhí)行角色和權(quán)限檢查。如清單7中的代碼片段所示:如何檢查Subject被分配了某個(gè)角色:

    列表7. 角色檢查
    if ( subject.hasRole(“administrator”) ) {
        
    //顯示‘Create User’按鈕
    }
     else {
        
    //按鈕置灰?
    }
     
      如你所見,你的應(yīng)用程序可基于訪問控制檢查打開或關(guān)閉某些功能。

    權(quán)限檢查是執(zhí)行授權(quán)的另一種方法。上例中的角色檢查有個(gè)很大的缺陷:你無法在運(yùn)行時(shí)增刪角色。

    角色名字在這里是硬編碼,所以,如果你修改了角色名字或配置,你的代碼就會(huì)亂套!如果你需要在運(yùn)行時(shí)改變角色含義,或想要增刪角色,你必須另辟蹊徑。為此,Shiro支持了權(quán)限(permissions)概念。

    權(quán)限是功能的原始表述,如‘開門’,‘創(chuàng)建一個(gè)博文’,‘刪除‘jsmith’用戶’等。通過讓權(quán)限反映應(yīng)用的原始功能,在改變應(yīng)用功能時(shí),你只需要改變權(quán)限檢查。

    進(jìn)而,你可以在運(yùn)行時(shí)按需將權(quán)限分配給角色或用戶。如清單8中,我們重寫了之前的用戶檢查,取而代之使用權(quán)限檢查。

    清單8. 權(quán)限檢查
    if ( subject.isPermitted(“user:create”) ) {
        
    //顯示‘Create User’按鈕
    }
     else {
        
    //按鈕置灰?
    }
     
    這樣,任何具有“user:create”權(quán)限的角色或用戶都可以點(diǎn)擊‘Create User’按鈕,并且這些角色和指派甚至可以在運(yùn)行時(shí)改變,這給你提供了一個(gè)非常靈活的安全模型。

    “user:create”字符串是一個(gè)權(quán)限字符串的例子,它遵循特定的解析慣例。

    Shiro借助它的WildcardPermission支持這種開箱即用的慣例。盡管這超出了本文的范圍,你會(huì)看到在創(chuàng)建安全策略時(shí),WildcardPermission非常靈活,甚至支持像實(shí)例級(jí)別訪問控制這樣的功能。

    清單9. 實(shí)例級(jí)別的權(quán)限檢查
    if ( subject.isPermitted(“user:delete:jsmith”) ) {
        
    //刪除‘jsmith’用戶
    }
     else {
        
    //不刪除‘jsmith’
    }
      該例表明,你可以對(duì)你需要的單個(gè)資源進(jìn)行訪問控制,甚至深入到非常細(xì)粒度的實(shí)例級(jí)別。

    如果愿意,你甚至還可以發(fā)明自己的權(quán)限語法。參見Shiro Permission文檔可以了解更多內(nèi)容。

    最后,就像使用認(rèn)證那樣,上述調(diào)用最終會(huì)轉(zhuǎn)向SecurityManager,它會(huì)咨詢Realm做出自己的訪問控制決定。

    必要時(shí),還允許單個(gè)Realm同時(shí)響應(yīng)認(rèn)證和授權(quán)操作。以上就是對(duì)Shiro授權(quán)功能的簡(jiǎn)要概述。雖然多數(shù)安全框架止于授權(quán)和認(rèn)證,但Shiro提供了更多功能。下面,我們將談?wù)凷hiro的高級(jí)會(huì)話管理功能。

    會(huì)話管理

    在安全框架領(lǐng)域,Apache Shiro提供了一些獨(dú)特的東西:可在任何應(yīng)用或架構(gòu)層一致地使用Session API。

    即,Shiro為任何應(yīng)用提供了一個(gè)會(huì)話編程范式 - 從小型后臺(tái)獨(dú)立應(yīng)用到大型集群Web應(yīng)用。這意味著,那些希望使用會(huì)話的應(yīng)用開發(fā)者,不必被迫使用Servlet或EJB容器了。

    或者,如果正在使用這些容器,開發(fā)者現(xiàn)在也可以選擇使用在任何層統(tǒng)一一致的會(huì)話API,取代Servlet或EJB機(jī)制。但Shiro會(huì)話最重要的一個(gè)好處或許就是它們是獨(dú)立于容器的。

    這具有微妙但非常強(qiáng)大的影響。

    例如,讓我們考慮一下會(huì)話集群。對(duì)集群會(huì)話來講,支持容錯(cuò)和故障轉(zhuǎn)移有多少種容器特定的方式?Tomcat的方式與Jetty的不同,而Jetty又和Websphere不一樣,等等。

    但通過Shiro會(huì)話,你可以獲得一個(gè)容器無關(guān)的集群解決方案。Shiro的架構(gòu)允許可插拔的會(huì)話數(shù)據(jù)存儲(chǔ),如企業(yè)緩存、關(guān)系數(shù)據(jù)庫、NoSQL系統(tǒng)等。

    這意味著,只要配置會(huì)話集群一次,它就會(huì)以相同的方式工作,跟部署環(huán)境無關(guān) - Tomcat、Jetty、JEE服務(wù)器或者獨(dú)立應(yīng)用。不管如何部署應(yīng)用,毋須重新配置應(yīng)用。

    Shiro會(huì)話的另一好處就是,如果需要,會(huì)話數(shù)據(jù)可以跨客戶端技術(shù)進(jìn)行共享。例如,Swing桌面客戶端在需要時(shí)可以參與相同的Web應(yīng)用會(huì)話中 - 如果最終用戶同時(shí)使用這兩種應(yīng)用,這樣的功能會(huì)很有用。

    那你如何在任何環(huán)境中訪問Subject的會(huì)話呢?請(qǐng)看下面的示例,里面使用了Subject的兩個(gè)方法。

    清單10. Subject的會(huì)話
    Session session = subject.getSession();
    Session session 
    = subject.getSession(boolean create);
      如你所見,這些方法在概念上等同于HttpServletRequest API。

    第一個(gè)方法會(huì)返回Subject的現(xiàn)有會(huì)話,或者如果還沒有會(huì)話,它會(huì)創(chuàng)建一個(gè)新的并將之返回。

    第二個(gè)方法接受一個(gè)布爾參數(shù),這個(gè)參數(shù)用于判定會(huì)話不存在時(shí)是否創(chuàng)建新會(huì)話。

    一旦獲得Shiro的會(huì)話,你幾乎可以像使用HttpSession一樣使用它。Shiro團(tuán)隊(duì)覺得對(duì)于Java開發(fā)者,HttpSession API用起來太舒服了,所以我們保留了它的很多感覺。

    當(dāng)然,最大的不同在于,你可以在任何應(yīng)用中使用Shiro會(huì)話,不僅限于Web應(yīng)用。清單11中顯示了這種相似性。

    清單11. 會(huì)話的方法
    Session session = subject.getSession();
    session.getAttribute(
    "key", someValue); 
    Date start 
    = session.getStartTimestamp();
    Date timestamp 
    = session.getLastAccessTime(); 
    session.setTimeout(millis); 

    加密

    加密是隱藏或混淆數(shù)據(jù)以避免被偷窺的過程。

    在加密方面,Shiro的目標(biāo)是簡(jiǎn)化并讓JDK的加密支持可用。清楚一點(diǎn)很重要,一般情況下,加密不是特定于Subject的,所以它是Shiro API的一部分,但并不特定于Subject。

    你可以在任何地方使用Shiro的加密支持,甚至在不使用Subject的情況下。對(duì)于加密支持,Shiro真正關(guān)注的兩個(gè)領(lǐng)域是加密哈希(又名消息摘要)和加密密碼。下面我們來看看這兩個(gè)方面的詳細(xì)描述。

    哈希

    如果你曾使用過JDK的MessageDigest類,你會(huì)立刻意識(shí)到它的使用有點(diǎn)麻煩。

    MessageDigest類有一個(gè)笨拙的基于工廠的靜態(tài)方法API,它不是面向?qū)ο蟮?,并且你被迫去捕獲那些永遠(yuǎn)都不必捕獲的Checked Exceptions。

    如果需要輸出十六進(jìn)制編碼或Base64編碼的消息摘要,你只有靠自己 - 對(duì)上述兩種編碼,沒有標(biāo)準(zhǔn)的JDK支持它們。Shiro用一種干凈而直觀的哈希API解決了上述問題。

    打個(gè)比方,考慮比較常見的情況,使用MD5哈希一個(gè)文件,并確定該哈希的十六進(jìn)制值。被稱為‘校驗(yàn)和’,這在提供文件下載時(shí)常用到 - 用戶可以對(duì)下載文件執(zhí)行自己的MD5哈希。如果它們匹配,用戶完全可以認(rèn)定文件在傳輸過程中沒有被篡改。

    不使用Shiro,你需要如下步驟才能完成上述內(nèi)容:
    1. 將文件轉(zhuǎn)換成字節(jié)數(shù)組。JDK中沒有干這事的,故而你需要?jiǎng)?chuàng)建一個(gè)輔助方法用于打開FileInputStream,使用字節(jié)緩存區(qū),并拋出相關(guān)的IOExceptions,等等。
    2. 使用MessageDigest類對(duì)字節(jié)數(shù)組進(jìn)行哈希,處理相關(guān)異常,如清單12所示。
    3. 將哈希后的字節(jié)數(shù)組編碼成十六進(jìn)制字符。JDK中還是沒有干這事的,你依舊需要?jiǎng)?chuàng)建另外一個(gè)輔助方法,有可能在你的實(shí)現(xiàn)中會(huì)使用位操作和位移動(dòng)。
    清單12. JDK的消息摘要
    try {
        MessageDigest md 
    = MessageDigest.getInstance("MD5");
        md.digest(bytes);
        
    byte[] hashed = md.digest();
    }
     catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
     
    對(duì)于這樣簡(jiǎn)單普遍的需求,這個(gè)工作量實(shí)在太大了?,F(xiàn)在看看Shiro是如何做同樣事情的:
    String hex = new Md5Hash(myFile).toHex(); 
    當(dāng)使用Shiro簡(jiǎn)化所有這些工作時(shí),一切都非常簡(jiǎn)單明了。完成SHA-512哈希和密碼的Base64編碼也一樣簡(jiǎn)單。
    String encodedPassword = new Sha512Hash(password, salt, count).toBase64();
    你可以看到Shiro對(duì)哈希和編碼簡(jiǎn)化了不少,挽救了你處理在這類問題上所消耗的腦細(xì)胞。

    密碼

    加密是使用密鑰對(duì)數(shù)據(jù)進(jìn)行可逆轉(zhuǎn)換的加密算法。

    我們使用其保證數(shù)據(jù)的安全,尤其是傳輸或存儲(chǔ)數(shù)據(jù)時(shí),以及在數(shù)據(jù)容易被窺探的時(shí)候。如果你曾經(jīng)用過JDK的Cryptography API,特別是javax.crypto.Cipher類,你會(huì)知道它是一頭需要馴服的極其復(fù)雜的野獸。

    對(duì)于初學(xué)者,每個(gè)可能的加密配置總是由一個(gè)javax.crypto.Cipher實(shí)例表示。必須進(jìn)行公鑰/私鑰加密?你得用Cipher。需要為流操作使用塊加密器(Block Cipher)?

    你得用Cipher。需要?jiǎng)?chuàng)建一個(gè)AES 256位Cipher來保護(hù)數(shù)據(jù)?你得用Cipher。你懂的。

    那么如何創(chuàng)建你需要的Cipher實(shí)例?您得創(chuàng)建一個(gè)非直觀、標(biāo)記分隔的加密選項(xiàng)字符串,它被稱為“轉(zhuǎn)換字符串(transformation string)”,把該字符串傳給Cipher.getInstance靜態(tài)工廠方法。

    這種字符串方式的cipher選項(xiàng),并沒有類型安全以確保你正在用有效的選項(xiàng)。

    這也暗示沒有JavaDoc幫你了解相關(guān)選項(xiàng)。并且,如果字符串格式組織不正確,你還需要進(jìn)一步處理Checked Exception,即便你知道配置是正確的。如你所見,使用JDK Cipher是一項(xiàng)相當(dāng)繁重的任務(wù)。

    很久以前,這些技術(shù)曾經(jīng)是Java API的標(biāo)準(zhǔn),但是世事變遷,我們需要一種更簡(jiǎn)單的方法。 Shiro通過引入它的CipherService API試圖簡(jiǎn)化加密密碼的整個(gè)概念。

    CipherService是多數(shù)開發(fā)者在保護(hù)數(shù)據(jù)時(shí)夢(mèng)寐以求的東西:簡(jiǎn)單、無狀態(tài)、線程安全的API,能夠在一次方法調(diào)用中對(duì)整個(gè)數(shù)據(jù)進(jìn)行加密或解密。

    你所需要做的只是提供你的密鑰,就可根據(jù)需要加密或解密。如下列清單13中,使用256位AES加密:

    清單13. Apache Shiro的加密API
    AesCipherService cipherService = new AesCipherService();
    cipherService.setKeySize(
    256);

    //創(chuàng)建一個(gè)測(cè)試密鑰: 
    byte[] testKey = cipherService.generateNewKey();
    //加密文件的字節(jié): 
    byte[] encrypted = cipherService.encrypt(fileBytes, testKey);
    較之JDK的Cipher API,Shiro的示例要簡(jiǎn)單的多:
    • 你可以直接實(shí)例化一個(gè)CipherService - 沒有奇怪或讓人混亂的工廠方法;
    • Cipher配置選項(xiàng)可以表示成JavaBean - 兼容的getter和setter方法 - 沒有了奇怪和難以理解的“轉(zhuǎn)換字符串”;
    • 加密和解密在單個(gè)方法調(diào)用中完成;
    • 沒有強(qiáng)加的Checked Exception。如果愿意,可以捕獲Shiro的CryptoException。
    Shiro的CipherService API還有其他好處,如同時(shí)支持基于字節(jié)數(shù)組的加密/解密(稱為“塊”操作)和基于流的加密/解密(如加密音頻或視頻)。

    不必再忍受Java Cryptography帶來的痛苦。Shiro的Cryptography支持就是為了減少你在確保數(shù)據(jù)安全上付出的努力。

    Web支持

    最后,但并非不重要,我們將簡(jiǎn)單介紹一下Shiro的Web支持。Shiro附帶了一個(gè)幫助保護(hù)Web應(yīng)用的強(qiáng)建的Web支持模塊。對(duì)于Web應(yīng)用,安裝Shiro很簡(jiǎn)單。唯一需要做的就是在web.xml中定義一個(gè)Shiro Servlet過濾器。清單14中,列出了代碼。 清單14. web.xml中的ShiroFilter
    <filter>
        
    <filter-name>ShiroFilter</filter-name>
        
    <filter-class>
             org.apache.shiro.web.servlet.IniShiroFilter
        
    </filter-class>
        
    <!-- 沒有init-param屬性就表示從classpath:shiro.ini裝入INI配置 --> 
    </filter>
    <filter-mapping>
        
    <filter-name>ShiroFilter</filter-name>
        
    <url-pattern>/*</url-pattern> 
    </filter-mapping>
      這個(gè)過濾器可以讀取上述shiro.ini配置,這樣不論什么開發(fā)環(huán)境,你都擁有了一致的配置體驗(yàn)。

    一旦完成配置,Shiro Filter就會(huì)過濾每個(gè)請(qǐng)求并且確保在請(qǐng)求期間特定請(qǐng)求的Subject是可訪問的。

    同時(shí)由于它過濾了每個(gè)請(qǐng)求,你可以執(zhí)行安全特定的邏輯以保證只有滿足一定標(biāo)準(zhǔn)的請(qǐng)求才被允許通過。

    URL特定的Filter鏈

    Shiro通過其創(chuàng)新的URL過濾器鏈功能支持安全特定的過濾規(guī)則。

    它允許你為任何匹配的URL模式指定非正式的過濾器鏈。這意味著, 使用Shiro的過濾器機(jī)制,你可以很靈活的強(qiáng)制安全規(guī)則(或者規(guī)則的組合) - 其程度遠(yuǎn)遠(yuǎn)超過你單獨(dú)在web.xml中定義過濾器時(shí)所獲得的。

    清單15中顯示了Shiro INI中的配置片段。

    清單15. 路徑特定的Filter鏈
    [urls]
    /assets/** 
    = anon
    /user/signup 
    = anon
    /user/** 
    = user
    /rpc/rest/** 
    = perms[rpc:invoke], authc
    /** 
    = authc
      http://www.iteye.com/topic/1044714?page=2

    posted on 2012-01-27 20:18 paulwong 閱讀(886) 評(píng)論(1)  編輯  收藏 所屬分類: Apache Shiro

    Feedback

    # re: 讓Apache Shiro保護(hù)你的應(yīng)用[未登錄] 2015-08-14 16:27 redcoatjk

    要不要寫的這么透徹. 強(qiáng)~  回復(fù)  更多評(píng)論   



    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产在线观看麻豆91精品免费| 2020国产精品亚洲综合网| 羞羞视频免费网站日本| 国产猛烈高潮尖叫视频免费| 亚洲国产一区二区三区在线观看| AA免费观看的1000部电影| 99久久婷婷国产综合亚洲| 色www永久免费视频| 久久久亚洲精华液精华液精华液| 免费无码又爽又刺激毛片| 亚洲国产成人精品无码区二本| 欧洲精品免费一区二区三区| jizzjizz亚洲日本少妇| 亚洲AV无码之日韩精品| 国产无遮挡又黄又爽免费网站| 久久精品国产亚洲沈樵| 曰批全过程免费视频播放网站| 亚洲娇小性xxxx| 四虎永久免费影院在线| 成人国产精品免费视频| 亚洲精品成人无码中文毛片不卡| 午夜免费福利片观看| 亚洲高清中文字幕免费| 国产免费观看网站| 三年在线观看免费观看完整版中文| 亚洲午夜在线电影| 好爽…又高潮了毛片免费看| 一本久久A久久免费精品不卡| 久久精品亚洲综合| 成全视频在线观看免费高清动漫视频下载| 337p日本欧洲亚洲大胆人人| 亚洲午夜无码久久久久| 免费成人福利视频| 一本久久A久久免费精品不卡| 亚洲一本综合久久| 国产在线19禁免费观看国产| 国产羞羞的视频在线观看免费| 亚洲人成网站看在线播放| 亚洲中文字幕无码日韩| 国产在线a免费观看| 边摸边脱吃奶边高潮视频免费|