国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
InfoQ: 讓Apache Shiro保護(hù)你的應(yīng)用

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

作者 Les Hazlewood 譯者 胡偉紅 發(fā)布于 2011年5月11日

領(lǐng)域
架構(gòu) & 設(shè)計(jì),
語(yǔ)言 & 開發(fā)
主題
安全 ,
Java ,
架構(gòu)

在嘗試保護(hù)你的應(yīng)用時(shí),你是否有過挫敗感?是否覺得現(xiàn)有的Java安全解決方案難以使用,只會(huì)讓你更糊涂?本文介紹的Apache Shiro,是一個(gè)不同尋常的Java安全框架,為保護(hù)應(yīng)用提供了簡(jiǎn)單而強(qiáng)大的方法。本文還解釋了Apache Shiro的項(xiàng)目目標(biāo)、架構(gòu)理念以及如何使用Shiro為應(yīng)用安全保駕護(hù)航。

什么是Apache Shiro?

Apache Shiro(發(fā)音為“shee-roh”,日語(yǔ)“堡壘(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í)還提供了一套控制頁(yè)面輸出的JSP標(biāo)簽庫(kù)。
  • 可插拔 - 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ù)。

誰(shuí)在用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)前用戶是誰(shuí)?”或“當(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ù)語(yǔ),其基本意思是“當(dāng)前的操作用戶”。稱之為“用戶”并不準(zhǔn)確,因?yàn)椤坝脩簟币辉~通常跟人相關(guān)。在安全領(lǐng)域,術(shù)語(yǔ)“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.HashedCredentialsMatchercm.hashAlgorithm = SHA-512cm.hashIterations = 1024# Base64 encoding (less text):cm.storedCredentialsHexEncoded = falseiniRealm.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)”語(yǔ)法進(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ù)庫(kù)(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.JndiLdapRealmldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=comldapRealm.contextFactory.url = ldap://ldapHost:389ldapRealm.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頁(yè)面等等。多數(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)限語(yǔ)法。參見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ù)庫(kù)、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í)在太大了。現(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

如你所見,Web應(yīng)用可以使用[urls] INI段落。對(duì)于每一行,等號(hào)左邊的值表示相對(duì)上下文的Web應(yīng)用路徑。等號(hào)右邊的值定義了過濾器鏈 - 一個(gè)逗號(hào)分隔的有序Servlet過濾器列表,它會(huì)針對(duì)給出的路徑進(jìn)行執(zhí)行。每個(gè)過濾器都是普通的Servlet過濾器,你看到的上面的過濾器名字(anon,user,perms,authc)是Shiro內(nèi)置的安全相關(guān)的特殊過濾器。你可以搭配這些安全過濾器來創(chuàng)建高度定制的安全體驗(yàn)。你還可以指定任何其他現(xiàn)有的Servlet過濾器。

相比起使用web.xml,在其中先定義過濾器塊,然后定義單獨(dú)分離的過濾器模式塊,這種方式帶來的好處有多少?采用Shiro的方法,可以很容易就準(zhǔn)確知道針對(duì)給定匹配路徑執(zhí)行的過濾器鏈。如果想這么做,你可以在web.xml中僅定義Shiro Filter,在shiro.ini中定義所有其他的過濾器和過濾器鏈,這要比web.xml簡(jiǎn)潔得多,而且更容易理解過濾器鏈定義機(jī)制。即使不使用Shiro的任何安全特性,單憑這樣小小的方便之處,也值得讓你使用Shiro。

JSP標(biāo)簽庫(kù)

Shiro還提供了JSP標(biāo)簽庫(kù),允許你根據(jù)當(dāng)前Subject的狀態(tài)控制JSP頁(yè)面的輸出。一個(gè)有用的常見示例是在用戶登錄后顯示“Hello <username>"文本。但若是匿名用戶,你可能想要顯示其他內(nèi)容,如換而顯示“Hello! Register Today!”。清單16顯示了如何使用Shiro的JSP標(biāo)簽實(shí)現(xiàn)這個(gè)示例:

清單16. JSP 標(biāo)簽庫(kù)示例

