1. 程式人生 > >Apache Shiro(四)shiro配置

Apache Shiro(四)shiro配置

Shiro旨在適用於任何環境,從簡單的命令列應用程式到最大的企業叢集應用程式。由於環境的多樣性,存在許多適合於配置的配置機制。本節介紹僅Shiro核心支援的配置機制。

程式化配置

建立SecurityManager並使其可供應用程式使用的絕對最簡單的方法是建立一個org.apache.shiro.mgt.DefaultSecurityManager並在程式碼中引用它。例如:

Realm realm = //instantiate or acquire a Realm instance.  We'll discuss Realms later.
SecurityManager securityManager = new DefaultSecurityManager(realm);

//Make the SecurityManager instance available to the entire application via static memory: 
SecurityUtils.setSecurityManager(securityManager);

正如前面架構章節中所討論的,Shiro的SecurityManager實現本質上是巢狀安全特定元件的模組化物件。因為它們也是JavaBeans相容的,所以可以呼叫任何巢狀的元件,通過gettersetter方法來配置SecurityManager它及其內部物件關係。

...

DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);

SessionDAO sessionDAO = new CustomSessionDAO();

((DefaultSessionManager)securityManager.getSessionManager()).setSessionDAO(sessionDAO);
...

使用直接方法呼叫,您可以配置SecurityManager物件網的任何部分。

但是,儘管程式化配置很簡單,但是對於大部分真實的應用來說,它並理想配置。程式設計配置可能不適合您的應用程式有幾個原因:

  • 它要求您瞭解並例項化介面的實現。如果你不必瞭解具體的實現以及在哪裡找到它們會更好。

  • 由於Java的型別安全性,您需要將通過get*方法獲得的物件轉換為其特定的實現。這些強制轉換是醜陋的,冗長的,並且將你與實現課程緊密結合在一起。

  • 通過SecurityUtils.setSecurityManager方法呼叫建立的,SecurityManager例項在JVM是靜態的,單例的。這會使用該物件能被同一JVM中的多個應用公用。如果超過一個啟用shiro的應用程式是在運行同一個JVM上會造成問題。如果例項是應用程式單例,但不是靜態記憶體引用可能會更好。

  • 每次要進行Shiro配置更改時,它都要求您重新編譯應用程式。

然而,即使有這些警告,直接程式設計操作方法在記憶體受限的環境中仍然很有價值,例如智慧手機應用程式。如果您的應用程式不在記憶體受限的環境中執行,您將發現基於文字的配置更易於使用和閱讀。

INI配置

大多數應用程式都可以從基於文字的配置中受益,這些配置可以獨立於原始碼進行修改,甚至可以讓那些不太熟悉Shiro API的人更容易理解。

為了確保shiro可以在具有最小第三方依賴性的環境中工作,Shiro支援INI格式來構建SecurityManager物件關係網及其支援元件。INI易於閱讀,易於配置,易於設定,適合大多數應用。

以下是如何基於INI配置構建SecurityManager的兩個示例。

我們可以從INI資源路徑建立SecurityManager例項。當前綴為,或分別為字首時file:,可以從檔案系統,類路徑或URL獲取資源。此示例使用工廠從類路徑的根目錄中提取檔案並返回例項:classpath:url:Factoryshiro.iniSecurityManager

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);
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();
//populate the Ini instance as necessary
...
Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);

INI部分

INI基本上是一個文字配置,由鍵/值對組成,且鍵名在每個部分是唯一,而不是在整個配置上(這與JDK 屬性不同)。每個部分可以被視為單個定義。

註釋行由#開始或者分號(';')

以下是Shiro配置的部分示例:

# =======================
# Shiro INI configuration
# =======================

[main]
# Objects and their properties are defined here,
# Such as the securityManager, Realms and anything
# else needed to build the SecurityManager

[users]
# The 'users' section is for simple deployments
# when you only need a small number of statically-defined
# set of User accounts.

[roles]
# The 'roles' section is for simple deployments
# when you only need a small number of statically-defined
# roles.

[urls]
# The 'urls' section is used for url-based security
# in web applications.  We'll discuss this section in the
# Web documentation

1、[main]部分

[main]部分是您配置應用程式SecurityManager例項及其任何依賴項的部分,例如Realm

配置像SecurityManager或其任何依賴項之類的物件例項聽起來與INI很難相關,我們只能使用名稱/值對。但是通過對物件關係的一些約定和理解,你會發現你可以做很多事情。Shiro使用這些假設來實現簡單但相當簡潔的配置機制。

我們經常喜歡將這種方法稱為“窮人版”依賴注入,雖然沒有完整的Spring / Guice / JBoss XML檔案那麼強大,但是你會發現它在沒有太多複雜性的情況下完成了很多工作。當然,那些其他配置機制也是可用的,但它們並不是被Shiro強制要求的。

為了滿足你的胃口,這裡是一個有效[main]配置的例子。我們將在下面詳細介紹它,但是你可能會發現你已經通過直覺瞭解了很多已經發生的事情:

[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

定義一個物件

請考慮以下[main]部分程式碼段:

[main]
myRealm = com.company.shiro.realm.MyRealm
...

此行例項化一個com.company.shiro.realm.MyRealm物件例項,並使該物件的例項名為myRealm,以便進一步引用和配置。

如果例項化的物件實現了org.apache.shiro.util.Nameable介面,則將呼叫Nameable.setName為其設定例項名(在此示例中例項名為myRealm)。

設定物件屬性

基本型別的值

...
myRealm.connectionTimeout = 30000
myRealm.username = jsmith
...

等價於

...
myRealm.setConnectionTimeout(30000);
myRealm.setUsername("jsmith");
...

事實上,Shiro預設使用Apache Commons BeanUtils來完成設定這些屬性。因此,儘管INI值是文字,但BeanUtils知道如何將字串值轉換為正確的基本型別,然後呼叫相應的JavaBeans setter方法。

設定引用屬性

如果您需要設定的值不是原始值,而是另一個物件,可以使用美元符號($)來引用先前定義的例項。例如:

...
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
...
myRealm.credentialsMatcher = $sha256Matcher
...

設定巢狀屬性

...
securityManager.sessionManager.globalSessionTimeout = 1800000
...

等價於
securityManager.getSessionManager().setGlobalSessionTimeout(1800000);

設定位元組陣列值

因為原始位元組陣列不能以文字格式來描述,所以我們必須使用位元組陣列的文字編碼。可以將值指定為Base64編碼的字串(預設值)或Hex編碼的字串。預設值為Base64,因為Base64編碼需要較少的實際文字來表示值 - 它具有更大的編碼字母表,這意味著您的標記更短(文字配置更好一點)。

# 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==
...

但是,如果您更喜歡使用十六進位制編碼,則必須在String標記前加上0x

securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008

設定集合屬性

 list

sessionListener1 = com.company.my.SessionListenerImplementation
...
sessionListener2 = com.company.my.other.SessionListenerImplementation
...
securityManager.sessionManager.sessionListeners = $sessionListener1, $sessionListener2

map

object1 = com.company.some.Class
object2 = com.company.another.Class
...
anObject = some.class.with.a.Map.property

anObject.mapProperty = key1:$object1, key2:$object2

在上面的示例中,引用的物件$object1將位於key1鍵下的對映中,即map.get("key1")返回object1。您還可以使用其他物件作為鍵

anObject.map = $objectKey1:$objectValue1, $objectKey2:$objectValue2
...

每個物件例項化和每個值賦值按它們在[main]部分中出現的順序執行。這些行最終轉換為JavaBeans getter / setter方法呼叫,因此這些方法以相同的順序呼叫!

編寫配置時請記住這一點。

覆蓋例項

任何物件都可以被稍後在配置中定義的新例項覆蓋。因此,例如,第二個myRealm定義將覆蓋第一個:

...
myRealm = com.company.security.MyRealm
...
myRealm = com.company.security.DatabaseRealm
...

這將導致myRealm成為一個com.company.security.DatabaseRealm例項,並且永遠不會使用前一個例項(並收集垃圾)。

您可能已經在上面的完整示例中注意到,我們未定義SecurityManager例項的類,但是可以直接跳轉到僅設定巢狀屬性:

myRealm = ...

securityManager.sessionManager.globalSessionTimeout = 1800000
...

這是因為securityManager例項是一個特殊的例項 - 它已經為您例項化並準備好了,所以您不需要知道SecurityManager要例項化的特定實現類。

當然,如果您確實想要指定自己的實現,則可以按照上面“覆蓋例項”部分中的說明定義實現:

...
securityManager = com.company.security.shiro.MyCustomSecurityManager
...

當然,這很少需要。

2、[users]

[users]部分允許您定義一組靜態使用者帳戶。這在具有非常少量使用者帳戶的環境中或者在執行時不需要動態建立使用者帳戶的環境中非常有用。這是一個例子:

[users]
admin = secret
lonestarr = vespa, goodguy, schwartz
darkhelmet = ludicrousspeed, badguy, schwartz

僅定義非空[users]或[roles]部分將自動觸發org.apache.shiro.realm.text.IniRealm例項的建立,並使其在名稱下的[main]部分中可用iniRealm。您可以像上面描述的任何其他物件一樣配置它。

行格式

[users]部分中的每一行必須符合以下格式:

usernamepasswordroleName1roleName2,...,roleNameN

  • 等號左側的值是使用者名稱
  • 等號右側的第一個值是使用者的密碼。密碼是必需的。
  • 密碼後面的任何逗號分隔值是分配給該使用者的角色的名稱。角色名稱是可選的。

加密密碼

如果您不希望[users]部分密碼為純文字,您可以使用您喜歡的雜湊演算法(MD5,Sha1,Sha256等)加密它們,但是您喜歡並使用生成的字串作為密碼值。預設情況下,密碼字串應為十六進位制編碼,但可以配置為Base64編碼(見下文)。

一旦指定了雜湊文字密碼值,就必須告訴Shiro這些是加密的。您可以通過配置iniRealm[main]部分中隱式建立的方法來使用CredentialsMatcher與您指定的雜湊演算法相對應的適當實現:

[main]
...
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
...
iniRealm.credentialsMatcher = $sha256Matcher
...

[users]
# user1 = sha256-hashed-hex-encoded password, role1, role2, ...
user1 = 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b, role1, role2, ...

例如,如果使用者的密碼字串是Base64編碼而不是預設的Hex,則可以指定:

[main]
...
# true = hex, false = base64:
sha256Matcher.storedCredentialsHexEncoded = false

3、[roles]

[roles]部分允許您將許可權與[users]部分中定義的角色相關聯。同樣,這在具有少量角色的環境中或在執行時不需要動態建立角色時非常有用。這是一個例子:

[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5

行格式

[roles]部分中的每一行必須使用以下格式定義角色到許可權的鍵/值對映:

rolenamepermissionDefinition1permissionDefinition2,...,permissionDefinitionN

請注意,如果單個permissionDefinition需要在內部以逗號分隔(例如printer:5thFloor:print,info),則需要用雙引號(“)括起該定義以避免解析錯誤:"printer:5thFloor:print,info"

如果您的角色不需要許可權關聯,則不需要在[roles]部分中列出它們。只是在[users]部分中定義角色名稱就足以建立角色(如果它尚不存在)。

[urls]

本章及其選項在其他章節中進行了描述。