天天看點

shiro實戰系列(九)之Web

一、Configuration(配置)

将 Shiro 內建到任何 Web 應用程式的最簡單的方法是在 web.xml 中配置 ContextListener 和 Filter,了解如何讀取 Shiro 的 INI 配置檔案。大部分的 INI 配置格式定義在 Configuration 頁的 INI Sections 節,但我在這裡我們将介紹一些額外 的 Web 的特定部分。

Shiro 1.2 and later 在 Shiro 1.2 及以後版本,标準的 Web 應用程式通過添加下面的 XML 塊到 web.xml 來初始化 Shiro

shiro實戰系列(九)之Web

這假設一個 Shiro INI 配置檔案在以下兩個位置任意一個,并使用最先發現的那個:

 1. /WEB-INF/shiro.ini

 2. 在 classpath 根目錄下 shiro.ini 檔案  

下面是上述配置所做的事情: 

(1)    EnvironmentLoaderListener 初始化一個 Shiro WebEnvironment 執行個體(其中包含 Shiro 需要的一切操作,包括 SecurityManager),使得它在 ServletContext 中能夠被通路。如果你需要在任何時候獲得 WebEnvironment 實 例,你可以調用 WebUtils.getRequiredWebEnvironment(ServletContext)。 

(2)     ShiroFilter 将使用此 WebEnvironment 對任何過濾的請求執行所有必要的安全操作。 

(3)    最後,filter-mapping 的定義確定了所有的請求被 ShiroFilter 過濾,建議大多數 Web 應用程式使用以確定任何 請求是安全的。 

二、ShiroFilter filter-mapping              

它通常是可取的在任何其他 filter-mapping 聲明之前定義 ShiroFilter filter-mapping,以確定 Shiro 也能在那些過濾器 下工作的很好。  

三、 Custom WebEnvironment Class

 預設情況下,EnvironmentLoaderListener 将建立一個 IniWebEnvironment 執行個體,呈現 Shiro 基于 INI 檔案的配置。如 果你願意,你可以在 web.xml 中指定一個自定義的 ServletContext context-param:

shiro實戰系列(九)之Web

這允許你自定義一個如何解析和代表 WebEnvironment 執行個體的配置格式。你可以為自定義的行為對現有的 IniWebEnvironment 建立子類,或完全支援不同的配置格式。例如,如果有人想在 XML 中配置 Shiro 而不是在 INI 中, 他們可以建立一個基于 XML 的實作,如 com.foo.bar.shiro.XmlWebEnviroment

四、Custom Configuration Locations

IniWebEnvironment 将會去讀取和加載 INI 配置檔案。預設情況下,這個類會自動地在下面兩個位置尋找 Shiro.ini 配 置(按順序)。 1. /WEB-INF/shiro.ini 2. classpath:shiro.ini 它将使用最先發現的那個。   然而,如果你想把你的配置放在另一位置,你可以在 web.xml 中用 contex-param 指定該位置。

shiro實戰系列(九)之Web

預設情況下,在 ServletContext.getResource 方法定義的規則下,param-value 是可以被解析的。例如, /WEB-INF/some/path/shiro.ini。  

但你也可以指定具體的檔案系統,如 classpath 或 URL 位置,通過使用 Shiro 支援的合适的資源字首,例如: 

  (1)file://home/foobar/myapp/shiro.ini 

(2)classpath:com/foo/bar/shiro.ini  (3)

url:http://confighost.mycompany.com/myapp/shiro.ini

Shiro 1.1 and earlier

在 Web 應用程式中使用 Shiro 1.1 或更早版本的最簡單的方法是定義 IniShiroFilter 并指定一個 filter-mapping:

shiro實戰系列(九)之Web

該定義期望你的 INI 配置是一個在 classpath 根目錄的 Shiro.ini 檔案(如:classpath:shiro.ini)。

Custom Path

如果你不想将你的 INI 配置放在/WEB-INF/shiro.ini 或 classpath:shiro.ini,你可以指定一個自定義的資源位置,如果必 要的話。添加一個 configPath 的 init-param,并指定資源位置。

shiro實戰系列(九)之Web

不合格的(不完整的組合或'non-prefixed')configPath 值被假定為 ServletContext 的資源路徑,通過 ServletContext.getResource 方法所定義的規則來解析。

shiro實戰系列(九)之Web

通過分别地使用 classpath:,url:,或 file:字首來指明 classpath,url,或 filesystem 位置,你也可以指定其他非 ServletContext 資源位置。例如:

shiro實戰系列(九)之Web

Inline Config

 最後,也可以将你的 INI 配置嵌入到 web.xml 中而不使用一個獨立的 INI 檔案。你可以通過使用 init-param 做到這點, 而不是 configPath:

shiro實戰系列(九)之Web

内嵌配置對于小型的或簡單的應用程式通常是很好用的,但是由于以下原因一般把它具體化到一個專用的 Shiro.ini 檔案中:

  (1)你可能編輯了許多安全配置,不希望為 web.xml 添加版本控制。 

(2)你可能想從餘下的 web.xml 配置中分離安全配置。 

(3)你的安全配置可能變得很大,你想保持 web.xml 的苗條并易于閱讀。 

(4)你有個負責的編譯系統,相同的 shiro 配置可能需要在多個地方被引用。 

 這取決于你——使用什麼使你的項目更有意義。

Subject Inspection

shiro實戰系列(九)之Web

[urls]項允許你做一些在我們已經見過的任何 Web 架構都不存在的東西:在你的應用程式中定義自适應過濾器鍊來 比對 URL 路徑!

  這将更為靈活,功能更為強大,比你通常在 web.xml 中定義的過濾器鍊更為簡潔:即使你從未使用任何 Shiro 提供 的其他功能并僅僅使用了這個,但它即使是單獨使用也是值得的。 

[urls] 在 urls 項的每一行格式如下:

URL_Ant_Path_Expression = Path_Specific_Filter_Chain  

shiro實戰系列(九)之Web

接下來我們将讨論這些行的具體含義。

URL Path Expressions  

等号左邊是一個與 Web 應用程式上下文根目錄相關的 Ant 風格的路徑表達式。   例如,假設你有如下的[urls]行:

shiro實戰系列(九)之Web

此行表明,“任何對我應用程式的/accout 或任何它的子路徑(/account/foo, account/bar/baz,等等)的請求都将觸 發'ssl, authc'過濾器鍊”。我們将在下面讨論過濾器鍊。   請注意,所有的路徑表達式都是相對于你的應用程式的上下文根目錄而言的。這意味着如果某一天你在某個位置部 署了你的應用程式,如 www.somehost.com/myapp ,然後又将它部署到了 www.anotherhost.com (沒有'myapp'子 目錄),這樣的比對模式仍将繼續工作。所有的路徑都是相對于 HttpServletRequest.getContextPath()的值來的。

shiro實戰系列(九)之Web

Filter Chain Definitions

 等号右邊是逗号隔開的過濾器清單,用來執行比對該路徑的請求。它必須符合以下格式: filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN]   并且: 

(1)    filterN 是一個定義在[main]項中的 filter bean 的名字。 

(2)     [optional_configN]是一個可選的括号内的對特定的路徑,特定的過濾器有特定含義的字元串(每個過濾器,每 個路徑的具體配置!)。若果該過濾器對該 URL 路徑并不需要特定的配置,你可以忽略括号,于是 filteNr[] 就變成了 filterN.

因為過濾器标志符定義了鍊(又名清單),是以請記住順序問題!請按順序定義好你的逗号分隔的清單,這樣請求 就能夠流通這個鍊。  

最後,每個過濾器按照它期望的方式自由的處理請求,即使不具備必要的條件(例如,執行一個重定向,響應一個 HTTP 錯誤代碼,直接渲染等)。否則,它有可能允許該請求繼續通過這個過濾器鍊到達最終的視圖。

shiro實戰系列(九)之Web

Available Filters

在過濾器鍊中能夠使用的過濾器“池”被定義在[main]項。在[main]項中指派給它們的名字就是在過濾器鍊定義中使 用的名字。例如:

shiro實戰系列(九)之Web

Default Filters

當運作一個 Web 應用程式時,Shiro 将會建立一些有用的預設 Filter 執行個體,并自動地在[main]項中将它們置為可用。 你可以在 main 中配置它們,當作在你的鍊的定義中你是否有任何其他的 bean 和 reference。例如:

shiro實戰系列(九)之Web

自動地可用的預設的 Filter 執行個體是被 DefaultFilter 枚舉定義的,枚舉的名稱字段是可供配置的名稱。它們是:

shiro實戰系列(九)之Web
shiro實戰系列(九)之Web

由于這是與任何過濾器鍊定義機制(web.xml,Shiro 的 INI 等)相關的例子,你通過在過濾器鍊中包含它來啟用過 濾器,通過在過濾器鍊中移除它來禁用過濾器。  