<%@ taglib prefix="shiro"    uri="http://shiro.apache.org/tags" %>...<p>Hello<shiro:user>     <!-- shiro:principal打印出了Subject的主當(dāng)事人 - 在這個(gè)示例中,就是用戶名: -->     <shiro:principal/>!</shiro:user><shiro:guest>     <!-- 沒有登錄 - 就認(rèn)為是Guest。顯示注冊(cè)鏈接: -->     ! <a href=”register.jsp”>Register today!</a></shiro:guest></p> 

除了上面例子用到的標(biāo)簽,還有其他標(biāo)簽可以讓你根據(jù)用戶屬于(或不屬于)的角色,分配(或未分配)的權(quán)限,是否已認(rèn)證,是否來自“記住我”服務(wù)的記憶,或是匿名訪客,包含輸出。

Shiro還支持其他許多Web特性,如簡(jiǎn)單的“記住我”服務(wù),REST和BASIC認(rèn)證。當(dāng)然,如果想使用Shiro原生的企業(yè)會(huì)話,它還提供透明的HttpSession支持。參見Apache Shiro Web文檔可以了解更多內(nèi)容。

Web會(huì)話管理

最后值得一提的是Shiro在Web環(huán)境中對(duì)會(huì)話的支持。

缺省Http會(huì)話

對(duì)于Web應(yīng)用,Shiro缺省將使用我們習(xí)以為常的Servlet容器會(huì)話作為其會(huì)話基礎(chǔ)設(shè)施。即,當(dāng)你調(diào)用subject.getSession()和subject.getSession(boolean)方法時(shí),Shiro會(huì)返回Servlet容器的HttpSession實(shí)例支持的Session實(shí)例。這種方式的曼妙之處在于調(diào)用subject.getSession()的業(yè)務(wù)層代碼會(huì)跟一個(gè)Shiro Session實(shí)例交互 - 還沒有“認(rèn)識(shí)”到它正跟一個(gè)基于Web的HttpSession打交道。這在維護(hù)架構(gòu)層之間的清晰隔離時(shí),是一件非常好的事情。

Web層中Shiro的原生會(huì)話

如果你由于需要Shiro的企業(yè)級(jí)會(huì)話特性(如容器無關(guān)的集群)而打開了Shiro的原生會(huì)話管理,你當(dāng)然希望HttpServletRequest.getSession()和HttpSession API能和“原生”會(huì)話協(xié)作,而非Servlet容器會(huì)話。如果你不得不重構(gòu)所有使用HttpServletRequest和HttpSession API的代碼,使用Shiro的Session API來替換,這將非常令人沮喪。Shiro當(dāng)然從來不會(huì)期望你這么做。相反,Shiro完整實(shí)現(xiàn)了Servlet規(guī)范中的Session部分以在Web應(yīng)用中支持原生會(huì)話。這意味著,不管何時(shí)你使用相應(yīng)的HttpServletRequest或HttpSession方法調(diào)用,Shiro都會(huì)將這些調(diào)用委托給內(nèi)部的原生會(huì)話API。結(jié)果,你無需修改Web代碼,即便是你正在使用Shiro的‘原生’企業(yè)會(huì)話管理 - 確實(shí)是一個(gè)非常方便(且必要)的特性。

附加特性

Apache Shiro框架還包含有對(duì)保護(hù)Java應(yīng)用非常有用的其他特性,如:

  • 為維持跨線程的Suject提供了線程和并發(fā)支持(支持Executor和ExecutorService);
  • 為了將執(zhí)行邏輯作為一種特殊的Subject,支持Callable和Runnable接口;
  • 為了表現(xiàn)為另一個(gè)Subject的身份,支持“Run As”(比如,在管理應(yīng)用中有用);
  • 支持測(cè)試工具,這樣可以很容易的對(duì)Shiro的安全代碼進(jìn)行單元測(cè)試和集成測(cè)試。

框架局限

