Apache Shiro是一個(gè)包含多種特性的綜合應(yīng)用安全框架。下圖展示了Shiro的主要特性,本參考手冊(cè)也會(huì)參照下面的特性來(lái)組織內(nèi)容:
Shiro的目標(biāo)(Shiro開(kāi)發(fā)團(tuán)隊(duì)稱(chēng)其為“四大應(yīng)用安全基石”)是認(rèn)證、授權(quán)、會(huì)話管理、加密:
如果你從未接觸過(guò)Apache Shiro,這篇簡(jiǎn)短的指南會(huì)向你展示如何使用Apache Shiro來(lái)設(shè)置一個(gè)初始和非常簡(jiǎn)單的安全web應(yīng)用程序. 我們將討論Shiro的核心概念來(lái)幫助您熟悉Shiro的設(shè)計(jì)和API。
在這個(gè)簡(jiǎn)單的例子中, 我們會(huì)創(chuàng)建一個(gè)非常簡(jiǎn)單的快速運(yùn)行和退出的命令行應(yīng)用程序, 這只是為了讓你對(duì) Shiro的API有所感覺(jué).
對(duì)于本指南,請(qǐng)確保你使用的是Maven 2.2.1+. 你可以在命令中輸入mvn --version查看類(lèi)似于下面的東西:
我們將運(yùn)行一個(gè)簡(jiǎn)單的命令行應(yīng)用程序,因此我們需要?jiǎng)?chuàng)建一個(gè)帶有public static void main(String[] args)方法的java類(lèi).
在包含pom.xml文件的同一個(gè)目錄中,創(chuàng)建一個(gè)src/main/java子目錄,并使用下面的內(nèi)容在其中創(chuàng)建一個(gè)Tutorial.java文件:
現(xiàn)在不用擔(dān)心import語(yǔ)句- 我們很快就會(huì)講解它們. 現(xiàn)在,我們已經(jīng)有了一個(gè)典型的命令行程序外殼. 程序會(huì)打印文件"My First Apache Shiro Application"并退出.
要測(cè)試我們的Tutorial應(yīng)用程序,切換到tutorial根目錄(即.shiro-tutorial),在命令行提示窗口中執(zhí)行下面的命令:
到此我們已經(jīng)驗(yàn)證了應(yīng)用程序能成功運(yùn)行 -現(xiàn)在讓我們啟用Apache Shiro. 在繼續(xù)本指南的過(guò)程中,每次修改代碼后,你都可以運(yùn)行mvn compile exec:java 來(lái)查看結(jié)果變化.
理解啟用Shiro的第一件事是幾乎Shiro中的每件事情都與核心組件SecurityManager有關(guān). 對(duì)于那些熟悉Java安全的人來(lái)說(shuō),這是Shiro的SecurityManager概念 -它不同于java.lang.SecurityManager.
因此,在Tutorial 程序中,我們要做的第一件事就是建立SecurityManager實(shí)例.
雖然我們可以直接實(shí)例化SecurityManager類(lèi), Shiro的SecurityManager 實(shí)現(xiàn)包含足夠的配置選項(xiàng)和內(nèi)部組件,使其可以從Java源代碼中進(jìn)行構(gòu)建- 但可以使用靈活的文本配置格式來(lái)更簡(jiǎn)單地配置SecurityManager.
對(duì)于這個(gè)簡(jiǎn)單應(yīng)用程序,因此我們使用INI文件來(lái)配置Shiro SecurityManager.首先,在與pom.xml相同的目錄中,創(chuàng)建一個(gè)
src/main/resources 目錄. 然后使用下面的命令,在新創(chuàng)建的目錄中創(chuàng)建一個(gè)shiro.ini文件:
src/main/resources/shiro.ini
# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
# =============================================================================
# -----------------------------------------------------------------------------
# Users and their (optional) assigned roles # username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users] root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
正如你看到的,這個(gè)配置基本建立一套小的靜態(tài)用戶(hù)帳戶(hù),對(duì)于我們的第一個(gè)程序來(lái)說(shuō),已經(jīng)足夠了.在后面的章節(jié)中,你會(huì)看到如何使用更復(fù)雜的用戶(hù)數(shù)據(jù)資源,如關(guān)系數(shù)據(jù)庫(kù),LDAP的ActiveDirectory等等.
引用配置
現(xiàn)在我們已經(jīng)定義了一個(gè)INI文件, 然后我們就可在Tutorial類(lèi)中創(chuàng)建SecurityManager實(shí)例了. 修改main方法,以反映下面的更新:
public static void main(String[] args) {
log.info("My First Apache Shiro Application");
//1.
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2.
SecurityManager securityManager = factory.getInstance();
//3.
SecurityUtils.setSecurityManager(securityManager);
System.exit(0);
}
在添加了3行代碼后,我們就在樣例程序中啟用了Shiro!是不是很簡(jiǎn)單?
運(yùn)行 mvn compile exec:java能看到一切都能成功運(yùn)行(因?yàn)镾hiro默認(rèn)為debug或更低級(jí)別,你不會(huì)看到任何Shiro日志消息-如果啟動(dòng)或運(yùn)行時(shí)不出現(xiàn)錯(cuò)誤,那么一切就工作得很好).
以下是上面增加代碼所做的事情:
- 我們使用Shiro的IniSecurityManagerFactory實(shí)現(xiàn)來(lái)獲取位于classpath根下的shiro.ini文件.此實(shí)現(xiàn)了反映了Shiro對(duì)工廠設(shè)計(jì)模式的支持. classpath: 前輟是一個(gè)資源標(biāo)識(shí)符,用于告訴shiro在什么位置加載ini文件(也支持其它前輟,像url: 和file: ).
- 然后調(diào)用了factory.getInstance()方法,它會(huì)解析INI文件,并返回一個(gè)反映配置的SecurityManager實(shí)例.
- 在這個(gè)簡(jiǎn)單示例中,我們將SecurityManager設(shè)為了一個(gè)可通過(guò)JVM來(lái)訪問(wèn)的static (memory) 單例.請(qǐng)注意,如果在單個(gè)虛擬機(jī)中存在多個(gè)啟用了Shiro的應(yīng)用程序,這是不理想的.對(duì)于這個(gè)簡(jiǎn)單示例,它是OK的, 但在更復(fù)雜的應(yīng)用程序環(huán)境中,通常應(yīng)該將SecurityManager放置在特定應(yīng)用的內(nèi)存中(如,放在web app的ServletContext中或Spring, Guice或 JBoss DI 容器實(shí)例中).
使用Shiro
現(xiàn)在我們建立好了SecurityManager,可以準(zhǔn)備使用了, 現(xiàn)在讓我們做一些我們真正關(guān)心的東西 - 執(zhí)行安全操作.
當(dāng)為我們的應(yīng)用程序提供安全時(shí),我們通常問(wèn)自己最相關(guān)的問(wèn)題是"當(dāng)前用戶(hù)是誰(shuí)?”或“是否允許當(dāng)前用戶(hù)做某些事情”? 這是經(jīng)常會(huì)問(wèn)的問(wèn)題,因此我們會(huì)編寫(xiě)代碼并設(shè)計(jì)我們的接口:
應(yīng)用程序通常會(huì)基于用戶(hù)存儲(chǔ)進(jìn)行構(gòu)建, 并且你希望每個(gè)用戶(hù)都是安全的. 因此,對(duì)于我們來(lái)說(shuō),最自然的方式是認(rèn)為應(yīng)用程序的安全是基于當(dāng)前用戶(hù)的. Shiro的 API從根本上使用了Subject概念來(lái)表達(dá)當(dāng)前用戶(hù).
在幾乎所有環(huán)境中,你可以通過(guò)下面的調(diào)用來(lái)獲取當(dāng)前執(zhí)行用戶(hù):
Subject currentUser = SecurityUtils.getSubject();
使用
SecurityUtils.
getSubject(), 我們能獲取當(dāng)前執(zhí)行的
Subject.
Subject是一種安全術(shù)語(yǔ),其基本意義為"當(dāng)前執(zhí)行用戶(hù)的特定安全視圖". 它不稱(chēng)為'User',因?yàn)?'User' 這個(gè)詞通常與人類(lèi)聯(lián)系在一起.
在安全世界中,術(shù)語(yǔ)'Subject'可以表示人類(lèi),但也可以表示第三過(guò)程, cron job, 后臺(tái)帳戶(hù),或任何相似的東西.它簡(jiǎn)單地表示為當(dāng)前與軟件交互的事物.
雖然這只是針對(duì)大多數(shù)的意圖和目的,你仍然可將Subject認(rèn)為是Shiro的'User'.
在一個(gè)獨(dú)立的應(yīng)用程序中,getSubject()方法會(huì)返回在應(yīng)用特定位置用戶(hù)數(shù)據(jù)的基礎(chǔ)上返回一個(gè)Subject,在服務(wù)器環(huán)境中(如. web app),它會(huì)基于當(dāng)前線程或傳入請(qǐng)求相關(guān)的用戶(hù)數(shù)據(jù)中來(lái)獲取Subject.
現(xiàn)在你有了Subject,你能用它來(lái)做什么呢?
如果你想在應(yīng)用程序其會(huì)話期間對(duì)當(dāng)前用戶(hù)做些什么,你可以獲取它們的會(huì)話:
Session session = currentUser.getSession(); session.setAttribute( "someKey", "aValue" );
Session是Shiro特有的實(shí)例,它提供了類(lèi)似常規(guī)HttpSessions大部分的功能,但也存在額外的東西以及一個(gè)重大區(qū)別:它不需要HTTP環(huán)境!
如果部署在web應(yīng)用程序中,默認(rèn)情況下,Session是基于HttpSession的. 但在非web環(huán)境中,就像這個(gè)簡(jiǎn)單的tutorial程序, Shiro會(huì)默認(rèn)自動(dòng)使用其企業(yè)級(jí)會(huì)話管理.
這意味著,無(wú)論是哪種部署環(huán)境,無(wú)論在哪一層,在你的應(yīng)用程序中都可以使用同一套API. 這打開(kāi)了應(yīng)用程序的新世界,因?yàn)槿魏螒?yīng)用程序都可以不強(qiáng)制使用 HttpSession或EJB有狀態(tài)會(huì)話bean的前提下獲取Session. 除此之外,任何client技術(shù)現(xiàn)在都可以共享會(huì)話數(shù)據(jù).
所以現(xiàn)在你可以獲取Subject及其會(huì)話了.像檢查它們是否允許做某些事情以及檢查其角色與權(quán)限這樣真正有用的事情又怎樣呢?
我們只能為一個(gè)已知的用戶(hù)做這些檢查。上面的Subject實(shí)例只能代表當(dāng)前用戶(hù),但當(dāng)前用戶(hù)是誰(shuí)呢? 這里,它們是匿名的- 也就是說(shuō),除非它們至少登錄了一次,才知道是已知的. 因此, 讓我們按下面這樣做:
if ( !currentUser.isAuthenticated() ) {
//collect user principals and credentials in a gui specific manner
//such as username/password html form, X509 certificate, OpenID, etc.
//We'll use the username/password example here since it is the most common.
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//this is all you have to do to support 'remember me' (no config - built in!):
token.setRememberMe(true);
currentUser.login(token);
}
這就是它了!不能再容易了.
但如果登錄失敗了呢? 你可以捕獲各種各樣的異常,這些異常將明確地告訴你發(fā)生了什么,并允許你進(jìn)行相應(yīng)地處理:
try {
currentUser.login( token );
//if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
//username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {
//password didn't match, try again? }
catch ( LockedAccountException lae ) {
//account for that username is locked - can't login. Show them a message?
}
... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
//unexpected condition - error?
}
溫馨提示
最佳安全實(shí)踐是向用戶(hù)提示一般的登錄失敗消息,因?yàn)槟悴幌霂椭粽咴噲D闖入你的系統(tǒng).
Ok, 到現(xiàn)在為止,我們有了一個(gè)登錄用戶(hù).還有什么事情可以做呢?
讓我們看看它是誰(shuí):
//print their identifying principal (in this case, a username):
log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." );
我們也可以測(cè)試它是否有指定的角色:
if ( currentUser.hasRole( "schwartz" ) ) {
log.info("May the Schwartz be with you!" );
} else {
log.info( "Hello, mere mortal." );
}
我們還可以測(cè)試它是否有某種類(lèi)型實(shí)體上的權(quán)限:
if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
此外,我們可以執(zhí)行一個(gè)非常強(qiáng)大的實(shí)例級(jí)權(quán)限檢查 - 查看用戶(hù)是否有能力訪問(wèn)一個(gè)特定類(lèi)型的實(shí)例:
if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " + "Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
非常簡(jiǎn)單,對(duì)嗎?
最后,當(dāng)用戶(hù)在程序程序中完成了使用,它們可以登出:
currentUser.logout(); //removes all identifying information and invalidates their session too.
最終Tutorial類(lèi)
在添加了上述代碼后,以下是最終的Tutorial類(lèi)文件.只要你喜歡,你可以隨意編輯,并修改安全檢查(和INI配置)來(lái)玩它:
Final src/main/java/Tutorial.java
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Tutorial {
private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
public static void main(String[] args) {
log.info("My First Apache Shiro Application");
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// get the currently executing user:
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
// let's login the current user so we can check against roles and permissions:
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal()); }
catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:weild")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//all done - log out!
currentUser.logout();
System.exit(0);
}
}
總結(jié)
希望這本入門(mén)教程幫助你了解如何在一個(gè)基本應(yīng)用中設(shè)置Shiro,以及Shiro的主要設(shè)計(jì)理念 Subject 和SecurityManager。
但這是一個(gè)相當(dāng)簡(jiǎn)單的應(yīng)用。你可能會(huì)問(wèn)自己,“如果我不想使用INI用戶(hù)帳戶(hù)而是要連接到一個(gè)更復(fù)雜的用戶(hù)數(shù)據(jù)源,又該怎么做呢?”
要回答這個(gè)問(wèn)題,需要深入一點(diǎn)了解Shiro的架構(gòu)和支持的配置機(jī)制。我們將下一個(gè)Shiro的
Architecture 進(jìn)行講解.
Apache Shiro 架構(gòu)
Apache Shiro的設(shè)計(jì)目標(biāo)是通過(guò)直觀而簡(jiǎn)單的方式來(lái)簡(jiǎn)化應(yīng)用程序安全的開(kāi)發(fā). Shiro的核心設(shè)計(jì)模型-在一個(gè)上下文里與應(yīng)用程序進(jìn)行交互.
軟件應(yīng)用程序程序通常是基于用戶(hù)存儲(chǔ)來(lái)設(shè)計(jì)的. 也就是說(shuō),你會(huì)經(jīng)常設(shè)計(jì)用戶(hù)如何與軟件進(jìn)行交互的接口或服務(wù).
例如,你可能會(huì)說(shuō), "如果用戶(hù)與應(yīng)用程序交互時(shí)已經(jīng)登錄了,我應(yīng)該向其展示一個(gè)按鈕,它們可以點(diǎn)擊來(lái)查看它們的帳戶(hù)信息.如果它們沒(méi)有登錄,我將向它們展示一個(gè)登錄按鈕."
此示例語(yǔ)句暗示了應(yīng)用程序在很大程度上是為了滿(mǎn)足用戶(hù)的需求而編寫(xiě)的。即使'用戶(hù)'是另一個(gè)軟件系統(tǒng),而不是一個(gè)人,你仍然要編寫(xiě)代碼來(lái)反映誰(shuí)(或什么)目前正在與您的軟件交互。
高層次概述
在最高概念層面,Shiro的架構(gòu)包含了3個(gè)主要概念:Subject, SecurityManager 和 Realms. 下面的圖表是這些組件如何交互的高級(jí)概述,我們將覆蓋下面的每一個(gè)概念:

Subject:
正如我們?cè)?nbsp;
Tutorial中提到的, Subject本質(zhì)上當(dāng)前執(zhí)行用戶(hù)的特定安全視圖.
"User”往往意味著一個(gè)人類(lèi),一個(gè)Subject可以是一個(gè)人,但它也可能代表一個(gè)第三方服務(wù),后臺(tái)賬戶(hù),cron作業(yè),或任何類(lèi)似的東西-基本上是當(dāng)前與軟件交互的任何東西.
Subject 實(shí)例都綁定在SecurityManager上. 當(dāng)你與Subject交互時(shí),這些交互都會(huì)轉(zhuǎn)換成特定subject與SecurityManager的交互.
SecurityManager:
SecurityManager 是Shiro架構(gòu)的心臟,它扮演了一種保護(hù)傘(umbrella)對(duì)象,協(xié)調(diào)其內(nèi)部的安全組件共同形成一個(gè)對(duì)象圖.
然而,一旦應(yīng)用程序配置了SecurityManager及其內(nèi)部對(duì)象圖,它就會(huì)退居幕后,應(yīng)用開(kāi)發(fā)人員幾乎把他們的所有時(shí)間都花在Subject API調(diào)用上。
后面我們會(huì)詳細(xì)討論SecurityManager, 但要意識(shí)到,當(dāng)你與Subject交互時(shí),其實(shí)是幕后的SecurityManager在執(zhí)行Subject的安全操作.這可以從上面的圖中得到反映.
Realms:
Realms扮演的是Shiro與應(yīng)用程序安全數(shù)據(jù)的橋梁或連接器.
當(dāng)它與像用戶(hù)帳戶(hù)這樣的相關(guān)安全數(shù)據(jù)進(jìn)行實(shí)際交互時(shí),如執(zhí)行認(rèn)證(login) 和授權(quán)(訪問(wèn)控制), Shiro會(huì)查看為應(yīng)用程序中配置的一個(gè)或多個(gè)Realms.
從這種意義上說(shuō),Realm本質(zhì)上是一個(gè)帶有安全特征的數(shù)據(jù)訪問(wèn)對(duì)象(
DAO): 它封裝了數(shù)據(jù)源的連接細(xì)節(jié),并按需將相關(guān)數(shù)據(jù)提供給Shiro.
當(dāng)配置Shiro時(shí),你必須至少指定一個(gè)Realm來(lái)用于驗(yàn)證或授權(quán). SecurityManager可配置多個(gè)Realms, 但至少要配置一個(gè).
Shiro提供了多個(gè)開(kāi)箱即用的Realms來(lái)連接多種安全數(shù)據(jù)源,如LDAP, 關(guān)系型數(shù)據(jù)庫(kù)(JDBC),像INI和屬性文件這樣的文本配置源.
如果默認(rèn)的Realms不能滿(mǎn)足你的需要,你可以插入你自己的Realm實(shí)現(xiàn)來(lái)表示自定義數(shù)據(jù)源.
像其它內(nèi)部組件一樣, Shiro SecurityManager會(huì)對(duì)如何使用Realms來(lái)獲取安全身份數(shù)據(jù)(表示為Subject實(shí)例)進(jìn)行管理.
詳細(xì)架構(gòu)
下圖展示了Shiro的核心架構(gòu)概念以及每種概念的簡(jiǎn)短概述:

與軟件進(jìn)行交互的實(shí)體(用戶(hù),第三方服務(wù),cron作業(yè)等等)特定安全視圖.
正如上面提到的, SecurityManager是Shiro架構(gòu)的心臟.它主要充當(dāng)保護(hù)傘對(duì)象,協(xié)調(diào)其管理組件來(lái)確保工作順利進(jìn)行. 同時(shí),它也會(huì)管理每個(gè)應(yīng)用程序用戶(hù)的Shiro視圖,因此它知道如何執(zhí)行每個(gè)用戶(hù)上的操作.
認(rèn)證負(fù)責(zé)驗(yàn)證用戶(hù)的登錄. 當(dāng)一個(gè)用戶(hù)試圖登錄時(shí),認(rèn)證將執(zhí)行驗(yàn)證邏輯. 認(rèn)證組件知道如何協(xié)調(diào)一個(gè)或多個(gè)存儲(chǔ)相關(guān)用戶(hù)信息的. 從Realms中獲取的數(shù)據(jù)用于驗(yàn)證用戶(hù)的身份,以確保其合法性.
如果配置了多個(gè)Realm, AuthenticationStrategy 會(huì)協(xié)調(diào)Realms來(lái)確定在哪些情況下認(rèn)證是成功或是失敗的(例如,如果某個(gè)realm是成功的,但其它是失敗的l,那么認(rèn)證是成功的嗎?
是否需要所有realms都成功?還是只需要第一個(gè)成功?).
授權(quán)是在應(yīng)用程序中負(fù)責(zé)確定訪問(wèn)控制的組件.最終說(shuō)來(lái)它只是允許用戶(hù)做什么或不做什么的機(jī)制.類(lèi)似于Authenticator,Authorizer也知道如何協(xié)調(diào)多個(gè)后端數(shù)據(jù)源來(lái)訪問(wèn)角色和權(quán)限信息.
Authorizer可使用這些信息來(lái)決定一個(gè)用戶(hù)是否允許來(lái)執(zhí)行給定的操作.
SessionManager知道如何來(lái)創(chuàng)建和管理用戶(hù)生命周期,以為所有環(huán)境中的用戶(hù)提供強(qiáng)大的會(huì)話體驗(yàn). 在安全框架的世界中,這是獨(dú)特的特性- Shiro有管理任何環(huán)境中用戶(hù)會(huì)話的能力, 即使這些環(huán)境處于非Web/Servlet或在EJB容器環(huán)境.
默認(rèn)情況下,如果可行的話,Shiro會(huì)使用現(xiàn)有的會(huì)話機(jī)制, (例如. Servlet容器),但如果不存在這樣的環(huán)境,如獨(dú)立程序或非web環(huán)境,它會(huì)使用內(nèi)置的企業(yè)會(huì)話管理來(lái)提供相同的編程體驗(yàn). SessionDAO的存在允許使用任何數(shù)據(jù)源來(lái)存儲(chǔ)會(huì)話.
SessionDAO可代表SessionManager來(lái)執(zhí)行會(huì)話持久化操作(CRUD).這允許在會(huì)話管理框架中插入任何數(shù)據(jù)存儲(chǔ).
緩存管理器用于創(chuàng)建和管理其它Shiro組件使用的緩存實(shí)例生命周期.因?yàn)镾hiro在執(zhí)行認(rèn)證,授權(quán)和會(huì)話管理時(shí),可訪問(wèn)多個(gè)后端數(shù)據(jù)源,當(dāng)使用這些數(shù)據(jù)源時(shí),緩存通常是框架中提高性能的最好架構(gòu)特征.
任何一個(gè)現(xiàn)代開(kāi)源或企業(yè)級(jí)緩存產(chǎn)品都可以插入到Shiro中,以提供快速,高效的用戶(hù)體驗(yàn).
加密是企業(yè)級(jí)安全框架中一個(gè)很自然的功能. Shiro的加密包包含了易于使用和理解的加密 Ciphers, Hashes (aka digests)表現(xiàn)以及不同的編碼實(shí)現(xiàn).
包中的所有類(lèi)都是精心設(shè)計(jì)為易于使用和理解. 任何使用過(guò)Java本地加密的人,都可以輕易地駕馭它.
Shiro的加密APIs簡(jiǎn)化了復(fù)雜的Java機(jī)制,使得加密對(duì)于普通人來(lái)說(shuō)也能簡(jiǎn)單使用.
正如上面所提到的, Realms扮演的是Shiro與應(yīng)用程序安全數(shù)據(jù)之間的橋梁或連接器.
當(dāng)它與像用戶(hù)帳戶(hù)這樣的相關(guān)安全數(shù)據(jù)進(jìn)行實(shí)際交互時(shí),如執(zhí)行認(rèn)證(login) 和授權(quán)(訪問(wèn)控制), Shiro會(huì)查看為應(yīng)用程序中配置的一個(gè)或多個(gè)Realms.
你可以根據(jù)需要來(lái)配置多個(gè)Realms (通常情況下一個(gè)數(shù)據(jù)源一個(gè)Realms) ,Shiro會(huì)按照需要來(lái)為認(rèn)證和授權(quán)協(xié)調(diào)數(shù)據(jù).
SecurityManager
因?yàn)镾hiro的API鼓勵(lì)使用以Subject為中心的編程模型,大部分應(yīng)用程序開(kāi)發(fā)者會(huì)極少直接與SecurityManager交互(框架開(kāi)發(fā)者有時(shí)可能會(huì)發(fā)現(xiàn)它很有用).
即使是這樣,了解SecurityManager的功能,特別是在為應(yīng)用配置時(shí), 也是極其重要的.
設(shè)計(jì)
如前所述,應(yīng)用程序安全管理器會(huì)為所有應(yīng)用程序用戶(hù)執(zhí)行安全操作和管理用戶(hù)狀態(tài). 在Shiro的默認(rèn)SecurityManager實(shí)現(xiàn)中包含:
- Authentication(認(rèn)證)
- Authorization(授權(quán))
- Session Management(會(huì)話管理)
- Cache Management(緩存管理)
- Realm coordination(Realm協(xié)作)
- Event propagation(事件傳播)
- "Remember Me" Services(記住我服務(wù))
- Subject creation(Subject創(chuàng)建)
- Logout(登出)
等等.
但在單個(gè)組件中,管理著多個(gè)功能. 而且,如果一切功能都集中到單一的實(shí)現(xiàn)類(lèi)中,會(huì)使這些操作變得靈活和可定制化是相當(dāng)困難的.
為了簡(jiǎn)化配置和開(kāi)啟靈活的配置性/可插拔性, Shiro的實(shí)現(xiàn)設(shè)計(jì)上是高度模塊化的- SecurityManager實(shí)現(xiàn)及其類(lèi)層次并沒(méi)有做這么多工作.
相反,SecurityManager實(shí)現(xiàn)幾乎充當(dāng)?shù)氖禽p量級(jí)容器組件, 幾乎把所有行為/操作都委派給了內(nèi)嵌/包裝組件.上面的詳細(xì)框架圖就反映了這種包裝設(shè)計(jì).
雖然是由組件來(lái)實(shí)際執(zhí)行邏輯,SecurityManager實(shí)現(xiàn)知道如何以及何時(shí)來(lái)協(xié)調(diào)組件來(lái)完成正確行為.
SecurityManager 實(shí)現(xiàn)同時(shí)也是JavaBeans兼容的,這允許你通過(guò)標(biāo)準(zhǔn)的Java訪問(wèn)器/修改器方法(get*/set*)來(lái)輕松地定制可插拔組件.
這意味著Shiro的架構(gòu)模塊可以很容易配置自定義行為。
簡(jiǎn)單配置
由于JavaBeans的兼容性, 使其很容易地通過(guò)支持JavaBeans的自定義組件來(lái)配置SecurityManager,如Spring, Guice, JBoss等等. Apache Shiro 配置
Shiro設(shè)計(jì)為可工作于任何環(huán)境, 從簡(jiǎn)單的命令行應(yīng)用程序到大型企業(yè)集群應(yīng)用程序. 由于環(huán)境的多樣性,存在著多種配置機(jī)制. 本章講述Shiro核心支持的配置機(jī)制.
多個(gè)配置選項(xiàng)
Shiro的SecurityManager實(shí)現(xiàn)以及所有支持組件都是與JavaBeans兼容的.這允許Shiro使用特定配置格式,如常規(guī)Java, XML (Spring, JBoss, Guice, etc), YAML, JSON, Groovy Builder標(biāo)記來(lái)配置. 編程配置
創(chuàng)建SecurityManager并使用可用的最簡(jiǎn)單方式是創(chuàng)建一個(gè)org.apache.shiro.mgt.DefaultSecurityManager. 例如:
Realm realm = //實(shí)例化或獲取一個(gè)Realm實(shí)例.我們后面會(huì)討論Realms. SecurityManager securityManager = new DefaultSecurityManager(realm); //通過(guò)靜態(tài)內(nèi)存,使SecurityManager 實(shí)例對(duì)整個(gè)應(yīng)用程序可用: SecurityUtils.setSecurityManager(securityManager);
令人驚訝的是,雖只有3行代碼,但你現(xiàn)在有了一個(gè)適合于許多應(yīng)用且功能齊全的Shiro環(huán)境。多么容易!
SecurityManager 對(duì)象圖
正如在
Architecture章節(jié)中討論的, Shiro的SecurityManager實(shí)現(xiàn)本質(zhì)上內(nèi)嵌特定安全的組件模塊對(duì)象圖.
因?yàn)樗鼈円彩荍avaBeans兼容的,你可調(diào)用任何內(nèi)嵌組件上的 getter和setter方法來(lái)配置SecurityManager和它的內(nèi)部對(duì)象圖.
例如,如果你想配置SecurityManager實(shí)例來(lái)使用自定義的SessionDAO來(lái)定制
Session Management,你可以直接使用內(nèi)置的SessionManager的setSessionDAO來(lái)設(shè)置SessionDAO:
...
DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);
SessionDAO sessionDAO = new CustomSessionDAO();
((DefaultSessionManager)securityManager.getSessionManager()).setSessionDAO(sessionDAO); ...
使用直接方法調(diào)用,你可以配置SecurityManager的對(duì)象圖的任何部分.
雖像程序化定制一樣簡(jiǎn)單,但它并不代表大多數(shù)現(xiàn)實(shí)世界應(yīng)用程序的理想配置。
有以下幾個(gè)原因?yàn)槭裁淳幊膛渲每赡懿贿m合您的應(yīng)用程序:
- 它需要你了解和實(shí)例化一個(gè)直接實(shí)現(xiàn).如果不需要了解具體和查找位置,這是很好的.
- 由于Java的類(lèi)型安全特性,你需要在get*方法中對(duì)特定實(shí)現(xiàn)進(jìn)行類(lèi)型轉(zhuǎn)換. 因此太多的轉(zhuǎn)換是丑陋的,冗長(zhǎng)的,并緊密耦合了你的實(shí)現(xiàn)類(lèi).
- SecurityUtils.setSecurityManager 方法調(diào)用使得實(shí)例化了的SecurityManager實(shí)例化了一個(gè) VM 靜態(tài)單例, 在這里是很好的,但對(duì)于多數(shù)應(yīng)用程序來(lái)說(shuō),如果在同一個(gè)JVM中運(yùn)行了多個(gè)開(kāi)啟了Shiro的應(yīng)用程序,將會(huì)引發(fā)問(wèn)題.本身來(lái)說(shuō),如果實(shí)例是應(yīng)用程序級(jí)單例的,這會(huì)更好,但不是靜態(tài)內(nèi)存引用.
- 為了讓Shiro配置生效,每次你都需要重新編譯代碼.
但,即使有這些警告,直接編程操作方案對(duì)于內(nèi)存受限的環(huán)境,仍然是價(jià)值的,像智能手機(jī)應(yīng)用程序.如果你的應(yīng)用程序不會(huì)運(yùn)行內(nèi)存受限環(huán)境中,你會(huì)發(fā)現(xiàn)基于文本的配置更加簡(jiǎn)單和閱讀.
INI 配置
大部分應(yīng)用程序都受益于基于文本的配置,這樣可以獨(dú)立于源碼進(jìn)行修改,甚至使得那于不熟悉Shiro API的人來(lái)說(shuō),也更易于理解.
為了確保通用基于文本配置的解決方案能工作于任何環(huán)境(有很小的依賴(lài)),Shiro支持使用
INI format 來(lái)構(gòu)建SecurityManager 對(duì)象圖和它的支撐組件.
INI易于閱讀和配置,并可以簡(jiǎn)單建立而適用于大部分應(yīng)用程序.
從INI創(chuàng)建SecurityManager
下面有兩個(gè)例子來(lái)講述如何基于INI配置來(lái)構(gòu)建SecurityManager.
從INI資源來(lái)構(gòu)建SecurityManager
我們可以從INI資源路徑來(lái)創(chuàng)建SecurityManager實(shí)例. 資源可通過(guò)文件系統(tǒng), classpath,或URLs來(lái)獲取,此時(shí)其前輟分別為file:, classpath:, 或 url: .
這個(gè)例子使用Factory來(lái)從classpath根路徑下來(lái)攝取shiro.ini文件,并返回SecurityManager實(shí)例:
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.config.IniSecurityManagerFactory;
...
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
從INI實(shí)例來(lái)構(gòu)建SecurityManager
For example:
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.IniSecurityManagerFactory;
...
Ini ini = new Ini(); //按需填充 Ini實(shí)例
...
Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
現(xiàn)在,我們知道如何從INI配置來(lái)構(gòu)建一個(gè)SecurityManager, 讓我們明確地了解如何定義一個(gè)Shiro INI配置.
INI 分段
INI是一個(gè)包含由唯一分段組織的key/value對(duì)的文本配置. 每個(gè)段中Keys是唯一的,而不是整個(gè)配置(這一點(diǎn)不像JDK Properties). 每個(gè)段都可像單個(gè)Properties定義進(jìn)行查看.
注釋行可以#或;開(kāi)頭
下面是分段的例子:
# ======================= # Shiro INI configuration # =======================
[main]
# 這里用于定義對(duì)象及其屬性,如securityManager, Realms 以及任何需要用于構(gòu)建SecurityManager的配置
[users]
# 'users' 分段用于簡(jiǎn)單部署
# 當(dāng)你只需要少量靜態(tài)定義的用戶(hù)帳戶(hù)時(shí)
[roles]
# 'roles' 分段用于簡(jiǎn)單部署
# 當(dāng)你只需要少量靜態(tài)定義的角色時(shí)
[urls] # 'urls' 分段用于web應(yīng)用程序中作基于url的基礎(chǔ)安全控制
# 我們會(huì)Web文檔中討論這個(gè)分段
[main]
[main]分段是你配置應(yīng)用程序SecurityManager實(shí)例,及其任何依賴(lài),如
Realms.
像SecurityManager或其依賴(lài)這樣的配置對(duì)象實(shí)例聽(tīng)起來(lái)對(duì)于INI來(lái)說(shuō),是一件困難的事情,因?yàn)樵谶@里我們只能使用name/value對(duì).
但是通過(guò)一點(diǎn)點(diǎn)的約定和對(duì)對(duì)象圖的理解,你會(huì)發(fā)現(xiàn)你可以做得很好。Shiro用這些假設(shè)使一個(gè)簡(jiǎn)單的配置機(jī)制成為了可能。
我們常喜歡把這種方法稱(chēng)為“窮人的“依賴(lài)注入,雖然不比全面的Spring/ Guice / JBoss的XML文件強(qiáng)大,但你會(huì)發(fā)現(xiàn)它沒(méi)有過(guò)多的復(fù)雜性。
當(dāng)然也可使用其他的配置機(jī)制,但對(duì)于Shiro來(lái)說(shuō),他們不是必須的.
只是為了刺激你的欲望,這里有一個(gè)有效的[main]配置實(shí)例:
[main]
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
myRealm = com.company.security.shiro.DatabaseRealm
myRealm.connectionTimeout = 30000
myRealm.username = jsmith
myRealm.password = secret
myRealm.credentialsMatcher = $sha256Matcher
securityManager.sessionManager.globalSessionTimeout = 1800000
定義對(duì)象
細(xì)想下面的[main] 分段片斷:
[main] myRealm = com.company.shiro.realm.MyRealm ...
這行實(shí)例化了一個(gè)com.company.shiro.realm.MyRealm類(lèi)型的新對(duì)象,并為了以后的引用和配置,使其在名稱(chēng)myRealm名稱(chēng)下可用.
如果實(shí)例的對(duì)象實(shí)現(xiàn)了org.apache.shiro.util.Nameable接口, 那么可在對(duì)象上使用名稱(chēng)值(myRealm)來(lái)調(diào)用Nameable.setName.
設(shè)置對(duì)象屬性
原始類(lèi)型值
簡(jiǎn)單原始類(lèi)型屬性可以使用=號(hào)來(lái)賦值:
...
myRealm.connectionTimeout = 30000
myRealm.username = jsmith ...
這些行的配置轉(zhuǎn)換成方法調(diào)用為:
...
myRealm.setConnectionTimeout(30000);
myRealm.setUsername("jsmith"); ...
這怎么可能呢?它假定所有的對(duì)象都是java bean兼容的POJOs。
在后臺(tái),Shiro默認(rèn)使用Apache Commons BeanUtils來(lái)設(shè)置這些屬性。所以盡管INI值是文本,BeanUtils知道如何將字符串值的原始類(lèi)型,然后調(diào)用相應(yīng)的JavaBean的setter方法。
引用值
如果需要設(shè)置的值不是原始類(lèi)型而是其它對(duì)象呢? 你可以使用$來(lái)引用先前定義的實(shí)例.例如:
...
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
...
myRealm.credentialsMatcher = $sha256Matcher ...
這會(huì)簡(jiǎn)單地定位名為sha256Matcher 的對(duì)象,然后使用 BeanUtils 在 myRealm 實(shí)例上來(lái)設(shè)置這個(gè)對(duì)象(通過(guò)調(diào)用myRealm.setCredentialsMatcher(sha256Matcher)方法).
內(nèi)嵌屬性
在=的左邊使用點(diǎn)號(hào)分隔法來(lái)對(duì)內(nèi)嵌屬性進(jìn)行設(shè)值.例如:
...
securityManager.sessionManager.globalSessionTimeout = 1800000 ...
用BeanUtils轉(zhuǎn)換成下面的邏輯為:
securityManager.getSessionManager().setGlobalSessionTimeout(1800000);
圖的遍歷可以根據(jù)需要來(lái)設(shè)置深度: object.property1.property2....propertyN.value = blah
BeanUtils Property Support
Any property assignment operation supported by the BeanUtils.setProperty method will work in Shiro's [main] section, including set/list/map element assignments. Byte 數(shù)組值
由于原始字節(jié)數(shù)組不能文本格式進(jìn)行指定,我們必須使用字節(jié)數(shù)組的文本編碼.這些值可以通過(guò)Base64 編碼字符串(默認(rèn))或 Hex編碼字符串進(jìn)行指定.
默認(rèn)是Base64 ,因?yàn)?Base64編碼需要更少的實(shí)際文本來(lái)表現(xiàn)值- 它有一個(gè)更大的編碼字母表,這意味著你的令牌更短(對(duì)于文本配置更好).
# The 'cipherKey' attribute is a byte array. By default, text values
# for all byte array properties are expected to be Base64 encoded:
securityManager.rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA== ...
然而,如果你選擇使用Hex編碼, 你必須使用0x ('zero' 'x')作為字符串的前輟:
securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008
Collection屬性
Lists, Sets 和Maps 也可以像其它屬性來(lái)設(shè)置 -要么直接設(shè)置,要么通過(guò)內(nèi)嵌屬性設(shè)置. 對(duì)于sets和Lists,只需要以逗號(hào)分隔來(lái)指定值或?qū)ο笠玫募希?/div>
sessionListener1 = com.company.my.SessionListenerImplementation
...
sessionListener2 = com.company.my.other.SessionListenerImplementation
...
securityManager.sessionManager.sessionListeners = $sessionListener1, $sessionListener2
對(duì)于Maps,你可以以逗號(hào)分隔的key-value對(duì)列表來(lái)指定, 每個(gè)key-value對(duì)都由冒號(hào)來(lái)分隔
object1 = com.company.some.Class
object2 = com.company.another.Class
...
anObject = some.class.with.a.Map.property
anObject.mapProperty = key1:$object1, key2:$object2
In the above example, the object referenced by $object1 will be in the map under the String key key1, i.e. map.get("key1") returns object1. You can also use other objects as the keys:
anObject.map = $objectKey1:$objectValue1, $objectKey2:$objectValue2 ...
注意事項(xiàng)
順序影響
雖然INI格式非常方便和易于理解,但它并沒(méi)有像其它基于text/XML配置機(jī)制強(qiáng)大. 使用上述機(jī)制時(shí),要理解的最重要的事情是順序帶來(lái)的影響.
小心
Each object instantiation and each value assignment is executed in the order they occur in the 在每個(gè)[main] 分段中,每個(gè)對(duì)象實(shí)例和及值的分配都是出現(xiàn)順序來(lái)執(zhí)行的.
這些行最終都會(huì)轉(zhuǎn)換成對(duì)avaBeans getter/setter 方法的調(diào)用,且這些方法也是按相同順序來(lái)調(diào)用的!在編寫(xiě)配置時(shí),要當(dāng)心.
覆蓋實(shí)例
任何對(duì)象都可以通過(guò)配置文件后面新定義的實(shí)例來(lái)覆蓋.因此對(duì)于下面的例子,第二個(gè)myRealm定義將覆蓋前面一個(gè):
...
myRealm = com.company.security.MyRealm
...
myRealm = com.company.security.DatabaseRealm ...
譯者注:如果這兩個(gè)key在同一個(gè)段內(nèi),就與先前的描述違背了.
這個(gè)導(dǎo)致myRealm成為com.company.security.DatabaseRealm的實(shí)例,先前的實(shí)例將不再使用(垃圾回收掉).
默認(rèn)SecurityManager
你可能已經(jīng)注意到了,上面的例子中沒(méi)有定義 SecurityManager 實(shí)例, 我們跳到右邊只設(shè)置了一個(gè)內(nèi)嵌屬性:
myRealm = ...
securityManager.sessionManager.globalSessionTimeout = 1800000 ...
這是因?yàn)閟ecurityManager實(shí)例是特定的一個(gè)- 它已經(jīng)為你實(shí)例化好了,因此你不需要了解具體的SecurityManager實(shí)現(xiàn).
當(dāng)然,如果你想指定你自己的實(shí)現(xiàn),你可以定義你的實(shí)現(xiàn)來(lái)進(jìn)行覆蓋:
... securityManager = com.company.security.shiro.MyCustomSecurityManager ...
當(dāng)然,極少需要這樣- Shiro的SecurityManager實(shí)現(xiàn)是可充分定制的,通常可以按任何需要進(jìn)行配置.
[users]
[users] 分段允許你定義一些靜態(tài)用戶(hù)帳戶(hù)集合. 這在一個(gè)非常小的用戶(hù)帳戶(hù)或用戶(hù)帳戶(hù)不需要在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建環(huán)境中有用. 下面是例子:
[users]
admin = secret lonestarr = vespa, goodguy, schwartz darkhelmet = ludicrousspeed, badguy, schwartz
Automatic IniRealm
你可以像上面一樣來(lái)配置其它任何對(duì)象.
行格式
[users]分段中的每行都有下面的格式:
username = password, roleName1, roleName2, ..., roleNameN
- 等號(hào)左邊的值是用戶(hù)名
- 等號(hào)右邊的第一個(gè)值是用戶(hù)的密碼.密碼是必須的.
- 密碼后面以逗號(hào)分隔的值是為用戶(hù)分配的角色.角色名稱(chēng)是可選的.
密碼加密
如果你不想[users]分段中的密碼是明文的,你可以使用你喜歡的hash算法(MD5, Sha1, Sha256)來(lái)對(duì)其加密,并使用結(jié)果字符串來(lái)作為密碼值.
默認(rèn)情況下期望的密碼字符串是Hex編碼的,但你可以使用Base64編碼來(lái)代替(見(jiàn)下面).
Easy Secure Passwords
為了節(jié)省時(shí)間, 你可以使用Shiro的 Command Line Hasher, 它會(huì)將密碼hash成其它類(lèi)型的資源.對(duì)于加密 INI [users] 密碼是非常方便的. 一旦你指定了哈稀化的文本值, 你必須告訴Shiro它們是加密的. 你可為隱式創(chuàng)建的iniRealm 在[main]分段中來(lái)配置適當(dāng)?shù)膶?duì)應(yīng)你指定的哈希算法的CredentialsMatcher實(shí)現(xiàn):
[main]
...
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
...
iniRealm.credentialsMatcher = $sha256Matcher
...
[users]
# user1 = sha256-hashed-hex-encoded password, role1, role2,
...
user1 = 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b, role1, role2, ...
你可在CredentialsMatcher上配置任何屬性,如反映哈稀策略的對(duì)象,例如, 是否使用鹽(salting)或者執(zhí)行多少次hash迭代.
例如,如果你想對(duì)用戶(hù)密碼使用Base64編碼來(lái)代替默認(rèn)的Hex,你可以按如下來(lái)指定:
[main]
...
# true = hex, false = base64:
sha256Matcher.storedCredentialsHexEncoded = false
[roles]
[roles]分段允許你使用
Permissions 來(lái)關(guān)聯(lián)定義在[users]分段中的角色.
再次說(shuō)明,這對(duì)于少量角色或在運(yùn)行時(shí)不需要?jiǎng)討B(tài)創(chuàng)建角色的環(huán)境有用.下面是一個(gè)例子:
[roles]
# 'admin' 的角色擁有所有權(quán)限, 使用通配符'*'來(lái)表示 admin = *
# 'schwartz' 角色可在任何lightsaber上執(zhí)行任何操作(*): schwartz = lightsaber:*
# 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with # license plate 'eagle5' (instance specific id) goodguy = winnebago:drive:eagle5
行格式
[roles]分段中的每行必須按下面的格式來(lái)定義role-to-permission(s) key/value:
rolename = permissionDefinition1, permissionDefinition2, ..., permissionDefinitionN
內(nèi)部逗號(hào)
注意,如果在單個(gè)permissionDefinition 中存在逗號(hào)分隔符(如. printer:5thFloor:print,info),你必須使用雙引號(hào),以避免解析錯(cuò)誤:
"printer:5thFloor:print,info"
無(wú)權(quán)限的角色
如果角色不需要關(guān)聯(lián)權(quán)限,那么則不需要在[roles]分段中列出.如果角色不存在,只需要在[users]分段中定義角色就足夠創(chuàng)建角色了.
[urls]
這個(gè)分段及其它選項(xiàng)將在
Web章節(jié)中進(jìn)行講解.