但在 Shiro 1.2 中新增的一個新功能是不通過從過濾器鍊中移除過濾器來啟用或禁用過濾器。如果啟用(預設設定), 那麼請求将如預期一樣過濾。如果禁用,那麼該過濾器将允許請求立即通過到 FilterChain 的下一個元素。你可以基 于一般配置屬性觸發過濾器的啟用狀态,或者你甚至可以在每一個請求的基礎上觸發。   這是一個強大的概念,因為基于特定需求啟用或禁用一個過濾器比更改靜态過濾器鍊(這是永久的且固定的)定義 更為友善。 

 Shiro通過它的OncePerRequestFilter抽象父類來完成這點。所有Shiro的不受規範限制的過濾器實作子類實作這一點,

是以不需要從過濾器鍊移除它們實作啟用或禁用。如果你需要實作此功能,你可以為自己的過濾器實作繼承這個類 的子類。

  SHIRO-224 将有望為任何過濾器使用這項功能,不僅僅隻是那些 OncePerRequestFilter 的子類。如果這對你很重要, 請為這個

issue 投票。

General Enabling/Disabling

OncePerRequestFilter(及其所有子類)支援

Enabling/Disabling 所有請求及 per-request 基礎。   一般為所有的請求啟用或禁用一個過濾器是通過設定其 enabled 屬性為 true 或

false。預設的設定是 true 由于大多 數過濾器本質上是需要執行的,如果他們被配置在一個過濾器鍊中。   例如,在 shiro.ini 中:

該例表明,許多潛在的 URL 路徑都需要請求必須通過 SSL 連接配接保證。在開發中設定 SSL 是令人沮喪且費時的。在開 發時,你可以禁用 ssl 過濾器。當部署産品時,你可以啟用它通過一個配置屬性——這比手動更改所有 URL 路徑或 維護兩個 Shiro 配置要容易得多。

Request-specific Enabling/Disabling

 OncePerRequestFilter 實際上決定過濾器啟用或禁用是基于它的 isEnabled(request, response)方法。   該方法預設傳回 enabled 屬性的值,該屬性通常是用來 enabling/disabling 上面提及的所有請求。如果你想啟用或禁 用一個基于特定标準的請求的過濾器,你可以通過覆寫 OncePerRequestFilter 的 isEnabled(request, response)方法來 執行更多特定的檢查。  

Path-specific Enabling/Disabling

Shiro 的 PathMatchingFilter(一個 OncePerRequestFilter 的子類)能夠對基于被過濾的特定路徑的配置作出反應。這 意味着你可以啟用或禁用一個過濾器基于路徑和特定路徑配置,除了傳入的 request 和 response。   如果你需要能夠對比對的路徑和特定路徑配置作出反應來判斷一個過濾器是否是啟用的或禁用的,而不是通過覆寫 OncePerRequestFilter 的 isEnabled(request, reponse)方法,你應該是覆寫 PathMatchingFilter 的 isEnabled(request, response)方法。  

Remember Me Services

Shiro 将執行'rememberMe'服務如果 AuthenticationToken 實作了 org.apache.shiro.authc.RememberMeAuthenticationToken 接口。該接口指定了一個方法:

shiro實戰系列(九)之Web
shiro實戰系列(九)之Web

如果該方法傳回 true,Shiro 将會在整個會話中記住終端使用者的身份 ID。

shiro實戰系列(九)之Web

Programmatic Support

要有計劃性地使用 rememberMe,你可以在一個支援該配置的類上把它的值設為 true。例如,使用标準的 UsernamePasswordToken:

shiro實戰系列(九)之Web

Form-based Login

對于 Web 應用程式而言,authc 過濾器預設是 FormAuthenticationFilter。它支援将'rememberMe'的布爾值作為一個 form/request 參數讀取。預設地,它期望該 request 參數被命名為 rememberMe。下面是一個支援這點的 shiro.ini 配 置的例子: 

shiro實戰系列(九)之Web

同時在你的 web form 中有一個名為'rememberMe'的 checkbox。

shiro實戰系列(九)之Web

預設地,FormAuthenticationFilter 将會尋找名為 username,password 及 rememberMe 的 request 參數。如果這些不 同于你使用的 form 中的表單域名,你可能想在 FormAuthenticationFilter 上配置這些參數名。例如,在 shiro.ini 中:

shiro實戰系列(九)之Web

Cookie configuration

你可以通過設定{{RememberMeManager}}的各方面的 cookie 屬性來配置 rememberMe cookie 是如何工作的。例如, 在 shiro.ini 中:

shiro實戰系列(九)之Web

Custom RememberMeManager

 應該注意到,預設基于 cookie 的 RememberMeManager 實作不符合你的需求,你可以插入任何你喜歡的插件到 securityManager 當中,就像你配置任何其他對象的引用一樣:

shiro實戰系列(九)之Web