常識(shí)告訴我們,Apache Shiro不是“銀彈” - 它不能毫不費(fèi)力的解決所有安全問題。如下是Shiro還未解決,但是值得知道的:

  • 虛擬機(jī)級(jí)別的問題:Apache Shiro當(dāng)前還未處理虛擬機(jī)級(jí)別的安全,比如基于訪問控制策略,阻止類加載器中裝入某個(gè)類。然而,Shiro集成現(xiàn)有的JVM安全操作并非白日做夢(mèng) - 只是沒人給項(xiàng)目貢獻(xiàn)這方面的工作。
  • 多階段認(rèn)證:目前,Shiro不支持“多階段”認(rèn)證,即用戶可能通過一種機(jī)制登錄,當(dāng)被要求再次登錄時(shí),使用另一種機(jī)制登錄。這在基于Shiro的應(yīng)用中已經(jīng)實(shí)現(xiàn),但是通過應(yīng)用預(yù)先收集所有必需信息再跟Shiro交互。這個(gè)功能在Shiro的未來版本中非常有可能得到支持。
  • Realm寫操作:目前所有Realm實(shí)現(xiàn)都支持“讀”操作來獲取驗(yàn)證和授權(quán)數(shù)據(jù)以執(zhí)行登錄和訪問控制。諸如創(chuàng)建用戶帳戶、組和角色或與用戶相關(guān)的角色組和權(quán)限這類“寫”操作還不支持。這是因?yàn)橹С诌@些操作的應(yīng)用數(shù)據(jù)模型變化太大,很難為所有的Shiro用戶強(qiáng)制定義“寫”API。

未來的特性

Apache Shiro社區(qū)每天都在壯大,借此,Shiro的特性亦是如此。在即將發(fā)布的版本中,你可能會(huì)看到:

  • 更干凈的Web過濾機(jī)制,無需子類化就可支持更多的插件式過濾器。
  • 更多可插拔的缺省Realm實(shí)現(xiàn),優(yōu)先采用組合而非繼承。你可以插入查找認(rèn)證和授權(quán)數(shù)據(jù)的組件,無需實(shí)現(xiàn)為Shiro Realm的子類;
  • 強(qiáng)健的OpenIDOAuth(可能是混合)客戶端支持;
  • 支持Captcha;
  • 針對(duì)純無狀態(tài)應(yīng)用的配置簡(jiǎn)化(如,許多REST環(huán)境);
  • 通過請(qǐng)求/響應(yīng)協(xié)議進(jìn)行多階段認(rèn)證;
  • 通過AuthorizationRequest進(jìn)行粗粒度的授權(quán);
  • 針對(duì)安全斷言查詢的ANTLR語(yǔ)法(比如,(‘role(admin) && (guest || !group(developer))’)

總結(jié)

Apache Shiro是一個(gè)功能齊全、健壯、通用的Java安全框架,你可以用其為你的應(yīng)用護(hù)航。通過簡(jiǎn)化應(yīng)用安全的四個(gè)領(lǐng)域,即認(rèn)證、授權(quán)、會(huì)話管理和加密,在真實(shí)應(yīng)用中,應(yīng)用安全能更容易被理解和實(shí)現(xiàn)。Shiro的簡(jiǎn)單架構(gòu)和兼容JavaBean使其幾乎能夠在任何環(huán)境下配置和使用。附加的Web支持和輔助功能,比如多線程和測(cè)試支持,讓這個(gè)框架為應(yīng)用安全提供了“一站式”服務(wù)。Apache Shiro開發(fā)團(tuán)隊(duì)將繼續(xù)前進(jìn),精煉代碼庫(kù)和支持社區(qū)。隨著持續(xù)被開源和商業(yè)應(yīng)用采納,可以預(yù)期Shiro會(huì)繼續(xù)發(fā)展壯大。

資源

關(guān)于作者

Les Hazlewood是Apache Shiro PMC主席以及Katasoft的CTO和合伙創(chuàng)辦人,該公司是專注于應(yīng)用安全產(chǎn)品以及提供Apache Shiro專業(yè)支持的創(chuàng)業(yè)公司。作為專業(yè)Java開發(fā)人員及企業(yè)架構(gòu)師,以及之前Bloomberg,Delta Airlines和JBoss的高級(jí)角色,Les擁有10年的的經(jīng)驗(yàn)。Les積極從事開源開發(fā)已有9年時(shí)間,提交或做出貢獻(xiàn)的項(xiàng)目有Spring框架、Hibernate、JBoss,OpenSpaces,當(dāng)然還有JSecurity,Apache Shiro的前身。Les目前居住在加州的圣馬特奧,不編程時(shí),他會(huì)練習(xí)劍道和學(xué)習(xí)日語(yǔ)。

查看英文原文: Application Security With Apache Shiro


本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
shiro安全框架
第二章:Shiro入門
Apache Shiro 驗(yàn)證
第四章 INI配置
第二章 身份驗(yàn)證
初識(shí)Shiro
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服