JSP/GSP Tag Library

 Apache Shiro 提供了一個 Subject-aware JSP/GSP 标簽庫,它允許你控制你的 JSP,JSTL 或 GSP 頁面基于目前 Subject 的狀态進行輸出。這對于根據身份個性化視圖及目前使用者所浏覽的頁面授權狀态是相當有用的。  

Tag Library Configuration

 标簽庫描述檔案(TLD )被打包在 META-INF/shiro.tld 檔案中的 shiro-web.jar 檔案中。要使用任何标簽,添加下面一行 到你 JSP 頁面(或任何你定義的頁面指令)的頂部。  

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

  我們使用 shiro 字首用以表明 shiro 标簽庫命名空間,當然你可以指定任何你喜歡的名字。   現在我們将讨論每一個标簽,并展示它是如何用來渲染頁面的。

 The guest tag

guest 标簽将顯示它包含的内容,僅當目前的 Subject 被認為是'guest'時。'guest'是指沒有身份 ID 的任何 Subject。也 就是說,我們并不知道使用者是誰,因為他們沒有登入并且他們沒有在上一次的通路中被記住(RememberMe 服務)。 

 例子:  

shiro實戰系列(九)之Web

guest 标簽與 user 标簽邏輯相反。

The user tag

user 标簽将顯示它包含的内容,僅當目前的 Subject 被認為是'user'時。'user'在上下文中被定義為一個已知身份 ID 的 Subject,或是成功通過身份驗證及通過'RememberMe'服務的。請注意這個标簽在語義上與 authenticated 标簽是 不同的,authenticated 标簽更為嚴格。

shiro實戰系列(九)之Web

usre 标簽與 guest 标簽邏輯相反

The authenticated tag

僅僅隻當目前使用者在目前會話中成功地通過了身份驗證 authenticated 标簽才會顯示包含的内容。它比'user'标簽更 為嚴格。它在邏輯上與'notAuthenticated'标簽相反。   authenticated 标簽隻有當目前 Subject 在其目前的會話中成功地通過了身份驗證才會顯示包含的内容。它比 user 标 簽更為嚴格,authenticated 标簽通常在敏感的工作流中用來確定身份 ID 是可靠的。  

例子:

shiro實戰系列(九)之Web

authenticated 标簽與 notAuthenticated 标簽邏輯相反。

The notAuthenticated tag

notAuthenticated 标簽将會顯示它所包含的内容,如果目前 Subject 還沒有在其目前會話中成功地通過驗證。   

shiro實戰系列(九)之Web

notAuthenticated 标簽與 Authenticated 标簽邏輯相反。

The principal tag

principal 标簽将會輸出 Subject 的主體(辨別屬性)或主要的屬性。 若沒有任何标簽屬性,則标簽将使用 principal 的 toString()值來呈現頁面。例如(假設 principal 是一個字元串的使用者 名): 

shiro實戰系列(九)之Web

Typed principal

principal 标簽預設情況下,假定該 principal 輸出的是 subject.getPrincipal()的值。但若你想輸出一個不是主要 principal 的值,而是屬于另一個 Subject 的 principal collection,你可以通過類型來擷取該 principal 并輸出該值。   例如,輸出 Subject 的使用者 ID(并不是 username),假設該 ID 屬于 principal collection:

shiro實戰系列(九)之Web

Principal property

但如果該 principal(是預設主要的 principal 或是上面的'typed' principal)是一個複雜的對象而不是一個簡單的字元串, 而且你希望引用該 principal 上的一個屬性該怎麼辦呢?你可以使用 property 屬性來來表示 property 的名稱來了解 (必須通過 JavaBeans 相容的 getter 方法通路)。例如(假主要的 principal 是一個 User 對象) 

shiro實戰系列(九)之Web

The hasRole tag

 hasRole 标簽将會顯示它所包含的内容,僅當目前 Subject 被配置設定了具體的角色。

shiro實戰系列(九)之Web

The lacksRole tag

 lacksRole 标簽将會顯示它所包含的内容,僅當目前 Subject 未被配置設定具體的角色。

shiro實戰系列(九)之Web

The hasAnyRole tag

 hasAnyRole 标簽将會顯示它所包含的内容,如果目前的 Subject 被配置設定了任意一個來自于逗号分隔的角色名清單中 的具體角色。

shiro實戰系列(九)之Web

The hasPermission tag

 hasPermission 标簽将會顯示它所包含的内容,僅當目前 Subject“擁有”(蘊含)特定的權限。也就是說,使用者具 有特定的能力。     

shiro實戰系列(九)之Web

The lacksPermission tag

lacksPermission 标簽将會顯示它所包含的内容,僅當目前 Subject 沒有擁有(蘊含)特定的權限。也就是說,使用者沒 有特定的能力。

shiro實戰系列(九)之Web