shiro學習----資料

Apache Shiro Reference Documentation

 

 

Apache Shiro簡介

 

什麼是 Apache Shiro?

Apache  Shiro 是一個強大而靈活的開源安全框架,它乾淨利落地處理身份認證,授權,企業會話管理和加密。

 

Apache  Shiro 的首要目標是易於使用和理解。安全有時候是很複雜的,甚至是痛苦的,但它沒有必要這樣。框架應該儘可能掩蓋複雜的地方,露出一個乾淨而直觀的 API,來簡化開發人員在使他們的應用程序安全上的努力。

 

以下是你可以用 Apache Shiro 所做的事情:

  • 驗證用戶來覈實他們的身份
  • 對用戶執行訪問控制,如:
    • 判斷用戶是否被分配了一個確定的安全角色
    • 判斷用戶是否被允許做某事
  • 在任何環境下使用 Session API,即使沒有 Web 或 EJB 容器。
  • 在身份驗證,訪問控制期間或在會話的生命週期,對事件作出反應。
  • 聚集一個或多個用戶安全數據的數據源,並作爲一個單一的複合用戶“視圖”。
  • 啓用單點登錄(SSO)功能。
  • 爲沒有關聯到登錄的用戶啓用"Remember Me"服務

以及更多——全部集成到緊密結合的易於使用的 API 中。

 

Shiro 視圖在所有應用程序環境下實現這些目標——從最簡單的命令行應用程序到最大的企業應用,不強制依賴其他第三方框架,容器,或應用服務器。當然,該項目的目標是儘可能地融入到這些環境,但它能夠在任何環境下立即可用。

 

Apache Shiro的特點

Apache Shiro 是一個擁有許多功能的綜合性的程序安全框架。下面的圖表展示了 Shiro 的重點,並且這個參考手冊也會與之類似的被組織起來:

 

 

Shiro 把 Shiro 開發團隊稱爲“應用程序的四大基石”——身份驗證,授權,會話管理和加密作爲其目標。

  • Authentication:有時也簡稱爲“登錄”,這是一個證明用戶是他們所說的他們是誰的行爲。
  • Authorization:訪問控制的過程,也就是絕對“誰”去訪問“什麼”。
  • Session Management:管理用戶特定的會話,即使在非 Web 或 EJB 應用程序。
  • Cryptography:通過使用加密算法保持數據安全同時易於使用。

也提供了額外的功能來支持和加強在不同環境下所關注的方面,尤其是以下這些:

  • Web Support:Shiro 的 web 支持的 API 能夠輕鬆地幫助保護 Web 應用程序。
  • Caching:緩存是 Apache Shiro 中的第一層公民,來確保安全操作快速而又高效。
  • Concurrency:Apache Shiro 利用它的併發特性來支持多線程應用程序。
  • Testing:測試支持的存在來幫助你編寫單元測試和集成測試,並確保你的能夠如預期的一樣安全。
  • "Run As":一個允許用戶假設爲另一個用戶身份(如果允許)的功能,有時候在管理腳本很有用。
  • "Remember Me":在會話中記住用戶的身份,所以他們只需要在強制時候登錄。

 

Apache Shiro的教程

 

您的第一個 Apache Shiro應用程序

如果你從未使用過 Apache Shiro,這個簡短的教程將會向您展示如何建立一個由 Apache Shiro 擔保的初始的及非常簡單的應用程序。一路上我們將討論 Shiro 的核心概念來幫助你熟悉 Shiro 的設計和 API。

 

當你遵循本教程時,如果你確實不想編輯文件,你可以得到一個幾乎相同的實例應用程序並按照你的意願引用它。選擇一個位置:

  • 在 Apache Shiro 的版本控制庫:https://svn.apache.org/repos/asf/shiro/trunk/samples/quickstart
  • 在 Apache Shiro 的源代碼的 samples/quickstart 目錄。該源代碼在 Download 頁面提供下載。

 

設置

在這個簡單的示例中,我們將創建一個非常簡單的命令行應用程序,它將會運行並迅速退出,這樣你能夠獲得對 Shiro

的 API 的感受。

 

該教程需要 Java 1.5 及更高本。我們也使用 Apache Maven 作爲我們的構建工具,但當然這不是使用 Apache Shiro 所必需的。你可以獲取 Shiro 的 jar 包並按你喜歡的方式合併到你的應用程序,例如可能是一 Apache Ant 和 Ivy。

 

對於本教程,請確保你正在使用 Maven 2.2.1 或更高版本。你應該能夠鍵入 mvn -version 命令行提示符,並看到與下面類似的東西:

 

 
 

 

現在,在你的文件系統上創建一個新的目錄,例如,shiro-tutorial 並在該目錄下保存下面的 Maven pom.xml 文件:

pom.xml

 

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

http://maven.apache.org/maven-v4_0_0.xsd">

 

<modelVersion>4.0.0</moderVersion>

<groupId>org.apache.shiro.tutorials</groupId>

<artifactId>shiro-tutorial</artifactId>

<version>1.0.0-SNAPSHOT</version>

<name>First Apache Shiro Application</name>

<packaging>jar</packaging>

 

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

 

<build>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>2.0.2</version>

<configuration>

<source>1.5</source>

<target>1.5</target>

<encoding>${project.build.sourceEncoding}</encoding>

</configuration>

</plugin>

 

<!-- This plugin is only to test run our little application. It is not needed in most Shiro-enabled applications: -->

<plugin>

 

<groupId>org.codehaus.mojo</groupId>

<artifactId>exec-maven-plugin</artifactId>

<version>1.1</version>

<executions>

<execution>

<goals>

<goal>java</goal>

</goals>

</execution>

</executions>

<configuration>

<classpathScope>test</classpathScope>

<mainClass>Tutorial</mainClass>

</configuration>

</plugin>

</plugins>

</build>

 

<dependencies>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-core</artifactId>

<version>1.1.0</version>

</dependency>

<!-- Shiro use SLF4J for logging. We'll use the 'simple' binding

in this example app. See http://ww.slf4j.org for more info. -->

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-simple</artifactId>

<version>1.6.1</version>

<scope>test</scope>

</dependency>

<dependencies>

 

</project>

 

 

 

本教程類

我們將運行一個簡單的命令行應用程序,因此,我們需要創建一個擁有 public static void main(String[] args)方法的 Java

類。

在包含你 pom.xml 文件的同樣目錄下,創建 src/main/java 子目錄。在 src/main/java 目錄下創建具有下面內容的

Tutorial.java 文件:

 

 
 

 

 

現在不必擔心 import 語句——我們不久將會涉及到它們。但現在,我們得有一個命令行應用程序“外形”。該程序所能夠做到全部事情是打印出文本"My First Apache Shiro Application"並退出。

 

Test Run

要試用我們的教程應用程序,請在你的教程項目的根目錄下執行以下的命令提示符,並鍵入以下內容:

 

mvn compile exec:java

 

然後你將看到我們的 little 教程“程序”運行並退出。你應該會看到與下面相似的一些東西(注意粗體文本,它顯示了我們的輸出):

Run the Application

lhazlewood:~/projects/shiro-tutorial$ mvn compile exec:java

 

... a bunch of Maven output ...

1 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application

lhazlewood:~/projects/shiro-tutorial\$

 

我們已經驗證了該程序運行成功——現在讓我們啓用 Apache Shiro。當我們繼續本教程的時候,你可以在每次我們添加一些代碼後運行 mvn compile exec:java 來觀察我們變化後的結果。

 

Shiro的使用

在應用程序中啓用 Shiro 最先要明白的事情是幾乎在 Shiro 中的每個東西都與一個名爲 SecurityManager 的主要的/核心的組件有關。對於那些熟悉 Java 安全的人來說,這是 Shiro 的 SecurityManager 概念——它不等同於java.lang.SecurityManager。

 

雖然我們將在 Architecture 章節詳細的涉及到 Shiro 的設計,但現在瞭解 Shiro 的 SecurityManager 是應用程序的 Shiro 環境的核心及每個應用程序中必須存在一個 SecurityManager 是很有益處的。因此,在我們的教程應用程序中第一件要做的事情就是配置 SecurityManager 實例。

 

配置

 

雖然我們能夠直接實例化一個 SecurityManager 類,但 Shiro 的 SecurityManager 實現有足夠的配置選項及內置組件使得在 Java  源代碼做這件事情變得較爲痛苦——如果使用一個靈活的基於文本的配置格式來配置 SecurityManager,那麼這將是一件很容易的事情。

 

爲此,Shiro 通過基於文本的 INI 配置文件提供了一個默認的"共性(common denominator)"解決方案。近來人們已經相當厭倦了使用笨重的 XML 文件,且 INI 文件易於閱讀,使用簡單,依賴性低。你稍後將會看到有了對象導航圖的簡單理解,INI 文件能夠有效地被用來配置簡單的對象圖,如 SecurityManager。

 

shiro.ini文件

因此,我們將爲這個簡單的應用程序使用 INI 文件來配置 Shiro SecurityManager。首先,在 pom.xml 所在的同一目錄下創建 src/main/resources 目錄。然後在新目錄下創建包含以下內容的 shiro.ini 文件:

 

 
 

 

如你所見,這個配置基本上建立了一小組靜態用戶帳戶,對於我們的第一個應用程序已經足夠了。在後面的章節中, 你將看到我們如何使用更復雜的用戶數據源,如關係數據庫,LDAP 的 AcitveDirectory,以及更多。

 

引用配置

現在我們已經定義好了一個 INI 文件,我們可以在我們的教程應用程序類中創建 SecurityManager 實例了。改變 main

方法來反映以下的更新內容:

 

 

好了,在僅僅添加了 3 行代碼後,Shiro 就在我們的簡單應用程序中啓用了!很容易是吧?

 

輕鬆地運行 mvn compile exec:java,並看到這一切仍然運行成功(由於 Shiro 的默認調試日誌或更低版本,你將不會看到任何的 Shiro 日誌消息——如果在啓動和運行沒有報錯,那麼你知道一切仍然正常)。

 

這裏是上面增加的代碼所做的:

  1. 我們使用 Shiro 的 IniSecurityManager 實現來提取我們的 shiro.ini 文件,它位於 classpath 的根目錄。該實現反映了 Shiro 對工廠設計模式的支持。classpath: 前綴是一個資源定位符,用來告訴 shiro 去哪加載 ini 文件(其他前綴,如 url:和 file:也同樣被支持)。
  2. factory.getInstance()方法被調用,它來解析 INI 文件並返回反映該配置的 SecurityManager 實例。
  3. 在這個簡單的例子中,我們把 SecurityManager 設置爲一個靜態的(memory)單例,能夠跨 JVM 訪問。但請注意,這是不可取的,如果你在單個的 JVM 只中會有不只一個啓用 Shiro 的應用程序。對於這個簡單的例子而言,這是沒有問題的,但更爲複雜的應用程序環境通常將  SecurityManager  置於應用程序特定的存儲中(如在 Web 應用中的 ServletContext 或 Spring,Guice 後 JBoss DI 容器實例)。

 

使用Shiro

現在我們的 SecurityManager 已經設置好並可以使用了,現在我們能夠開始做一些我們真正關心的事情——執行安全操作。

 

當保護我們的應用程序時,我們對自己可能提出的最爲相關的問題是“當前用戶是誰”或“當前用戶是否被允許做

XXX”。當我們編寫代碼或設計用戶接口時,問這些問題是很常見的:應用程序通常是基於用戶的背景情況建立的,且你想基於每個用戶標準體現(保障)功能。因此,對於我們考慮應用程序安全的最自然的方式是基於當前用戶。

Shiro 的 API 使用它的 Subject 概念從根本上代表了“當前用戶”的概念。

幾乎在所有的環境中,你可以通過下面的調用獲取當前正在執行的用戶:

 

 
 

 

使用 SecurityUtils.getSubject(),我們可以獲得當前正在執行的 Subject。Subject 是一個安全術語,它基本上的意思是

“當前正在執行的用戶的特定的安全視圖”。它並沒有被稱爲"User"是因爲"User"一詞通常和人類相關聯。在安全  界,術語"Subject"可以表示爲人類,而且可是第三方進程,cron  job,daemon   account,或其他類似的東西。它僅僅意味着“該事物目前正與軟件交互”。對於大多數的意圖和目的,你可以把 Subject 看成是 Shiro 的"User"概念。

 

getSubject()在一個獨立的應用程序中調用,可以返回一個在應用程序特定位置的基於用戶數據的   Subject,並且在服務器環境中(例如,Web 應用程序),它獲取的 Subject 是基於關聯了當前線程或傳入請求的用戶數據的。

 

現在你擁有了一個 Subject,你能拿它來做什麼?

如果你想在應用程序的當前會話中使事物對於用戶可用,你可以獲得他們的會話:

 

 
 

 

Session 是一個 Shiro 的特定實例,它提供了大部分你經常與 HttpSessoins 使用的東西,除了一些額外的好處以及一個巨大的區別:它不需要一個 HTTP 環境!

 

如果在一個 Web 應用程序內部部署,默認的 Session 將會是基於 HttpSession 的。但,在一個非 Web 環境中,像這個簡單的教程應用程序,Shiro 將會默認自動地使用它的 Enterprise Session Management。這意味着你會使用相同的

API 在你的應用程序,在任何層,不論部署環境!這開闢了應用程序的新世界,由於任何需要會話的應用程序不必再被強制使用 HttpSession 或 EJB Stateful Session Beans。並且,任何客戶端技術現在能夠共享會話數據。

 

因此,現在你能獲取一個 Subject 以及他們的 Session。如果他們被允許做某些事,如對角色和權限的檢查,像“檢查”真正有用的地方在哪呢?

 

嗯,我們只能爲一個已知的用戶做這些檢查。我們上面的 Subject 實例代表了當前用戶,但誰又是當前用戶?呃, 他們是匿名的——也就是說,直到直到他們至少登錄一次。那麼,讓我像下面這樣做:

 

 
 

這就是了!它再簡單不過了。

 

但如果他們的登錄嘗試失敗了會怎樣?你能夠捕獲各種具體的異常來告訴你到底發生了什麼,並允許你去處理並作出相應反應:

 

 
 

 

你能夠檢查到許多不同類型的異常,或拋出你自己的自定義條件的異常——Shiro 可能不提供的。請參見

AuthenticationException JavaDoc 獲取更多。

 

 
 

 

好了,到現在爲止,我們已經有了一個登錄用戶。我們還能做些什麼?

 

 
 

比方說,他們是是誰:

 

 

 
 

我們也可以測試他們是否有特定的角色:

 

 

 
 

我們還可以判斷他們是否有權限在一個確定類型的實體上進行操作:

 

 

 
 

當然,我們可以執行極其強大的實例級權限檢查——判斷用戶是否有能力訪問某一類型的特定實例的能力:

 

小菜一碟,對吧?

最後,當用戶完成了對應用程序的使用,他們可以註銷:

 

最後教程類

在添加上面的示例代碼後,下面是我們的最終 Tutorial 類文件。請隨意編輯和操作它,並按你喜歡的方式改變安全檢查(以及 INI 配置):

 

 
 

 

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 vlaue! [" + vlaue + "]");

}

 

//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);

}

}

 

 

 

 

Summary

希望此次推出的教程幫助您瞭解如何在一個基本的應用程序設置 Shiro 以及 Shiro 的主要設計理念,Subject 和

SecurityManager。

 

但這是一個相當簡單的應用程序。你可能已經問過你自己,“如果我不想使用 INI 用戶帳戶,而是要連接到一個更復雜的用戶數數據源,該怎麼辦呢?”。

 

要回答這個問題,需要對 Shiro 的架構和支持的配置機制有更深一些的理解。我們下面將涉及到 Shiro 的架構。

 

Apache Shiro 架構

 

Apache  Shiro  的設計目標是通過直觀和易於使用來簡化應用程序安全。Shiro  的核心設計體現了大多數人們是如何考慮應用程序安全的——在某些人(或某些事)與應用程序交互的背景下。

 

應用軟件通常是基於用戶背景情況設計的。也就是說,你將經常設計用戶接口或服務 API,基於一個用戶將要(或應該)如何與該軟件交互。例如,你可能會說,“如果用戶與我的應用程序交互的用戶已經登錄,我將顯示一個他們能夠點擊的按鈕來查看他們的帳戶信息。如果他們沒有登錄,我將顯示一個登錄按鈕。”

 

這個簡單的陳述表明應用程序很大程度上的編寫是爲了滿足用戶的要求和需要。即使該“用戶”是另一個軟件系統 而不是一個人類,你仍然得編寫代碼來響應行爲,基於當前與你的軟件進行交互的人或物。

 

Shiro  在它自己的設計中體現了這些概念。通過匹配那些對於軟件開發人員來說已經很直觀的東西,Apache  Shiro  幾乎在任何應用程序保持了直觀和易用性。

 

高級概述

在最高的概念層次,Shiro 的架構有 3 個主要的概念:Subject,SecurityManager 和 Realms。下面的關係圖是關於這些組件是如何交互的高級概述,而且我們將會在下面討論每一個概念:

 

 
 

 

 

  • Subject:在我們的教程中已經提到,Subject 實質上是一個當前執行用戶的特定的安全“視圖”。鑑於"User" 一詞通常意味着一個人,而一個 Subject 可以是一個人,但它還可以代表第三方服務,daemon account,cron job,或其他類似的任何東西——基本上是當前正與軟件進行交互的任何東西。

 

所有 Subject 實例都被綁定到(且這是必須的)一個 SecurityManager 上。當你與一個 Subject 交互時,那些交互作用轉化爲與 SecurityManager 交互的特定 subject 的交互作用。

 

  • SecurityManager:SecurityManager 是 Shiro 架構的心臟,並作爲一種“保護傘”對象來協調內部的安全組件共同構成一個對象圖。然而,一旦  SecurityManager  和它的內置對象圖已經配置給一個應用程序,那麼它單獨留下來,且應用程序開發人員幾乎使用他們所有的時間來處理 Subject API。

 

我們稍後會更詳細地討論 SecurityManager,但重要的是要認識到,當你正與一個 Subject 進行交互時,實質上是幕後的 SecurityManager 處理所有繁重的 Subject 安全操作。這反映在上面的基本流程圖。

 

  • Realms:Realms 擔當 Shiro 和你的應用程序的安全數據之間的“橋樑”或“連接器”。當它實際上與安全相關的數據如用來執行身份驗證(登錄)及授權(訪問控制)的用戶帳戶交互時,Shiro 從一個或多個爲應用程序配置的 Realm 中尋找許多這樣的東西。

 

在這個意義上說,Realm 本質上是一個特定安全的 DAO:它封裝了數據源的連接詳細信息,使 Shiro 所需的相關的數據可用。當配置 Shiro 時,你必須指定至少一個 Realm 用來進行身份驗證和/或授權。SecurityManager 可能配置多個 Realms,但至少有一個是必須的。

 

Shiro 提供了立即可用的 Realms 來連接一些安全數據源(即目錄),如 LDAP,關係數據庫(JDBC),文本配置源,像 INI 及屬性文件,以及更多。你可以插入你自己的 Realm 實現來代表自定義的數據源,如果默認地

Realm 不符合你的需求。

 

像其他內置組件一樣,Shiro SecurityManager 控制 Realms 是如何被用來獲取安全和身份數據來代表 Subject 實例的。

 

詳細架構

下圖展示了 Shiro 的核心架構概念,緊跟其後的是每個的簡短總結:

 

 
 

 

  • Subject(org.apache.shiro.subject.Subject)

當前與軟件進行交互的實體(用戶,第三方服務,cron job,等等)的安全特定“視圖”。

 

  • SecurityManager(org.apache.shiro.mgt.SecurityManager)

如上所述,SecurityManager 是 Shiro 架構的心臟。它基本上是一個“保護傘”對象,協調其管理的組件以確保它們能夠一起順利的工作。它還管理每個應用程序用戶的 Shiro 的視圖,因此它知道如何執行每個用戶的安全操作。

 

  • Authenticator(org.apache.shiro.authc.Authenticator)

 

Authenticator 是一個對執行及對用戶的身份驗證(登錄)嘗試負責的組件。當一個用戶嘗試登錄時,該邏輯被 Authenticator 執行。Authenticator 知道如何與一個或多個 Realm 協調來存儲相關的用戶/帳戶信息。從這些

Realm 中獲得的數據被用來驗證用戶的身份來保證用戶確實是他們所說的他們是誰。

 

    • Authentication Strategy(org.apache.shiro.authc.pam.AuthenticationStrategy)

如果不止一個 Realm 被配置,則 AuthenticationStrategy 將會協調這些 Realm 來決定身份認證嘗試成功或失敗下的條件(例如,如果一個 Realm 成功,而其他的均失敗,是否該嘗試成功? 是否所有的 Realm 必須成功?或只有第一個成功即可?)。

 

  • Authorizer(org.apache.shiro.authz.Authorizer)

Authorizer 是負責在應用程序中決定用戶的訪問控制的組件。它是一種最終判定用戶是否被允許做某事的機制。與 Authenticator 相似,Authorizer 也知道如何協調多個後臺數據源來訪問角色惡化權限信息。Authorizer 使用該信息來準確地決定用戶是否被允許執行給定的動作。

 

  • SessionManager(org.apache.shiro.session.SessionManager)

SessionManager 知道如何去創建及管理用戶 Session 生命週期來爲所有環境下的用戶提供一個強健的 Session 體驗。這在安全框架界是一個獨有的特色——Shiro 擁有能夠在任何環境下本地化管理用戶 Session 的能力, 即使沒有可用的 Web/Servlet 或 EJB 容器,它將會使用它內置的企業級會話管理來提供同樣的編程體驗。

SessionDAO 的存在允許任何數據源能夠在持久會話中使用。

 

    • SessionDAO(org.apache.shiro.session.mgt.eis.SessionDAO)

SesssionDAO 代表 SessionManager 執行 Session 持久化(CRUD)操作。這允許任何數據存儲被插入到會話管理的基礎之中。

 

  • CacheManager(org.apahce.shiro.cache.CacheManager)

CacheManager 創建並管理其他 Shiro 組件使用的 Cache 實例生命週期。因爲 Shiro 能夠訪問許多後臺數據源, 由於身份驗證,授權和會話管理,緩存在框架中一直是一流的架構功能,用來在同時使用這些數據源時提高 性能。任何現代開源和/或企業的緩存產品能夠被插入到 Shiro 來提供一個快速及高效的用戶體驗。

 

  • Cryptography(org.apache.shiro.crypto.*)

Cryptography 是對企業安全框架的一個很自然的補充。Shiro 的 crypto 包包含量易於使用和理解的cryptographic Ciphers,Hasher(又名  digests)以及不同的編碼器實現的代表。所有在這個包中的類都被精心地設計以易於使用和易於理解。任何使用  Java  的本地密碼支持的人都知道它可以是一個難以馴服的具有挑戰性的動物。Shiro 的 cryptoAPI 簡化了複雜的 Java 機制,並使加密對於普通人也易於使用。

 

  • Realms(org.apache.shiro.realm.Realm)

如上所述,Realms 在 Shiro 和你的應用程序的安全數據之間擔當“橋樑”或“連接器”。當它實際上與安全相關的數據如用來執行身份驗證(登錄)及授權(訪問控制)的用戶帳戶交互時,Shiro 從一個或多個爲應用程序配置的 Realm 中尋找許多這樣的東西。你可以按你的需要配置多個 Realm(通常一個數據源一個 Realm),且 Shiro 將爲身份驗證和授權對它們進行必要的協調。

 

The SecurityManager

因爲Shiro 的API 鼓勵一個以Subject 爲中心的編程方式,大多數應用程序開發人員很少,如果真有,與SecurityManager 直接進行交互(框架開發人員有時候會覺得它很有用)。即便如此,瞭解如何 SecurityManager 是如何工作的仍然是很重要的,尤其是在爲應用程序配置一個 SecurityManager 的時候。

 

設計

如前所述,應用程序的 SecurityManager 執行安全操作並管理所有應用程序用戶的狀態。在 Shiro 的默認

SecurityManager 實現中,這包括:

 

  • Authentication
  • Authorization
  • Session Management
  • Cache Management
  • Realm coordination
  • Event propagation
  • "Remember Me" Services
  • Subject creation
  • Logout

以及更多。

 

但這是許多功能來嘗試管理一個單一的組件。而且,使這些東西靈活而又可定製將會是非常困難的,如果一切都集中到一個單一的實現類。

 

爲了簡化配置並啓用靈活配置/可插性,Shiro      的實現都是高度模塊化設計——由於如此的模塊化,SecurityManager 實現(以及它的類層次結構)並沒有做很多事情。相反,SecurityManager 實現主要是作爲一個輕量級的“容器”組件,委託計劃所有的行爲到嵌套/包裹的組件。這種“包裝”的設計體現在上面的詳細構架圖。

 

雖然組件實際上執行邏輯,但 SecurityManager 實現知道何時以及如何協調組件來完成正確的行爲。

 

SecurityManager 實現和組件都是兼容 JavaBean 的,它允許你(或某個配置機制)通過標準的 JavaBean 的accessor/mutator 方法(get*/set*)輕鬆地自定義可拔插組件。這意味着 Shiro 的架構的組件性能夠把自定義行爲轉化爲非常容易的配置文件。

 

 
 

 

我們接下來將討論 Configuration。

 

Lend a hand with documentation

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

 

Apache Shiro 配置

 

Shiro 被設計成能夠在任何環境下工作,從最簡單的命令行應用程序到最大的的企業羣集應用。由於環境的多樣性, 使得許多配置機制適用於它的配置。本節僅討論只被 Shiro 核心所支持的配置機制。

 

 
 

 

編程 Configuration

創建一個 SecurityManger 並把它提供給應用程序的絕對的最簡單的方法是創建一個

org.apache.shiro.mgt.DefaultSecurityManager 並把它鏈到代碼中。例如:

 

 
 

 

令人驚訝的是,僅在 3 行代碼後,你馬上就擁有了一個適合許多應用環境的功能全面的 Shiro 環境。這是多麼的容易!?

 

SecurityManager 對象圖

正如在 Architecture 章節中討論的一樣,Shiro 的 SecurityManager 實現實質上是一個特定安全的嵌套組件中的模塊化對象圖。因爲它們也是兼容 JavaBean 的,你可以調用任何嵌套組件的 getter 和 setter 方法來配置 SecurityManager 以及它的內部對象圖。

 

例如,如果你想配置 SecurityManager 實例來使用自定義的 SessionDAO 來定製 Session Management,你可以通過嵌套的 SessionManager 的 setSessionDAO 方法直接設置 SessionDAO:

 

 
 

 

通過使用直接方法調用,你可以配置 SecurityManager 的對象圖的任何部分。

 

但是,程式化定製過於簡單,它並不能代表大多數現實世界的應用程序的理想配置。以下是程式化定製可能不適合你的幾點原因:

  • 它需要你瞭解和實例化一個直接實現。這將會更好,如果你不需要了解具體的實現和在哪裏可以找到它們。
  • 由於 Java 的類型安全性,你需要轉換通過 get*方法獲取的對象來得到它們具體的實現。如此多的轉換是醜陋的,冗長的,並使你和實現類緊密連接起來。
  • SecurityUtils.setSecurityManager 方法調用在一個 VM 靜態單例中實例化 SecurityManager 實例,VM 靜態單例在給許多應用程序帶來好處的同時,也會引發一些問題,如果多個啓用 Shiro 的應用程序在同一個 JVM 中運行。如果該實例是一個應用程序單例,而不是一個靜態內存引用就再好不過了。
  • 每當你想改變 Shiro 配置時,它需要你重新編譯你的應用程序。

 

然而,即使有這麼多警告,直接的編程操作方法在內存受限的環境中仍然是有價值的,如智能手機應用。若你的應用程序不在一個內存受限的環境下運行,你會發現基於文本的配置要更容易使用和閱讀。

 

INI Configuration

大多數應用程序反而從基於文本的配置受益,能夠獨立地修改源代碼,甚至讓那些不熟悉 Shiro 的 API 的人能夠更容易理解。

 

爲了確保一個基於文本的通用標準配置機制能夠在所有環境下以最小的第三方依賴工作,Shiro 支持 INI 格式來構建

SecurityManager 對象圖及其支持的組件。INI 易於閱讀,易於配置,且設置簡單,適合大多數應用程序。

 

Creating a SecurityManager from INI

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

 

SecurityManager from an INI resource

我們能夠從一個 INI 資源路徑創建 SecurityManager 實例。我們可以從文件系統,classpath,或分別擁有前綴 file:, classpath:,或 url:的 URL 中獲取資源。本例採用 Factory 提取 classpath 根目錄下的 shiro.ini 文件並返回 SecurityManager實例:

 

 
 

 

SecurityManager from an INI instance

如果你需要,INI 配置也可以通過 org.apache.shiro.config.Ini 類使用編程方式創建。Ini 類的功能與 JDK

java.util.Properties 類相似,但通過 section 名稱,它同時還支持分割。

例如:

 

 
 

 

現在,我們知道如何從 INI 配置構建出一個 SecurityManager 了,我們看看到底是如何定義一個 Shiro INI 配置的。

 

INI Sections

INI 基本上是一個文本配置,包含了由唯一命名的 section 組織的鍵/值對。鍵只是每個 section 唯一,而不是在整個配置中(與 JDK 屬性不同)。不過每個 section 都可以被看作單一的屬性定義。

 

註釋行能夠以散列字符(# - 也就是"hash","pound"或"number"符號)或分號(";")開始。以下是 Shiro 能夠理解的 section 例子:

 

 

[main]

[main] section 是你配置應用程序的 SecurityManager 實例及任何它的依賴組件(如 Realms)的地方。

 

配置對象實例,如 SecurityManager 或任何它的依賴組件,通過使用 INI 聽起來是一件困難的事情,我們僅能使用名稱/值對。但通過一點點約定和對對象圖的理解,你將會發現你可以做很多事情。Shiro 使用這些條件來簡單卻相當簡潔的配置機制。

 

我們往往喜歡把這種方法稱爲“窮人的”依賴注入,雖然沒有完全成熟的 Spring/Guice/JBoss XML 文件強大,但你將會發現它能夠完成許多事情卻沒有太多的複雜性。當然那些其他的配置機制也同樣可用,但它們不是使用 Shiro 所必需的。

 

爲了激起你的慾望,這裏有一個有效的[main]配置的例子。我們將在下面討論它的細節,但你可能會發現,僅憑直覺就能明白很多事情是怎麼回事:

 

 
 

 

定義一個對象

請思考下面[main] section 的摘要:

 

 
 

 

這一行實例化了 com.company.shiro.realm.MyRealm 類型的新的對象實例,並使該對象使用 myRealm 名稱,以供進一步的引用和配置。

 

如果該實例化的對象實現了 org.apache.shiro.util.Nameable 接口,則 Nameable.setName 方法將會在擁有該名字(在本例中是 myRealm)的對象上調用。

 

設置對象屬性

Primitive Values

簡單的原始屬性,可以通過使用等號來指定:

 

 
 

 

配置中的這些行轉化爲方法調用:

 

 
 

 

這怎麼可能呢? 它假定所有的對象都是兼容 Java Bean 的 POJO。

 

在後臺,當設置這些屬性時,Shiro 默認使用 Apache Commons BeanUtils 來完成所有繁重的任務。所以,儘管 INI 值是文本的,BeanUtils 知道如何去轉換字符串值到正確的原始類型,然後調用相應的 JavaBean 的 setter 方法。

 

Reference Values

如果你需要設置的值不是一個原始的,而是另一個對象呢?那麼,你可以使用美元符號($)來引用之前定義的實例。例如:

 

 
 

 

這簡單地定位通過名稱 sha256Matcher 來定位定義好的對象,然後使用 BeanUtils 在 myRealm 實例上設置該對象(通過調用 myRealm.setCredentialsMatcher(sha256Matcher)方法)。

 

Nested Properties

在 INI 配置行的等號左側使用點號,你可以遍歷對象圖來得到想設置的最終的對象/屬性。例如,這個配置行:

 

 
 

 

(通過 BeanUtils)轉譯成下面的邏輯:

 

 

對象圖的遍歷可以按需要來加大遍歷深度:object.property1.property2….propertyN.value = blah

 

 
 

 

Byte Array Values

因爲原始的字節數組本身不能使用文本格式,所以我們必須使用文本編碼的字節數組。能夠指定的值是一個 Base64 編碼的字符串(默認),後一個 16 進制編碼的字符串。默認是 Base64 是因爲 Base64 編碼只需較少的文本來表示值——它擁有一個較大的編碼表,意味着你的 token 都是較短的(幾個較好的文本配置)。

 

 
 

 

然而,如果你喜歡使用 16 進制編碼,你必須在字符串 token 前加上 0x("zero" "x")前綴:

 

 
 

 

Collection Properties

List,Set 和 Map 能夠被設定任何屬性——直接或作爲一個嵌套屬性。對於 Set 和 list 而言,只需指定一組由逗號分隔的值或對象的引用。

 

例如某些 SessionListener:

 

 
 

 

對於 Map,你指定一系列由逗號分隔的鍵-值對,每個鍵-值對通過冒號":"被限定:

 

 
 

 

在上面的例子中,被$object1 引用的對象將屬於在 map 中的字符串鍵值 key1,也就是,map.get("key1")返回 object1。你還可以依照鍵來使用其他對象:

 

 
 

 

注意事項

Order Matters

上面的 INI 格式和約定都非常便捷且易於理解,但它沒有其他基於 text/XML 的配置機制強大。在使用上面的機制時

最重要的問題是理解順序問題!

 

 
 

 

最重要的實例

任何對象能夠被配置中後來新定義的實例覆蓋。所以舉例來說,第二個 myRealm 定義將會覆蓋第一個:

 

 
 

 

這將導致 myRealm 成爲一個 com.company.security.DatebaseRealm 實例,且之前的實例將永不會被使用(同時被垃圾回收)。

 

Default SecurityManager

你可能已經注意到在上面的完整實例中,SecurityManager 實例的類並沒有定義,我們僅在右邊設定一個嵌套屬性:

 

 
 

 

這是因爲 securityManager 實例是一個特殊的實例——它已經爲你實例化並準備好使用,所以你不需要知道用來實例化的具體 SecurityManager 實例類。

 

當然,如果你確實想指定你自己的實例,你可以只定義你自己的實現,正如上面的"Overriding     Instances"一節中所規定的:

 

 
 

 

當然,這基本上沒有必要——Shiro 的 SecurityManager 實現是可定製化的,且通常可以與任何必要的事物進行配置。如果你真想這麼做的話,你得想問問你自己(或用戶列表)。

 

[users]

[users] section 允許你定義一組靜態的用戶帳戶。這在大部分擁有少數用戶帳戶或用戶帳戶不需要在運行時被動態地創建的環境下是很有用的。以下是一個例子:

 

 
 
 
 

 

 

Line Format

在[users] section 中的每行都必須保證是下面的格式:

 

username = password, roleName1, roleName2, …, roleNameN

 

  • 在等號左邊的值是用戶名
  • 在等號右邊的第一個值是用戶的祕密。密碼是必須的。
  • 任何在密碼後用逗號分隔的值都是分配給該用戶的角色名。角色名是可選的。

 

加密Passwords

如果你不想[users] section 中密碼是純文本的,你可以使用你喜愛的散列算法(MD5,Sha1,Sha256,等等)來進行加密,並使用生產的字符串作爲密碼值。默認情況下,密碼字符串是 16 進制編碼,但可以使用 Base64 編碼代替 16 進制編碼來配置(見下面)。

 

一旦你指定了文本密碼散列值,你得告訴 Shiro 這些都是加密的。你可以通過配置在[main] section 中隱式地創建

iniRealm 來使用合適的 CredentialsMatcher 實現來對應到你所指定的哈希算法:

 

 
 

 

你可以像任何其他對象一樣在 CredentialsMatcher 上配置任何屬性,以反映你哈希策略,例如,指定 salting 是否被使用或需要執行多少次哈希迭代。請參見 org.apache.shiro.authc.credential.HashedCredentialsMatcher 的 JavaDoc 來更好的理解哈希策略,如果它們對你可能有用的話。

 

例如,如果你的用戶密碼字符串是 Base64 編碼而不是默認的 16 進制,你可以指定說明:

 

 

[roles]

[roles] section 允許你把定義在[users] section 中的角色與權限關聯起來。另外,這在大部分擁有少數用戶帳戶或用戶帳戶不需要在運行時被動態地創建的環境下是很有用的。以下是一個例子:

 

 
 

 

Line Format

在[roles] section 中每個配置行必須定義一個映射以下格式的角色到權限的鍵/值:

 

rolename = permissionDefinition1, permissionDefinition2, … , permissionDefinitionN

 

permissionDefinition 是一個任意的字符串,但大多數人將會使用符合

org.apache.shiro.authz.permission.WildcardPermission 格式的字符串,爲了易用性和靈活性。請參見 Permissions 文檔獲取更多關於 Permissions 及你如何從它們受益的信息。

 

 
 
 
 

 

 

[urls]

該 section 及它的選項在 Web 章節被描述。

 

幫助整理文件

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

Authentication身份驗證

 

 
 

Authentication 是指身份驗證的過程——即證明一個用戶實際上是不是他們所說的他們是誰。對於一個用戶證明自己的身份來說,他們需要提供一些身份識別信息,以及某些你的系統能夠理解和信任的身份證明。

 

這是通過提交用戶的身份和憑證給 Shiro,以判斷它們是否和應用程序預期的相匹配。

    • Principals(身份)是 Subject 的‘identifying attributes(標識屬性)’。Principals(身份)可以是任何能夠證明

Subject 的東西,如名,姓氏,用戶名,社會保險號(相當於我們的身份證號碼)等等。當然像姓氏這樣的用來標識 Subject 並不是很好,因此,最好的用來進行身份驗證的 Principals(身份)是對應用程序來說應該是獨一無二的——通常是用戶名或電子郵件地址。

 

 
 

 

    • Credentials(憑證)通常是隻被 Subject 知道的祕密值,它用來作爲一種起支持作用的證據,此證據事實上包含着所謂的身份證明。一些常見 credentials(憑證)的例子有密碼,生物特徵數據如指紋和視網膜掃描, 以及 X.509 證書。

principal/credential 配對最常見的例子是用戶名和密碼。用戶名是所聲稱的身份,密碼是匹配所聲稱的身份的證明。如果提交的密碼與應用程序期望的相匹配,應用程序可以很大程度上假設用戶真的是他們說的他們是誰,因爲其他人都應該不知道同樣的密碼。

 

Authenticating Subjects(驗證 Subjects)

驗證 Subjects 的過程中,可以有效地分解成三個不同的步驟:

    1. 收集 Subjects 提交的 Principals(身份)和 Credentials(憑證);
    2. 提交 Principals(身份)和 Credentials(憑證)進行身份驗證;
    3. 如果提交成功,則允許訪問,否則重新進行身份驗證或者阻止訪問。

Step 1:收集 Subject Principals(身份)Credentials(憑證)

 

 
 

 

 

在這種特殊情況下,我們使用 UsernamePasswordToken 來支持最常見的用戶名/密碼的身份驗證方法。這是

Shiro 的org.apache.shiro.authc.AuthenticationToken 的接口,是Shiro 代表提交的Principals(身份)和Credentials()的身份驗證系統所使用的基本接口的一個實現。

 

這裏需要重要注意的是 Shiro 不關心你是如何獲取此信息的:也許獲得的數據是由用戶提交的一個 HTML 表單, 或者是從 HTTP 頭中捕獲,或者它是從一個 Swing 或 Flex GUI 密碼錶單,或者通過命令行參數。從終端用戶收集信息的過程與 Shiro 的 Authentication Token 概念是不掛鉤的。

 

你可以創建和實例化你喜歡的 Authentication Token 實例——它是與協議無關的。

 

這個例子也說明了我們希望 Shiro 爲身份驗證的嘗試執行“記住我”的服務。這將確保在以後的日子,如果用戶返回到應用程序時,Shiro 能夠記得用戶的身份。我們將在後面的章節討論 Remember Me 服務。

 

Step 2:提交 Subject Principals(身份)Credentials(憑證)

在 Principals(身份)和 Credentials(憑證)被收集以及被實例化爲 AuthenticationToken 實例後,我們需要提交這個

token 給 Shiro 來執行真正的身份驗證嘗試:

 

 

在捕獲到當前執行的 Subject 後,我們獲得一個單一的 login 方法調用,並將之前獲得的 AuthenticationToken

實例傳遞給它。

通過調用 login 方法,有效地體現了身份驗證嘗試。

Step3:處理成功或失敗

如果 login 方法返回平靜地,就是它——我們所做的一切!該 Subject 已通過驗證。應用程序線程可以不受干擾地繼續下去,而且所有進一步對 SecurityUtils.getSubject()的調用將返回認證後的 Subject 實例,同時任何對

subject.isAuthenticated()的調用將返回 true。

 

但是如果登錄嘗試失敗會發生什麼呢?例如,如果終端用戶提供了不正確的密碼,或這訪問系統的次數太多, 亦或是他們的帳戶被鎖定?

 

Shiro 擁有豐富的運行時 AuthenticationException 層次結構,可以指出嘗試失敗的確切原因。你可以用一個

try/catch 塊將 login 方法包圍起來,然後捕捉任何你期望的異常並進行相應的反應。例如:

 

 
 

 

 

如果現有的異常類不符合您的要求,可以自定義 AuthenticationExceptions 來代表具體的異常情況。

 

 
 

 

Remembered vs. Authenticated(記住我對比認證)

如上面的例子所示,Shiro 支持除了普通的登錄過程的所有“記住我”的概念。此時值得指出的是,Shiro 對記住我的 Subject 和通過驗證的 Subject 作了精確的區分:

    • Remembered(記住我):一個記住我的 Subject 不是匿名的,而且有一個已知的身份 ID(也就是subject.getPrincipals()是非空的)。但是這個被記住的身份 ID 是在之前的 session 中被認證的。如果subject.isRemembered()返回 true,則 Subject 被認爲是被記住的。
    • Authenticated(已認證):一個已認證的 Subject 是指在當前 Session 中被成功地驗證過了(也就是說,login 方法被調用並且沒有拋出異常)。如果 subject.isAuthenticated()返回 true 則認爲 Subject 已通過驗證。

 

 
 

 

爲何有這樣的區別?

“身份驗證”這個詞有很強的證明的意思在裏面。也就是說,有一個預期保證 Subject 已經證明他們是他們所說的誰。

當用戶只記得之前與應用的交互時,認證將不復存在:被記住的身份 ID 使系統明白這個用戶可能是誰,但在現實中沒有辦法絕對保證被記住的 Subject 代表期望的用戶。一旦 Subject 通過驗證,它們將不再僅僅被認爲是被記住的,由於它們的身份已經在當前 session 中被證實。

儘管應用程序的許多部分仍然能夠執行基於被記住身份 ID 的用戶特定邏輯,像自定義視圖,但它絕不應該執行高度敏感的操作,除非用戶通過執行一個成功的認證嘗試來合法地驗證自己的身份。

例如,一個檢查來判斷一個 Subject 可以訪問財務信息應該幾乎總是取決於 isAuthenticated(),而不是

isRemembered(),以保證一個預期和核實的身份。

一個說明的例子

下面是一個相當普遍的情況,有助於說明 Remembered 和 Authenticated 之間區別的重要性。

比方說,你正在訪問   Amazon.com。你已經登錄成功並添加了幾本書到你的購物車。但你心煩意亂地跑出去開會,卻忘了註銷。會議結束後,已經到了回家的時候,於是你離開了辦公室。

 

第二天你工作的時候,你意識到你沒有完成購買,於是你返回到 amazon.com。這一次,Amazon“記得”你是誰,給出了你的歡迎頁面,並仍然爲你提供一些個性化的建議書籍。對 Amazon 而言,subject.isRemembered() 將返回 true。

 

但是,當你嘗試訪問你的帳戶來更新你的信用卡信息爲你書付賬時會發生什麼呢?儘管 Amazon“記住”你(isRemembered() = = true),它不能保證你就是實際上的你(例如,也許一個同事正在使用你的計算機)。

 

所以,在你能夠執行像更新信用卡信息等敏感行爲之前,Amazon 將強制讓你登錄,使它們能夠保證你的身份。在登錄後,你的身份已經被覈實,同時對 Amazon 而言,isAuthenticated()現在返回是 true。

 

這種情況在許多類型的應用中發生的是如此的頻繁,所以這些功能被內置在 Shiro 中,這樣你就能利用它來爲你的應用服務了。現在,無論你使用的是 isRemembered()還是 isAuthenticated()來定製你的視圖和工作流都由你來決定,但 Shiro 將維持這一基本情況以防你需要它。

註銷

進行身份驗證的反面是釋放所有已知的的識別狀態。當 Subject 完成了與應用程序的交互後,你可以調用

subject.logout()來釋放所有的識別信息:

 

 

當你調用 logout,任何現有的 Session 都將會失效,而且任何身份都將會失去關聯(例如,在 Web 應用程序中,RememberMe cookie 也將被刪除)。

在 Subject 註銷後,該 Subject 的實例被再次認爲是匿名的,當然,除了 Web 應用程序,它還可以重新用於 login

如果需要的話。

 

 

 

 

驗證順序

到現在爲止,我們只瞭解瞭如何從應用程序代碼中驗證一個 Subject。現在我們將涉及到當一個認證嘗試出現時 Shiro 內部會發什麼。

 

 
 

我們採用了 Architecture 那一章的體系結構圖,並只留下與 Authentication 有關的組件突出顯示。每個數字代表認證嘗試中的一個步驟:

 

Step 1:應用程序代碼調用 Subject.login 方法,傳遞創建好的包含終端用戶的 Principals(身份)和 Credentials()的 AuthenticationToken 實例。

Step 2:Subject 實例,通常是 DelegatingSubject(或子類)委託應用程序的 SecurityManager 通過調用

securityManager.login(token)開始真正的驗證工作。

Step3:SubjectManager 作爲一個基本的“保護傘”的組成部分,接收 token 以及簡單地委託給內部的

Authenticator 實例通過調用 authenticator.authenticate(token)。這通常是一個 ModularRealmAuthenticator 實例, 支持在身份驗證中協調一個或多個 Realm 實例。ModularRealmAuthenticator 本質上爲 Apache Shiro 提供了PAM-style 範式(其中在 PAM 術語中每個 Realm 都是一個'module')。

 

Step 4:如果應用程序中配置了一個以上的 Realm,ModularRealmAuthenticator 實例將利用配置好的

AuthenticationStrategy 來啓動 Multi-Realm 認證嘗試。在 Realms 被身份驗證調用之前,期間和以後,

AuthenticationStrategy 被調用使其能夠對每個 Realm 的結果作出反應。我們馬上就會涉及到

AuthenticationStrategies。

 

 

Step 5:每個配置的 Realm 用來幫助看它是否支持提交的 AuthenticationToken。如果支持,那麼支持 Realm 的

getAuthenticationInfo 方法將會伴隨着提交的 token 被調用。getAuthenticationInfo 方法有效地代表一個特定

Realm 的單一的身份驗證嘗試。我們將在不久涉及到 Realm 驗證行爲。

Authenticator(認證器)

如前所述,Shiro SecurityManager 的實現默認使用一個 ModularRealmAuthenticator 實例。

ModularRealmAuthenticator 同樣支持單一的 Realm 以及那些多個 Realm 的應用。

在一個單一的 Realm 應用中,ModularRealmAuthenticator 將直接調用這個單一的 Realm。如果有兩個或兩個以上的 Realm 配置,它將使用 AuthenticationStrategy 實例來調整這些嘗試如何出現。下面我們將介紹

AuthenticationStrategies。

如果你想配置 SecurityManager 通過一個自定義的 Authenticator 實現,你可以在 shiro.ini 中做,例如:

 

 

 

儘管在實踐中,ModularRealmAuthenticator 很可能是適用於大多數需要的。

 

AuthenticationStrategy

當一個應用程序配置了兩個或兩個以上的 Realm 時,ModularRealmAuthenticator 依靠內部的

AuthenticationStrategy 組件來確定這些認證嘗試的成功或失敗條件。

例如,如果只有一個 Realm 驗證一個 AuthenticationToken 成功,但所有其他的都失敗,這被認爲是成功的身份驗證嘗試嗎?或者必須所有的 Realm 驗證成功才被認爲樣子成功嗎?或者,如果一個 Realm 驗證成功,是否有必要進一步徵詢其他 Realm?AuthenticationStrategy 基於程序需要作出合適的決定。

AuthenticationStrategy 是一個無狀態的組件,它在身份驗證嘗試中被詢問 4 次(這 4 次交互所需的任何必要的狀態將被作爲方法參數):

  1. 在任何 Realm 被調用之前被詢問;
  2. 在一個單獨的 Realm 的 getAuthenticationInfo 方法被調用之前立即被詢問;
  3. 在一個單獨的 Realm 的 getAuthenticationInfo 方法被調用之後立即被詢問;
  4. 在所有的 Realm 被調用後詢問。

另外,AuthenticationStrategy 負責從每一個成功的 Realm 彙總結果並將它們“捆綁”到一個單一的

AuthenticationInfo 再現。這最後彙總的 AuthenticationInfo 實例就是從 Authenticator 實例返回的值以及 Shiro

所用來代表 Subject 的最終身份 ID 的值(即 Principals(身份))。

 

 

Shiro 有 3 個具體的 AuthenticationStrategy 實現:

 

 

則整體嘗試失敗。

FirstSuccessfulStrategy

只有第一個成功地驗證的 Realm 返回的信息將被使用。所有進一步的 Realm 將被忽略。如果沒有一個驗證成功,則整體嘗試失敗。

AllSucessfulStrategy

爲了整體的嘗試成功,所有配置的 Realm 必須驗證成功。如果沒有一個驗證成功,則整體嘗試失敗。

ModularRealmAuthenticator 默認的是 AtLeastOneSuccessfulStrategy 實現,因爲這是最常所需的方案。然而,如果你願意的話,你可以配置一個不同的方案:

shiro.ini

[main]

authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy seurityManager.authenticator.authenticationStrategy = $authcStrategy

...

Custom AuthenticationStrategy

如果你想創建你自己的 AuthenticationStrategy 來實現你自己,你可以使用

org.apache.shiro.authc.pam.AbstractAuthenticationStrategy 作爲出發點。AbstractAuthenticationStrategy 類自動實現“捆綁”/聚集行爲,從每一個 Realm 合併結果到一個單一的 AuthenticationInfo 實例。

 

Realm 的驗證順序

需要指出非常重要的一點是,ModularRealmAuthenticator 將與 Realm 實例以迭代的順序進行交互。

在 SecurityManager 中已經配置好了ModularRealmAuthenticator 對 Realm 實例的訪問。當執行一個認證嘗試時, 它將會遍歷該集合,並對每一個支持提交 AuthenticationToken 的 Realm 調用 Realm 的 getAuthenticationInfo 方法。

Implicit ordering(隱式排列)

當使用 Shiro 的 INI 配置文件格式時,你應該配置 Realm 處理 AuthenticationToken 的順序,你想要的順序。例如,在 shiro.ini 中,Realm 將會以它們在 INI 文件中定義好的順序被請求到。也就是說,對於下面的 shiro.ini 示例:

SecurityManager 根據這三個 Realm 來配置,在認證嘗試期間,blahRealm,fooRealm 和 barRealm 將按照上面

那個順序調用。

這基本是相同的效果,就像下面的這一行定義:

 

使用這種方法,你並不需要設置 SecurityManager 的 Realm 屬性——每個定義好的 realm 將會自動地被添加到

realm 的屬性。

Explicit Ordering(顯示排列)

如果你想明確地定義 Realm 的交互順序,忽略它們是如何定義的,你可以設置 securityManager 的屬性作爲一個明確的集合屬性。例如,如果使用上面的定義,但你想 blahRealm 最後被請求而不是第一個:

 

 

 

 

Realm 驗證

本章涵蓋了 Shiro 的主要工作流程,解釋了一個認證嘗試是如何產生的。內部工作流,指在驗證過程中一個單一的 realm 被訪問時發生的事情(也就是'Step 5'之上),已經包含在 Realm 章節的 Realm Authentication 部分。

 

Lend a hand with documentation

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助 Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而提高了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

 

Authorization授權

Apache Shiro Authorization

 

 
 

 

 

授權,又稱作爲訪問控制,是對資源的訪問管理的過程。換句話說,控制誰有權限在應用程序中做什麼。

授權檢查的例子是:該用戶是否被允許訪問這個網頁,編輯此數據,查看此按鈕,或打印到這臺打印機?這些都是決定哪些是用戶能夠訪問的。

 

授權的要素

 

Apache   Shiro   中的權限代表着安全政策中最基礎的元素。它們從根本上作出了對行爲的聲明,並明確表示可以在應用程序中做什麼。一個格式良好的權限聲明基本上描述了資源以及當 Subject 與這些資源進行交互時可能出現的行爲。

權限語句的一些例子:

  • 打開一個文件;
  • 查看'/user/list'網頁;
  • 打印文檔;
  • 刪除用戶'jsmith'。

大多數資源將支持典型的   CRUD(創建,讀取,更新,刪除)操作,但任何對特定資源有意義的行爲都是可以的。基本的概念是,最小的許可聲明是基於資源和行爲的。

在查看權限時,最重要的可能是認識到許可聲明沒有誰可以執行代表行爲的表現形式。它們僅僅只是在一個應用程序中能做什麼的聲明語句。

定義(用戶)被允許做什麼(權限),是一個以某種方式分配給用戶權限的運用。這通常是由應用程序的數據模型

來完成的,並且不同應用程序間變化很大。

例如,權限能夠被集合到一個角色中,該角色可以與一個或多個用戶對象相關聯。或者某些應用程序可以有一組可以被分配一個角色的用戶和組,傳遞的關聯意味着該組中的所有用戶都隱式地獲得了該角色的權限。

如何授予用戶權限可以有很多變化——應用程序決定如何基於應用的要求來建模。我們稍後將討論 Shiro 是如何確定一個 Subject 是否被允許做些什麼。

權限粒度

以上所有權限例子詳細說明了在某一資源類型(入口,文件,客戶等等)的行爲(打開,閱讀,刪除等等)。在某些情況下,它們甚至可以指定非常細粒度的實例級的行爲——例如,“刪除”(行爲)用戶名爲"jsmith"的“用戶”

(資源類型)。在 Shiro,你有能力來定義這些聲明能夠達到的精確粒度。

我們將在 Shiro 的 Permission 文檔中更加詳細地討論權限粒度和許可聲明的“等級”。

角色

角色是一個命名的實體,通常代表一組行爲或職責。這些行爲演化爲你在一個軟件應用中能或者不能做的事情。角色通常是分配給用戶帳戶的,因此,通過分配,用戶能夠“做”的事情可以歸屬於各種角色。

有兩種有效類型的角色,並且 Shiro 支持這兩個概念:

  • 隱式角色:大多數人使用的角色作爲一個隱式的構造:你的應用程序僅僅基於一個角色名就蘊含了一組行爲

(也就是權限)。有了隱式角色,在軟件級別上沒有說“角色 X 被允許執行行爲 A,B 和 C”。行爲已被一個單獨的名字所蘊含。

  • 顯式角色:一個顯式角色本質上是一個實際許可聲明的命名集合。在這種形式下,應用程序(以及 Shiro)確

切地知道有沒有一個特定的角色意味着什麼。因爲它是已知能不能夠被執行的確切行爲,沒有猜測或暗示一個特定的角色能或不能做什麼。

Shiro 團隊提倡使用權限和顯式角色,而不是陳舊的隱式方法。你將會擁有更多的控制應用程序的安全經驗。

 

Users(用戶)

用戶實質上是指與應用程序有關的人。然而正如我們已經討論的,Subject 纔是 Shiro 的“用戶”概念。

允許用戶(Subjects)在你的應用程序中執行某些操作,是通過與他們的角色相關聯或授予直接的權限。你的應用    程序的數據模型定義了 Subject 是如何被允許做某事或不的。

例如,在你的數據模型中,也許你有一個實際的 User 類,而且你直接分配權限給 User 實例。或者,你也許只分配權限給角色,然後分配角色給用戶,通過關聯,用戶延伸“有”的權限分配給自己的角色。或者你用"Group"的概  念來代替這些東西。這些都隨便你——使用什麼使得你的程序有意義。

你的數據模型定義授權究竟是如和工作的。Shiro 依靠 Realm 來實現轉換你的數據模型使其細節關聯到一種 Shiro 能夠理解的格式。

我們一會兒將討論 Realms 是如何做到這一點的。

 

 

Authorizing Subjects(授權的 Subjects)

在 Shiro 中執行授權可以有 3 種方式:

  • 編寫代碼——你可以在你的 Java 代碼中用像 if 和 else 塊的結構執行授權檢查。
  • JDK 的註解——你可以添加授權註解給你的 Java 方法。
  • JSP/GSP 標籤庫——你可以控制基於角色和權限的 JSP 或者 GSP 頁面輸出。

Programmatic Authorization(編程授權)

也許最簡單和最常見的方式來執行授權是直接以編程方式與當前 Subject 實例交互。

Role-Based Authorization(基於角色的授權)

如果你想進行基於簡單/傳統的隱式角色名來控制訪問,你可以執行角色檢查:

Role checks(角色檢查)

如果你只是簡單的想檢查當前的 Subject 是否擁有一個角色,你可以在 Subject 實例上調用變體的 hasRole*方法。例如,判斷一個 Subject 是否擁有一個特別的(單一的)角色,你可以通過調用 subject.hasRole 方法,並作出相應的反應:

有幾個面向角色的 Subject 方法可以調用,一起取決於你的需要:

 

 

 

 

 

Role Assertions(角色斷言)

 

另一種方法通過檢查布爾值來判斷 Subject 是否擁有一個角色,你可以簡單地斷言它們有一個預期的角色在邏輯被執行之前。如果 Subject 沒有預期的角色,AuthorizationException 將會被拋出。如果它們有預期的角色,斷言將悄悄地執行,並且邏輯將如預期般繼續。

例如:

 

 

 

 

通過使用 hasRole*方法的一個好處就是代碼可以變得清潔,由於你不需要創建你自己的 AuthorizationException 如果當前的 Subject 不符合預期條件(如果你不想的話)。

有幾個你能調用的面向角色的 Subject 斷言方法,取決於你的需要:

 

 

 

 

 

Permission-Based Authorization(基於權限的授權)

如前所述在我們所概述的角色,往往一個更好的方式執行訪問控制是通過基於權限的授權。基於權限的授權,由於它與你的應用程序的原始功能(以及應用程序核心資源上的行爲)緊密的關聯在一起的,基於權限的授權源代碼會在你的功能改變時改變,而不是在安全政策改變時改變。這意味着代碼很少會被影響對比相似的基於角色的授權的代碼。

Permission Checks(權限檢查)

如果你想進行檢查,看一個 Subject 是否被允許做某事,你可以調用各種 isPermitted*方法的變種。檢查權限主要有兩個方式——基於對象的權限實例或代表權限的字符串。

Object-based Permission Checks(基於對象的權限檢查)

執行權限檢查的一個可行方法是實例化 org.apache.shiro.authz.Permission 接口的一個實例,並把它傳遞給接收權限實例的*isPermitted 方法。

例如,請考慮以下情況:在辦公室有一臺打印機,具有唯一標識符 laserjet4400n。我們的軟件需要檢查當前用戶是否被允許在該打印機上打印文檔在我們允許他們按下“打印”按鈕之前。上述情況的權限檢查可以明確地像這樣表達:

在這個例子中,我也看到了一個非常強大的實例級的訪問控制檢查的例子——限制行爲的能力是基於個人的數據實

例。

基於對象的權限是很有用的,如果:

  • 你想編譯時類型安全
  • 你想保證權限被描述和使用是正確的
  • 你想顯式控制許可解析邏輯(被稱作許可蘊含的邏輯,基於權限接口的 implies 方法)是如何執行的。

 

  • 你想保證權限反映到應用程序資源是準確的(例如,也許權限類可以在能夠基於項目的域模型的項目編譯時自動生成)。

有幾個你能調用的面向權限的 Subject 方法,取決於你的需要:

 

 

 

 

 

 

String-based permission checks(基於字符串的權限檢查)

基於對象的權限可以是很有用的(編譯時類型安全,保證行爲,定製蘊含邏輯等),它們有時對應用程序來說會感到有點“笨手笨腳”的。另一種方法是使用正常的字符串來表示權限實例。

例如,基於上面的打印權限的例子上,我們可以重新制訂與之前檢查相同的基於字符串的權限檢查:

 

 

 

 

 

這個例子還顯示了相同的實例級權限檢查,但權限的重要組成部分——打印機(資源類型),打印(行爲),以及

laserjet4400n(實例 ID)——都用一個字符串表示。

這個特別的例子顯示了一個特殊冒號分隔的格式,它由 Shiro 默認的

org.apache.shiro.authz.permission.WildcardPermission 實現來定義,其中大多數人會找到適合自己的格式。也就是說,上面的代碼塊(大部分)是下面代碼的簡化:

WildcardPermission token 規定和構造操作的格式在 Shiro 的 Permission 文檔中被深入的涉及到。

除了上面的字符串默認的  WildcardPermission  格式,你可以創建和使用自己的字符串格式如果你喜歡的話。我們將在 Realm Authorization 這一節討論如何去做。

基於字符串的權限是很有幫助的,由於你不必被迫實現一個接口,而且簡單的字符串易於閱讀。其缺點是,你不具備類型安全,如果你需要更爲複雜的行爲將超出了字符串所能代表的範圍,你就得實現你自己的基於權限接口的權限對象。在實際中,大部分的 Shiro 終端用戶爲了簡潔選擇基於字符串的方式,但最終你應用程序的需求會決定哪一個更好。

像基於對象的權限檢查方法一樣,也有字符串的變體來支持基於字符串的權限檢查:

 

isPermitted(String… perms)

返回一個與方法參數中目錄一致的 isPermitted 結果的數組。有性能的提高如果許多字符串權限檢查需要被執行(例如,當自定義一個複雜的視圖)。

isPermittedAll(String… perms)

返回 true 如果該 Subject 被允許所有指定的字符串權限,否則返回 false。

Permission Assertions(權限斷言)

作爲檢查一個布爾值來判斷 Subject 是被允許做某事的一種替代,你可以在邏輯被執行之前簡單地斷言他們是否擁有預期的權限。如果該 Subject 是不被允許,AuthorizationException 異常將會被拋出。如果他們如預期的被允許,斷言將安靜地執行,邏輯也將如預期般繼續。

例如:

 

 

 

 

 

或者,同樣的檢查,使用字符串權限:

 

 

 

 

 

通過使用 hasRole*方法的一個好處就是代碼可以變得清潔,由於你不需要創建你自己的 AuthorizationException 如果當前的 Subject 不符合預期條件(如果你不想的話)。

有幾個你能調用的面向權限的 Subject 斷言方法,取決於你的需要:

 

 

 

 

 

 

 

 

 

 

 

 

Annotation-based Authorization(基於註解的授權)

除了 Subject API 的調用,Shiro 提供 Java 5+註解的集合,如果你喜歡以註解爲基礎的授權控制。

 

configuration(配置)

在你可以使用 Java 註釋之前,你需要在你的應用程序中啓用 AOP 支持。雖然現在有許多不同的 AOP 框架,但不幸的是,在應用程序中沒有一個使用 AOP 的標準。

對於 AspectJ 而言,你你可以回顧我們的 AspectJ 的示例程序。對於 Spring 應用而言,你可以看看我們的 Spirng 集成文檔。

The RequiresAuthentication annotation(RequiresAuthentication 註解)

RequiresAuthentication 註解要求當前 Subject 已經在當前的 session 中被驗證通過才能被註解的類/實例/方法訪問或調用。

例如:

 

 

 

 

 

這通常等同於接下來的基於 Subject 的邏輯:

 

 

 

 

 

 

The RequiresGuest annotation(RequiresGuest 註解)

RequiresGuest 註解要求當前的 Subject 是一個"guest",也就是說,他們必須是在之前的 session 中沒有被驗證或記住才能被註解的類/實例/方法訪問或調用。

例如:

 

 

 

 

這通常等同於接下來的基於 Subject 的邏輯:

 

 

 

 

 

 

 

 

The RequiresPermissions annotation(RequiresPermissions 註解)

RequiresPermissions 註解要求當前的 Subject 被允許一個或多個權限,以便執行註解的方法。

 

例如:

 

 

 

 

 

這通常等同於接下來的基於 Subject 的邏輯:

 

 

 

 

 

 

 

The RequiresRoles annotation(RequiresRoles 註解)

RequiresRoles 註解要求當前的 Subject 擁有所有指定的角色。如果他們沒有,則該方法將不會被執行,而且

AuthorizationException 異常將會被拋出。例如:

這通常等同於接下來的基於 Subject 的邏輯:

 

 

 

 

 

 

 

The RequiresUser annotation(RequiresUser 註解)

RequiresUser 註解需要當前的 Subject 是一個應用程序用戶才能被註解的類/實例/方法訪問或調用。一個“應用程序用戶”被定義爲一個擁有已知身份,或在當前 session 中由於通過驗證被確認,或者在之前 session 中的'RememberMe' 服務被記住。

 

這通常等同於接下來的基於 Subject 的邏輯:

 

 

 

 

 

 

 

 

 

JSP TagLib Authorization(JSP TagLib 授權)

Shiro 提供了一個用於控制 JSP/GSP 頁面輸出的基於 Subject 狀態的標籤庫。這些包含在 Web 章節的 JSP/GSP 標籤庫部分。

 

Authorization Sequence(授權順序)

現在我們已經知道了基於當前 Subject 上如何執行授權,讓我們看看當授權調用時,Shiro 內部會發生什麼。

我們採用了 Architecture 那一章的體系結構圖,並只留下與 authorization 有關的組件突出顯示。每個數字代表授權過程中的一個步驟:

 

Step 1:應用程序或框架代碼調用任何 Subject 的 hasRole*, checkRole*, isPermitted*, 或者 checkPermission*方法的變體,傳遞任何所需的權限或角色代表。

Step 2:Subject 的實例,通常是 DelegatingSubjec(t  或子類)代表應用程序的 SecurityManager 通過調用 securityManager

的幾乎各自相同的 hasRole*, checkRole*, isPermitted*,或 checkPermission*方法的變體(SecurityManager 實現

org.apache.shiro.authz.Authorizer 接口,他定義了所有 Subject 具體的授權方法)。

Step 3:SecurityManager,作爲一個基本的“保護傘”組件,接替/代表它內部的 org.apache.shiro.authz.Authorizer

實例通過調用 authorizer 各自的 hasRole*, checkRole*, isPermitted*, 或者 checkPermissions*方法。默認情況下,

authorizer 實例是一個 ModularRealmAuthorizer 實例,它支持協調任何授權操作過程中的一個或多個 Realm 實例。Step 4:每個配置好的 Realm 被檢查是否實現了相同的 Authorizer 接口。如果是,Realm 各自的 hasRole*, checkRole*, isPermitted*,或 checkPermission*方法將被調用。

 

ModularRealmAuthorizer

如前所述,Shiro SecurityManager 的實現默認是使用一個 ModularRealmAuthorizer 實例。ModularRealmAuthorizer 同樣支持單一的 Realm,以及那些與多個 Realm 的應用。

對於任何授權操作,ModularRealmAuthorizer 將遍歷其內部的 Realm 集合,並按迭代順序與每一個進行交互。每個

Realm 的交互功能如下:

  1. 如果 Realm 自己實現了 Authorizer 接口,它的各個 Authorizer 方法(hasRole*, checkRole*, isPermitted*, 或

checkPermission*)將被調用。

    1. 如果 Realm 的方法導致異常,該異常將會以 AuthorizationException 的形式傳遞給調用者。這將短路授權過程,同時任何剩餘的 Realm 將不會被該授權操作所訪問。
    2. 如果該 Realm 的方法是一個返回布爾值的 hasRole*或者 isPermitted*的變體,並且該返回值爲 true,真值將會立即被返回,同時任何剩餘的 Realm 都將被短路。這種行爲作爲提高性能的一種存在,如果該行爲被一個 Realm 允許,這意味着該 Subject 也是被允許的。這有利於安全政策,每一處都是默認被禁止的情況下,一切都明確允許的,這是安全政策最安全的類型。
  1. 如果 Realm 不實現 Authorizer 接口,它會被忽略。

Realm Authorization Order(Realm 的授權順序)

需要重要指出的是,尤其是身份驗證,ModularRealmAuthorizer 將以迭代順序與 Realm 實例進行交互。ModularRealmAuthorizer 根據 SecurityManager 的配置獲得對 Realm 實例的訪問。當執行授權操作時,它會遍歷該集合,同時對於每一個自己實現 Authorizer 接口的 Realm,調用 Realm 各自的 Authorizer 方法(如 hasRole*, checkRole*, isPermitted*,或 checkPermission*)。

Configuring a global PermissionResolver(配置全局的 PermissionResolver)

當執行基於字符串的權限檢查是,大多數 Shiro 的默認 Realm 實現首先將該字符串轉換成一個實際的 Permission 實例,在執行權限 implication 邏輯之前。

這是因爲 Permission 是基於 implication 邏輯評估的,而不是直接的 equality 檢查(見 Permission 文檔有關更多implication 和 equality 的對比)。Implication 邏輯對比通過字符串比較能夠更好的在代碼中體現。因此,大多數 Realm 需要轉換,或者將提交的權限字符串解析成相應的代表權限的實例。

爲了幫助這種轉換,Shiro 支持 PermissionResolver 的概念。大多數 Shiro Realm 的實現使用一個 PermissionResolver

以支持他們的基於字符串權限的 Authorizer 接口方法的實現:當其中一種方法在 Realm 上被調用是,它將使用

PermissionResolver 把該字符串轉換成一個權限實例,並用這種方式來執行檢查。

所有 Shiro Realm 的實現默認是內部的 WildcardPermissionResolver,它採用 Shiro 的 WildcardPermission 字符串格式。如過你想創建自己的 PermissionResolver 的實現,也許是爲了支持自己的權限字符串語法,而且你想要所有配置的

Realm 實例支持該語法,你可以將你的 PermissionResolver 設置爲全局的,這樣所有的 Realm 能夠用一個配置。例如,在 shiro.ini 中:

 

 
 

 

 

 
 

如果你想使用全局的 PermissionResolver 或你不想被 PermissionResolverAware 接口所困擾,你可以隨時顯式地配置

一個擁有 PermissionResolver 實例的 Realm(假設有一個兼容 JavaBean 的 setPermissionResolver 的方法):

 

Configuring a global RolePermissionResolver(配置全局的 RolePermissionResolver)

與 PermissionResolver 在概念上相似,RolePermissionResolver 有能力代表需要的權限實例,通過一個 Realm 執行權限檢查。

然而,與一個 RolePermissionResolver 的關鍵區別是輸入的字符串是一個角色名,而不是一個權限字符串。

RolePermissionResolver 能夠在 Realm 內部使用,當需要將一個角色名轉換成一組具體的權限實例時。這是一個特別有用的特徵用來支持舊的或不靈活的,可能沒有權限概念的數據源。

例如,許多 LDAP 目錄存儲了角色名(或組名),但是不支持關聯角色名到具體的權限由於他們沒有“權限”的概念。一個基於 Shiro 的應用程序能夠使用存儲在 LDAP 的角色名,還能實現一個 RolePermissionResolver 來轉化 LDAP 名到一組顯式的權限來執行首選的顯式的訪問控制。權限關聯將會被存儲在另一個數據倉庫,可能是一個本地數據庫。

由於這種轉換角色名到權限的概念非常特定於應用程序,Shiro 默認 Realm 的實現並不使用它們。

然而,如果你想創建你自己的 RolePermissionResolver,並有多個你想配置的 Realm 的實現,你可以將你的

RolePermissionResolver 設置爲全局的,這樣所有的 Realm 都能夠用一個配置。

 

 

 

 

 

 

 

 

如果你不希望使用全局的 RolePermissionResolver 或你不想被 RolePermissionResolverAware 接口所困擾,你可以隨時顯式地配置一個擁有 RolePermissionResolver 實例的 Realm(假設有一個兼容 JavaBean 的 setRolePermissionResolver 的方法):

Custom Authorizer(自定義授權者)

如果你的應用程序使用多個 realm 來執行授權,並且 ModularRealmAuthorizer 默認基於簡單的迭代,短路授權行爲不符合你的要求,你很有可能想創建一個自定義的授權者,並配置相應的 SecurityManager。

例如,在 shiro.ini 中:

 

理解 Apache Shiro中的權限

 

Shiro 將權限定義爲一個規定了明確行爲或活動的聲明。這是一個在應用程序中的原始功能語句,僅此而已。權限是在安全策略中最低級別的構造,且它們明確地定義了應用程序只能做“什麼”。

它們從不描述“誰”能夠執行這些動作。一些權限的例子:

  • 打開文件
  • 瀏覽'/user/list'頁面
  • 打印文檔
  • 刪除'jsmith'用戶

 

規定“誰”(用戶)允許做“什麼”(權限)在某種程度上是分配用權限的一種習慣做法。這始終是通過應用程序數據模型來完成的,並且在不同應用程序之間差異很大。

 

例如,權限可以組合到一個角色中,且該角色能夠關聯一個或多個用戶對象。或者某些應用程序能夠擁有一組用戶, 且這個組可以被分配一個角色,通過傳遞的關聯,意味着所有在該組的用戶隱式地獲得了該角色的權限。

 

如何授予用戶權限可以有很多變化——應用程序基於應用需求來決定如何使其模型化。

 

通配符的權限

上述的權限例子,“打開文件”、“瀏覽'/user/list'頁面”等都是有效的權限語句。然而,將這些解釋爲自然語言字符串,並判斷用戶是否被允許執行該行爲在計算上是非常困難的。

 

因此,爲了使用易於處理且仍然可讀的權限語句,Shiro 提供了強大而直觀的語法,我們稱之爲 WildcardPermission。

 

簡單的使用

假設你想要保護到貴公司打印機的訪問,使得某些人能夠打印到特定的打印機,而其他人可以查詢當前有哪些工作在隊列中。

 

一個極其簡單的方法是授予用戶"queryPrinter"權限。然後你可以檢查用戶是否具有 queryPrinter 權限通過調用:

 

 

這(很大程度)相當於

 

但遠不只這些。

簡單的權限字符串可能在簡單的應用程序中工作的很好,但它需要你擁有像"printPrinter","queryPrinter",

"managePrinter"等權限。你還可以通過使用通配符授予用戶"*"權限(賦予此權限構造它的名字),這意味着他們在整個應用程序中擁有了所有的權限。

 

但使用這種方法不能說用戶擁有“所有打印機權限”。由於這個原因,Wildcard Permissions(通配符權限)支持多層次的權限管理。

 

多個部分

通配符權限支持多層次或部件(parts)的概念。例如,你可以通過授予用戶權限來調整之前那個簡單的例子。

 

 

在這個例子中的冒號是一個特殊字符,它用來分隔權限字符串的下一部件。

 

在該例中,第一部分是權限被操作的領域(打印機),第二部分是被執行的操作(查詢)。上面其他的例子將被改爲:

 

 
 

 

對於能夠使用的部件是沒有數量限制的,因此它取決於你的想象,依據你可能在你的應用程序中使用的方法。

 

多個值

每個部件能夠保護多個值。因此,除了授予用戶"printer:print"和"printer:query"權限外,你可以簡單地授予他們一個:

 

 
 

 

它能夠賦予用戶 print 和 query 打印機的能力。由於他們被授予了這兩個操作,你可以通過調用下面的語句來判斷用戶是否有能力查詢打印機:

 

該語句將會返回 true。

 

所有的值

如果你想在一個特定的部件給某一用戶授予所有的值呢?這將是比手動列出每個值更爲方便的事情。同樣,基於通配符的話,我也可以做到這一點。若打印機域有 3 個可能的操作(query,print 和 manage),可以像下面這樣:

 

簡單點變成這樣:

 

 

然後,任何對"printer:XXX"的權限檢查都將返回 true。以這種方式使用的通配符比明確地列出操作具有更好的尺度, 如果你不久爲應用程序增加了一個新的操作,你不需要更新使用通配符那部分的權限。

 

最後,在一個通配符權限字符串中的任何部分使用通配符 token 也是可以的。例如,如果你想對某個用戶在所有領域(不僅僅是打印機)授予"view"權限,你可以這樣做:

 

這樣任何對"foo:view"的權限檢查都將返回 true。

 

實例級權限控制

另一種常見的通配符權限用法是塑造實例級的訪問控制列表。在這種情況下,你使用三個部件——第一個是域,第二個是操作,第三個是被付諸實施的實例。

 

因此,像下面的例子:

 

 

第一個定義了查詢擁有 ID lp7200 的打印機的行爲。第二條權限定義了打印到擁有 ID epsoncolor 的打印機的行爲。如果你授予這些權限給用戶,那麼他們能夠在特定的實例上執行特定的行爲。然後你可以在代碼中做一個檢查:

 

這是體現權限的一個極爲有效的方法。但同樣,爲所有的打印機定義多個實例 ID 能很好的擴展,尤其是當新的打印機添加到系統的時候。你可以使用通配符來代替:

 

這個做到了擴展,因爲它同時涵蓋了任何新的打印機。你甚至可以運行訪問所有打印機上的所有操作:

 

 

或在一臺打印機上的所有操作:

 

 

或甚至特定的操作:

 

 

"*"通配符,","子部件分離器可用於權限的任何部分。

 

缺少的部件

最後要注意的是權限分配:缺少的部件意味着用戶可以訪問所有與之匹配的值,換句話說,

 

 

等價於

 

 

並且

 

 

等價於

 

 

然而,你只能從字符串的結尾處省略部件,因此這樣的:

 

並不等價於

 

 

Checking Permissions

雖然權限分配使用通配符較爲方便且具有擴展性("printer:print:*" = print to any printer),但在運行時的權限檢查應該始終基於大多數具體的權限字符串。

 

例如,如果用戶有一個用戶界面,他們想打印一份文檔到 lp7200 打印機,你應該通過執行這段代碼來檢查用戶是否被允許這樣做:

 

 
 

 

這種檢查非常具體和明確地反映了用戶在那一時刻試圖做的事情。

然而,下面這個運行是檢查是不爲理想的:

 

 

 

爲什麼?因爲第二個例子表明“對於下面的代碼塊的執行,你必須能夠打印到任何打印機”。但請記住"pinter:print"  是等價於"priner:print:*"的!

 

因此,這是一個不正確的檢查。如果當前用戶不具備打印到任何打印機的能力,僅僅只有打印到 lp7200 和 epsoncolor 的能力,該怎麼辦呢?那麼上面的第二個例子也絕不允許他們打印到 lp7200 打印機,即使他們已被賦予了相應的能力!

 

因此,經驗法則是在執行權限檢查時,儘可能使用權限字符串。當然,上面的第二塊可能是在應用程序中別處的一個有效檢查,如果你真的想要執行該代碼塊,如果用戶被允許打印到任何打印機(令人懷疑的,但有可能)。你的應用程序將決定檢查哪些有意義,但一般情況下,越具體越好。

 

含義, 不平等

爲什麼運行時權限檢查應該儘可能的具體,但權限分配可以較爲普通?這是因爲權限檢查是通過蘊含的邏輯來判斷的——而不是通過相等檢查。

 

也就是說,如果一個用戶被分配了 user:*權限,這意味着該用戶可以執行 user:view 操作。"user:*"字符串明顯不等於"user:view",但前者包含了後者。"user:*"描述了"user:view"所定義的功能的一個超集。

 

爲了支持蘊含規則,所有的權限都被翻譯到實現 org.apache.shiro.authz.Permission  接口的的對象實例中。這是以便蘊含邏輯能夠在運行時執行,且蘊含邏輯通常比一個簡單的字符串相等檢查更爲複雜。所有在本文檔中描述的通配符行爲實際上是由 org.apache.shiro.authz.permission.WildcardPermission 類實現的。下面是更多的一些通過蘊含邏輯訪問的通配符權限字符串:

 

同時蘊含着刪除一個用戶的能力:

 

 

同樣地,

 

 

同時蘊含着更新 ID 爲 12345 的用戶帳戶的能力:

 

 

而且

 

 

蘊含着打印到任何打印機的能力

 

 

性能考慮

權限檢查比簡單的相等比較要複雜得多,因此運行時的蘊含邏輯必須執行每個分配的權限。當使用像上面展示的權限字符串時,你正在隱式地使用 Shiro 默認的 WildcardPermission,它能夠執行必要的蘊含邏輯。

 

Shiro 對 Realm 實現的默認行爲是,對於每一個權限驗證(例如,調用 subject.isPermitted),所有分配給該用戶的權限(在他們的組,角色中,或直接分配給他們)需要爲蘊含邏輯進行單獨的檢查。Shiro 通過首次成功檢查立即返回來“短路”該進程以提高性能,但它不是一顆銀彈。

 

這通常是極快的,當用戶,角色和權限緩存在內存中且使用了一個合適的 CacheManager 時,在 Shiro 不支持的 Realm

實現中。只要知道使用此默認行爲,當權限分配給用戶或他們的角色或組增加時,執行檢查的時間一定會增加。

 

如果一個 Realm 的實現者有一個更爲高效的方式來檢查權限並執行蘊含邏輯,尤其它如果是基於應用程序數據模型的,他們應該實現它作爲 Realm isPermitted* 方法實現的一部分。默認的 Realm/WildcardPermission 存在的支持覆蓋了大多數用例的 80~90%,但它可能不是在運行時擁有大量權限需要存儲或檢查的應用程序的最佳解決方案。

 

幫忙整理文件

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

 

Apache Shiro Realms

 

Realm 是一個能夠訪問應用程序特定的安全數據(如用戶、角色及權限)的組件。Realm 將應用程序特定的數據轉換成一種 Shiro 能夠理解的格式,這樣 Shiro 能夠提供一個單一的易理解的 Subject 編程 API,無論有多少數據源存在或你應用程序特定的數據是怎樣的。

 

Realm  通常和數據源是一對一的對應關係,如關係數據庫,LDAP  目錄,文件系統,或其他類似資源。因此,Realm 接口的實現使用數據源特定的 API 來展示授權數據(角色,權限等),如 JDBC,文件 IO,Hibernate 或 JPA,或其他數據訪問 API。

 

Realm 實質上就是一個特定安全的 DAO

因爲這些數據源大多通常存儲身份驗證數據(如密碼的憑證)以及授權數據(如角色或權限),每個 Shiro Realm

能夠執行身份驗證和授權操作。

 

Realm configuration

如果使用 Shiro 的 INI 配置文件,你能夠自定義及引用 Realm,就像在[main]項中的任何其他對象一樣,但它們在

securityManager 中採用兩種方法之一進行配置:顯式或隱式。

 

顯示賦值

基於迄今的 INI 配置知識,這是一個顯示的配置方法。在定義一個或多個 Realm 後,你將它們作爲 securityManager

對象的集合屬性。

例如:

fooRealm = com.company.foo.Realm barRealm = com.company.another.Realm bazRealm = com.company.baz.Realm

 

securityManager.realms = $fooRealm, $barRealm, $bazRealm

 

顯式分配是確定的——你控制具體使用哪一個 Realm 及它們用於身份驗證和授權的順序。Realm 順序的作用在

Authentication 章的 Authentication Sequence 節進行了詳細的介紹。

 

隱式賦值

 Preferred(不推薦)

這種方法可能引發意想不到的行爲,如果你改變 realm 定義的順序的話。建議你避免使用此方法,並使用顯式分配,

它擁有確定的行爲。該功能很可能在未來的 Shiro 版本中被廢棄或移除。

 

如果出於某些原因你不想顯式地配置 securityManager.realms 的屬性,你可以允許 Shiro 檢測所有配置好的 realm 並直接將它們指派給 securityManager。

使用這種方法,realm 將會按照它們預先定義好的順序來指派給 securityManager 實例。也就是說,對於下面的 shiro.ini 示例:

blahRealm = com.company.blah.Realm

fooRealm = com.company.foo.Realm barRealm = com.company.another.Realm

 

# no securityManager.realms assignment here

 

基本上和下面這一行具有相同的效果:

securityManager.realms = $blahRealm, $fooRealm, $barRealm

 

然而,實現隱式分配,只是 realm 定義的順序直接影響到了它們在身份驗證和授權嘗試中的訪問順序。如果你改變它們定義的順序,你將改變主要的 Authenticaor 的 Authentication Sequence 是如何起作用的。

 

由於這個原因,以及保證明確的行爲,我們推薦使用顯式分配而不是隱式分配。

 

Realm Authentication

當你理解了 Shiro 的主要 Authentication 工作流後,瞭解在一個授權嘗試中當 Authenticator 與 Realm 交互時到底發生了什麼是很重要的。

 

Supporting AuthenticationTokens

在 authentication sequence 中已經提到,當在 Realm 被訪問來執行一個授權嘗試之前,它的 supports 方法被調用。如果返回值爲 true,則只有這樣它的 getAuthenticationInfo(token)方法纔會被調用。

 

通常 realm 會檢查提交的 token 的類型(接口或類)來判斷它是否能夠處理它。例如,一個能夠處理生物數據的 realm

可能就一點也不理解 UsernamePasswordTokens,這樣它將從 supports 方法返回 false。

 

處理支持 AuthenticationTokens

若 Realm 支持一個提交的 AuthenticationToken,那麼 Authenticator 將會調用該 Realm 的 getAuthenticationInfo(token)

方法。這有效地代表了一個與 Realm 的後備數據源的授權嘗試。該方法按以下方法進行:

  1. 爲主要的識別信息(帳戶識別信息)檢查 token。
  2. 基於 principal 在數據源中尋找相吻合的帳戶數據。
  3. 確保 token 支持的 credentials 匹配那些存儲在數據源的。
  4. 若 credentials 匹配,返回一個封裝了 Shiro 能夠理解的帳戶數據格式的 AuthenticationInfo 實例。
  5. 若 credentials 不匹配,則拋出 AuthenticationException 異常。

 

這是對所有 Realm getAuthenticationInfo 實現的最高級別的工作流。在此方法中,Realm  可以自由地做任何它們想做的,如記錄在審計日誌的嘗試,更新數據記錄,或任何其他可以對該數據存儲的身份驗證嘗試有意義的東西。

 

唯一需要的東西就是,如果 credentials 匹配給予的 principal(s),那麼返回一個非空的 AuthenticationInfo 實例來代表來自於該數據源的 Subject 帳戶信息。

 

節省時間

直接實現 Realm 接口可能導致時間消耗及錯誤。大多數人民選擇 AuthorizingRealm 抽象類的子類而不是從頭開始。這個類實現了常用的 authentication 及 authorization 工作流來節省你的時間和精力。

 

憑證匹配

在上面的 realmauthentication 工作流中,Realm 不得不驗證 Subject 提交的 credentials(如,,密碼)必須匹配存儲在數據存儲中的 credentials。如果匹配,則被認爲身份驗證成功,同時系統還必須驗證終端用戶的身份。

 

Realm 憑證匹配

這是每個 Realm 的責任,去匹配提交的 credentials 和那些存儲在 Realm 後備數據存儲中的 credentials,而不是Authenticator 的責任。每個 Realm 擁有有關私人信息的 credentials 格式,存儲及能夠執行詳細的 credentials 匹配, 然而 Authenticator 只是一個普通的工作量組件。

 

credentials 的匹配過程在所有應用程序中幾乎一樣,通常不一樣的是進行比較的數據。爲了確保該過程是可插入及可定製的如果需要的話,AuthenticatingRealm 及它的子類支持 CredentialsMatcher 來執行 credentials 對比的概念。

 

在發現帳戶數據後,它以及提交的 AuthenticationToken 用來代表一個 CredentialsMatcher 來判斷所提交的是否匹配所存儲的。

 

Shiro 擁有某些可以讓你立即使用的 CredentialsMatcher 實現,如 SimpleCredenticalsMatcher 和

HashedCredentialsMatcher,但如果你想爲自定義的邏輯配置一個自定義的實現,你可以像下面一樣直接做:

 

Realm myRealm = new com.company.shiro.realm.MyRealm();

CredentialsMatcher customMatcher = new com.company.shiro.realm.CustomCredentialsMatcher(); myRealm.setCredentialsMatcher(customMatcher);

 

或者,如果使用 Shiro 的 INI 配置文件:

 

[main]

customMatcher = com.company.shiro.realm.CustomCredentialsMatcher myRealm = com.company.shiro.realm.MyRealm myRealm.credentialsMatcher = $customMatcher

...

 

簡單的平等檢測

所有 Shiro 立即可用的 Realm 的實現默認使用 SimpleCredentialsMatcher。SimpleCredentialsMatcher 執行一個普通的直接平等檢查,關於存儲的帳戶 credentials 與在 AuthenticationToken 所提交的之間的檢查。

 

例如,若一個 UsernamePasswordToken 被提交後,則 SimpleCredentialsMatcher 驗證該密碼實際上是否與存儲在數據庫中的密碼相同。

 

SimpleCredentialsMatcher    不僅僅爲字符串執行直接相等比較。它能夠處理大多數常用的字節碼,像字符串,字符數組,字節數組,文件及輸入流。請參考它的 JavaDoc 獲取更多。

 

Hashing 的 憑證

並非是存儲 credentials 在其原始的 form 及執行原始/普通的比較,一個更安全的方式存儲終端用戶的 credential(s  如,密碼)是在存儲它們到數據存儲之前將它們單向散列化。

 

這確保終端用戶的 credentials 絕不會以原始的 form 存儲,而且沒人會知道原始值。這是一個比純文本或原始比較更爲安全的機制,同時所有關注安全的應用程序應該較非哈希化的存儲更爲喜歡。

 

爲了支持這些首選的加密哈希策略,Shiro 提供了 HashedCredentialsMatcher 的實現配置在 realm 上而不是上述

SimpleCredentialsMatcher。

哈希 credentials 及 salting 和多個哈希迭代的好處超出了該 Realm 文檔的範圍,但絕對要閱讀

HashedCredentialsMatcher 的 JavaDoc,其中將詳細介紹這些細節。

 

Hashing 和相應的匹配器

那麼,你如何很容易地配置一個啓用 Shiro 的應用程序呢?

 

Shiro 提供了多個 HashedCredentialsMatcher 子類實現。你必須在你的 realm 中配置指定的實現來匹配你 hash 化你用戶 credentials 時使用的哈希算法。

 

例如,假設你的應用程序爲身份驗證使用用戶名/密碼對。由於上文所述的哈希憑據的好處,假設當你創建一個用戶帳戶時,你想使用 SHA-256 算法單向散列用戶的密碼。你將哈希用戶輸入的純文本密碼並保持該值:

 

import org.apache.shiro.crypto.hash.Sha256Hash;

import org.apache.shiro.crypto.RandomNumberGenerator; import org.apache.shiro.crypto.SecureRandomNumberGenerator;

 

//We'll use a Random Number Generator to generate salts. This

//is much more secure than using a username as a salt or not

//

//Note that a normal app would reference an attribute rather

//than create a new RNG every time:

RandomNumberGenerator rng = new SecureRandomNumberGenerator(); Object salt = rng.nextBytes();

 

//now hash the plain-text password with the random salt and multiple

//iterations and then Base64-encode the value (requires less space than Hex):

String hashedPasswordBase64 = new Sha256Hash(plainTextPassword, salt, 1024).toBase64();

 

User user =new User(username, hashedPasswordBase64);

//save the salt with the new account . The HashedCredentialsMatcher

//will need it later when handling login attempts: user.setPasswordSalt(salt); userDAO.create(user);

 

既然你使用 SHA-256 散列你用戶的密碼,你需要告訴 Shiro 使用合適的 HashedCredentialsMatcher 以匹配你的哈希參數選擇。在這個例子中,我們創建了一個隨機的 salt 並執行 1024 次哈希迭代,爲了強大的安全性(請參見

HashedCredentialsMatcher 的 JavaDoc 獲取原因)。這裏是完成這項工作的 Shiro INI 配置:

 

[main]

credentialsMatcher =  org.apache.shiro.authc.credential.Sha256CredentialsMatcher # base64 encoding, not hex in this example: credentialsMatcher.storedCredentialsHexEncoded = false credentialsMatcher.hashIterations = 1024

# This next property is only needed in Shiro 1.0. Remove it in 1.1 and later: credentialsMatcher.hashSalted =true

 

myRealm = com.company…… myRealm.credentialsMatcher = $credentialsMathcer

...

 

SaltedAuthenticationInfo

爲了確保這一工程,最後要做的事情是,你的 Realm  實現必須返回一個 SaltedAuthenticationInfo 實例而不是一個普通的 AuthenticationInfo 實例。SaltedAuthenticationInfo 接口確保在你創建用戶帳戶(如,user.setPasswordSalt(Salt); call

above)時使用的 salt 能夠被 HashedCredentialsMatcher 引用.

 

HashedCredentialsMatcher 需要該 salt 爲了能夠在提交的 AuthenticationToken 上執行相同的哈希技術來判斷該 token 是否匹配你保存在數據存儲中的東西。因此,如果你爲用戶密碼使用 salting(而且你應該這樣做!!!),確保你的 Realm 實現能夠通過返回的 SaltedAuthenticationInfo 實例代表密碼。

 

禁用驗證

 

如果出於某些原因,你不想用 Realm 對數據源執行身份驗證(也許是由於你只想 Realm 執行授權),你可以徹底地禁用 Realm 對身份驗證的支持通過從 Realm 的 support 方法返回 false。然後你的 realm 在身份驗證嘗試中永遠不會被訪問到。

 

當然,至少需要一個能夠支持 AuthenticationTokens 且已配置的 Realm,如果你想驗證 Subjects。

 

Realm Authorization

TBD(待定)

 

幫忙整理文檔

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

 

Session Management

 

Apache Shiro 提供安全框架界獨一無二的東西:一個完整的企業級 Session  解決方案,從最簡單的命令行及智能手機應用到最大的集羣企業 Web 應用程序。

 

這對許多應用有着很大的影響——直到 Shiro 出現,如果你需要 session 支持,你需要部署你的應用程序到 Web 容器或使用 EJB 有狀態會話 Bean。Shiro 的 Session 支持比這兩種機制的使用和管理更爲簡單,而且它在適用於任何程序,不論容器。

 

即使你在一個 Servlet 或 EJB 容器中部署你的應用程序,仍然有令人信服的理由來使用 Shiro 的 Session 支持而不是容器的。下面是一個 Shiro 的 Session 支持的最可取的功能列表:

 

特點

  • POJO/J2SE based(IoC friendly) - Shiro 的一切(包括所有 Session 和 Session Management 方面)都是基於接口和

POJO 實現。這可以讓你輕鬆地配置所有擁有任何 JavaBeans 兼容配置格式(如 JSON,YAML,Spring XML 或類似的機制)的會話組件。你也可以輕鬆地擴展 Shiro 的組件或編寫你自己所需的來完全自定義 session

management。

  • Easy Custom Session Storage - 因爲 Shiro 的 Session 對象是基於 POJO 的,會話數據可以很容易地存儲在任意數量的數據源。這允許你自定義你的應用程序會話數據的確切位置——例如,文件系統,聯網的分佈式緩存, 關係數據庫,或專有的數據存儲。
  • Container-Independent Clustering! - Shiro 的會話可以很容易地聚集通過使用任何隨手可用的網絡緩存產品,像Ehcache + Terracotta,Coherence,GigaSpaces,等等。這意味着你可以爲 Shiro 配置會話羣集一次且僅一次, 無論你部署到什麼容器中,你的會話將以相同的方式聚集。不需要容器的具體配置!
  • Heterogeneous Client Access - 與 EJB 或 web 會話不同,Shiro 會話可以被各種客戶端技術“共享”。例如,一個桌面應用程序可以“看到”和“共享”同一個被使用的物理會話通過在 Web 應用程序中的同一用戶。我們不知道除了 Shiro 以外的其他框架能夠支持這一點。
  • Event Listeners  -  事件監聽器允許你在會話生命週期監聽生命週期事件。你可以偵聽這些事件和對自定義應用程序的行爲作出反應——例如,更新用戶記錄當他們的會話過期時。
  • Host Address Retention - Shiro Sessions 從會話發起地方保留 IP 地址或主機名。這允許你確定用戶所在,並作出相應的反應(通常是在 IP 分配確定的企業內部網絡環境)。

 

  • Inactivity/Expiration Support - 由於不活動導致會話過期如預期的那樣,但它們可以延續很久通過 touch()方法來保持它們“活着”,如果你希望的話。這在 RIA(富互聯網應用)環境非常有用,用戶可能會使用桌面應用程序,但可能不會經常與服務器進行通信,但該服務器的會話不應過期。
  • Transparent Web Use - Shiro 的網絡支持,充分地實現和支持關於 Sessions(HttpSession 接口和它的所有相關的 API)的 Servlet2.5 規範.這意味着你可以使用在現有 Web 應用程序中使用 Shiro 會話,並且你不需要改變任何現有的 Web 代碼。
  • Can be used for SSO - 由於 Shiro 會話是基於 POJO 的,它們可以很容易地存儲在任何數據源,而且它們可以跨程序“共享”如果需要的話。我們稱之爲"poor man's SSO",並它可以用來提供簡單的登錄體驗,由於共享的會話能夠保留身份驗證狀態。

 

Using Sessions

幾乎與所有其他在 Shiro 中的東西一樣,你通過與當前執行的 Subject 交互來獲取 Session: Subject currentUser = SecurityUtils.getSubject();

Session session = currentUser.getSession(); session.setAttribute("someKey", someValue);

 

subject.getSession()方法是調用 currentUser.getSubject(true)的快捷方式。

 

對於那些熟悉 HttpServletRequest API 的,Subject.getSession(boolean create)方法與HttpServletRequest.getSession(boolean create)方法有着異曲同工之效。

  • 如果該 Subject 已經擁有一個 Session,則 boolean 參數被忽略且 Session 被立即返回。
  • 如果該 Subject 還沒有一個 Session 且 create 參數爲 true,則創建一個新的會話並返回該會話。
  • 如果該 Subject 還沒有一個 Session 且 create 參數爲 false,則不會創建新的會話且返回 null。

 

任何應用程序

getSession 要求能夠在任何應用程序工作,甚至是非 Web 應用程序。

當開發框架代碼來確保一個 Session 沒有被創建是沒有必要的時候,subject.getSession(false)可以起到很好的作用。

 

當你獲取了一個 Subject 的 Session 後,你可以用它來做許多事情,像設置或取得 attribute,設置其超時時間,以及更多。請參見 Session 的 JavaDoc 來了解一個單獨的會話能夠做什麼。

 

The SessionManager

SessionManager,名如其意,在應用程序中爲所有的 subject 管理 Session——

創建,刪除,inactivity(失效)及驗證,等等。如同其他在 Shiro 中的核心結構組件一樣,SessionManager 也是一個由

SecurityManager 維護的頂級組件。

 

默認的 SecurityManger 實現是默認使用立即可用的 DefaultSessionManager。DefaultSessionManager 的實現提供一個應用程序所需的所有企業級會話管理,如 Session 驗證,orphan cleanup,等等。這可以在任何應用程序中使用。

 

Web 應用程序

Web 應用程序使用不同 SessionManager 實現。請參見 Web 文檔獲取 web-specific Session Management 信息。

像其他被 SecurityManager 管理的組件一樣,SessionManager 可以通過 JavaBean 風格的 getter/setter 方法在所有 Shiro

默認 SecurityManager 實現(getSessionManager()/setSessionManager())上獲取或設置值。或者例如,如果在使用

shiro.ini 配置:

 

 
 

 

 

但從頭開始創建一個 SessionManager 是一個複雜的任務且是大多數人不想親自做的事情。Shiro 的立即可用的

SessionManager 實現是高度可定製的和可配置的,並滿足大多數的需要。本文檔的其餘部分假定你將使用 Shiro 的默認 SessionManager 實現,當覆蓋配置選項時。但請注意,你基本上可以創建或插入任何你想要的東西。

 

Session 超時時間

默認地,Shiro 的 SessionManager 實現默認是 30 分鐘會話超時。也就是說,如果任何 Session 創建後閒置(未被使用,它的上次訪問時間未被更新)的時間超過了 30 分鐘,那麼該 Session 就被認爲是過期的,且不允許再被使用。

 

你可以設置 SessionManager 默認實現的 globalSessionTimeout 屬性來爲所有的會話定義默認的超時時間。例如,如果你想超時時間是一個小時而不是 30 分鐘:

 

 

 
 

 

Per-Session 超時時間

上面的 globalSessionTimeout 值默認是爲新建的 Session 使用的。你可以在每一個會話的基礎上控制超時時間通過設置單獨的會話超時時間值。與上面的 globalSessionTimeout 一樣,該值以毫秒(不是秒)爲時間單位。

 

Session 監聽器

Shiro 支持 SessionListener 概念來允許你對發生的重要會話作出反應。你可以實現 SessionListener 接口(或擴展易用的 SessionListenerAdapter)並與相應的會話操作作出反應。

 

由於默認的 SessionManager sessionListeners 屬性是一個集合,你可以對 SessionManager 配置一個或多個 listener 實現,就像其他在 shiro.ini 中的集合一樣:

 

 
 

 

All Session 事件

當任何會話發生事件時,SessionListeners 都會被通知——不僅僅是對一個特定的會話。

 

Session 存儲

 

每當一個會話被創建或更新時,它的數據需要持久化到一個存儲位置以便它能夠被稍後的應用程序訪問。同樣地, 當一個會話失效且不再被使用時,它需要從存儲中刪除以便會話數據存儲空間不會被耗盡。SessionManager      實現委託這些 Create/Read/Update/Delete(CRUD)操作爲內部組件,同時,SessionDAO,反映了數據訪問對象(DAO)設計模式。

 

SessionDAO     的權力是你能夠實現該接口來與你想要的任何數據存儲進行通信。這意味着你的會話數據可以駐留在內存中,文件系統,關係數據庫或 NoSQL 的數據存儲,或其他任何你需要的位置。你得控制持久性行爲。

 

你可以將任何 SessionDAO 實現作爲一個屬性配置在默認的 SessionManager 實例上。例如,在 shiro.ini 中:

 

 

 
 

 

然而,正如你可能期望的那樣,Shiro 已經有一些很好的 SessionDAO 實現,你可以立即使用或實現你需要的子類。

 

Web 應用程序

上述的 securityManager.sessionManager .sessionDAO = $sessionDAO 作業僅在使用一個本地的 Shiro 會話管理器時才工作。Web 應用程序默認不會使用本地的會話管理器,而是保持不支持 SessionDAO 的 Servlet Container 的默認會話管理器。如果你想基於 Web 應用程序啓用 SessionDAO 來自定義會話存儲或會話羣集,你將不得不首先配置一個本地的 Web 會話管理器。例如:

[main]

sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager securityManager.sessionManager = $sessionManager

 

# Configure a SessionDAO and then set it: securityManager.sessionManager.sessionDAO = $sessionDAO

 

 

Configure a SessionDAO!

Shiro 的默認配置本地 SessionManagers 使用僅內存 Session 存儲。這是不適合大多數應用程序的。大多數生產應用程序想要配置提供的 EHCache(見下文)支持或提供自己的 SessionDAO 實現。

 

請注意 Web 應用程序默認使用基於 servlet 容器的 SessionManager,且沒有這個問題。這也是使用 Shiro 本地

SessionManager 的唯一問題。

 

EHCache SessionDAO

EHCache 默認是沒有啓用的,但如果你不打算實現你自己的 SessionDAO,那麼強烈地建議你爲 Shiro 的

SessionManagerment 啓用 EHCache 支持。EHCache SessionDAO 將會在內存中保存會話,並支持溢出到磁盤,若內存成爲制約。這對生產程序確保你在運行時不會隨機地“丟失”會話是非常好的。

 

U使用Ehcache作爲默認設置

如果你不準備編寫一個自定義的 SessionDAO,則明確地在你的 Shiro 配置中啓用 EHCache。EHCache 帶來的好處遠不止在 Sessions,緩存驗證和授權數據方面。更多信息,請參見 Caching 文檔。

 

C獨立於容器的會話集羣

如果你急需獨立的容器會話集羣,EHCache 會是一個不錯的選擇。你可以顯式地在 EHCache 之後插入 TerraCotta, 並擁有一個獨立於容器集羣的會話緩存。不必再擔心 Tomcat,JBoss,Jetty,WebSphere 或 WebLogic 特定的會話集羣!

 

爲會話啓用 EHCache 是非常容易的。首先,確保在你的 classpath 中有 shiro-ehcache-<version>.jar 文件(請參見

Download 頁面或使用 Maven 或 Ant+Ivy)。

當在 classpath 中後,這第一個 shiro.ini 實例向你演示怎樣爲所有 Shiro 的緩存需要(不只是會話支持)使用 EHCache:

 

 

 

 

 

 

 

最後一行,securityManager.cacheManager = $cacheManager,爲所有 Shiro 的需要配置了一個 CacheManager。該

CacheManager 實例會自動地直接傳送到 SessionDAO(通過 EnterpriseCacheSessionDAO 實現 CacheManagerAware 接口的性質)。

 

然後,當 SessionManager 要求 EnterpriseCacheSessionDAO 去持久化一個 Session 時,它使用一個 EHCache 支持的 Cache

實現去存儲 Session 數據。

 

Web Applications

當使用 Shiro 本地的 SessionManager 實現時不要忘了分配 SessionDAO 是一項功能。Web 應用程序默認使用基於容器的 SessionManager,它不支持 SessionDAO。如果你想在 Web 應用程序中使用基於 EHCache 的會話存儲,配置一個如上所解釋的 Web SessionManager。

 

EHCache Session Cache Configuration

默認地,EhCacheManager 使用一個 Shiro 特定的 ehcache.xml 文件來建立 Session 緩存區以及確保 Sessions 正常存取的必要設置。

 

然而,如果你想改變緩存設置,或想配置你自己的 ehcache.xml 或 EHCache net.sf.ehcache.CacheManager 實例,你需要配置緩存區來確保 Sessions 被正確地處理。

 

如果你查看默認的 ehcache.xml 文件,你會看到接下來的 shiro-activeSessionCache 緩存配置:

 

<cache name="shiro-activeSessionCache" maxElementsInMemory="10000" overflowToDisk="true" eternal="true" timeToLiveSeconds="0" timeToIdleSeconds="0" diskPersistent="true"

diskExpiryThreadIntervalSeconds="600"/>

 

如果你希望使用你自己的 ehcache.xml 文件,那麼請確保你已經爲 Shiro 所需的定義了一個類似的緩存項。很有可能你會改變  maxElementsInMemory  的屬性值來吻合你的需要。然而,至少下面兩個存在於你自己配置中的屬性是非常重要的:

  • overflowToDisk="true" - 這確保當你溢出進程內存時,會話不丟失且能夠被序列化到磁盤上。
  • eternal="true" - 確保緩存項(Session 實例)永不過期或被緩存自動清除。這是很有必要的,因爲 Shiro 基於計劃過程完成自己的驗證。如果我們關掉這項,緩存將會在 Shiro 不知道的情況下清掃這些 Sessions,這可能引起麻煩。

 

EHCache Session Cache Name

默認地,EnterpriseCacheSessionDAO 向 CacheManager 尋求一個名爲"shiro-activeSessionCache"的 Cache。該緩存的

name/region 將在 ehcache.xml 中配置,如上所述。

如果你想使用一個不同的名字而不是默認的,你可以在 EnterpriseCacheSessionDAO 上配置名字,例如:

 

 

 
 

 

只要確保在 ehcahe.xml 中有一項與名字匹配且你已經配置好了如上所述的 overflowToDisk="true"和 eternal="true"。

 

Custom Session IDs

Shiro 的 SessionDAO 實現使用一個內置的 SessionIdGenerator 組件來產生一個新的 Session ID 當每次創建一個新的會話的時候。該 ID 生成後,被指派給新近創建的 Session 實例,然後該 Session 通過 SessionDAO 被保存下來。

 

默認的 SessionIdGenerator 是一個 JavaUuidSessionIdGenerator,它能產生基於 Java UUIDs 的 String IDs。該實現能夠支持所有的生產環境。

 

如果它不符合你的需要,你可以實現 SessionIdGenerator 接口並在 Shiro 的 SessionDAO 實例上配置該實現。例如, 在 shiro.ini 中:

 

 

 
 

 

Session 驗證& 調度

Sessions     必須被驗證,這樣任何無效(過期或停止)的會話能夠從會話數據存儲中刪除。這保證了數據存儲不會由於不能再次使用的會話而導致寫入超時。

 

由於性能上的原因,僅僅在 Sessions 被訪問(也就是 subject.getSession())時驗證它們是否停止或過期。這意味着, 如果沒有額外的定期驗證,Session orphans(孤兒)將會開始填充會話數據存儲。

 

一個常見的說明孤兒的例子是 Web 瀏覽器中的場景:比方說,用戶登錄到 Web 應用程序並創建了一個會話來保留數據(身份驗證狀態,購物車等)。如果用戶不註銷,並在應用程序不知道的情況下關閉了瀏覽器,則他們的會話實質上是“躺在”會話數據存儲的(孤兒)。SessionManager 沒有辦法檢測用戶不再使用他們的瀏覽器,同時該會話永遠不會被再次訪問(它是孤兒了)。

 

會話孤兒,如果它們沒有定期 清除,將會填充會話數據存儲(這是很糟糕的)。因此,爲了防止丟放孤兒,

SessionManager 實現支持 SessionValidationScheduler 的概念。SessionValidationScheduler 負責定期地驗證會話以確保它們是否需要清理。

 

Default SessionValidationScheduler

默認可用的 SessionValidationScheduler 在所有環境中都是 ExecutorServiceSessionValidationScheduler,它使用 JDK ScheduledExecutorService 來控制驗證頻率。

 

默認地,該實現每小時執行一次驗證。你可以通過指定一個新的 ExecutorServiceSessionValidationScheduler 實例並指定不同的間隔(以毫秒爲單位)改變速率來更改驗證頻率:

 

 

 
 

 

自定義SessionValidationScheduler

如果你希望提供一個自定義的 SessionValidationScheduler 實現,你可以指定它作爲默認的 SessionManager 實例的一個屬性。例如,在 shiro.ini 中:

 

 

 
 

 

Disabling Session Validation

在某些情況下,你可能希望禁用會話驗證項,由於你建立了一個超出了 Shiro 控制的進程來爲你執行驗證。例如, 也許你正在使用一個企業的 Cache 並依賴於緩存的 Time To Live 設置來自動地去除舊的會話。或者也許你已經制定了一個計劃任務來自動清理一個自定義的數據存儲。在這些情況下你可以關掉 session validation scheduling:

 

 
 

 

當會話從會話數據存儲取回數據時它仍然會被驗證,但這會禁用掉 Shiro 的定期驗證。

 

Enable Session Validation somewhere

如果你關閉了 Shiro 的 session validation scheduler,你必須通過其他的機制(計劃任務等)來執行定期的會話驗證。這是保證會話孤兒不會填充數據存儲的唯一方法。

 

刪除無效的會話

正如我們上面所說的,進行定期的會話驗證主要目的是爲了刪除任何無效的(過期或停止)會話來確保它們不會填充會話數據存儲。

 

默認地,某些應用程序可能不希望 Shiro 自動地刪除會話。例如,如果一個應用程序已經提供了一個 SessionDAO 備份數據存儲查詢,也許是應用程序團隊希望舊的或無效的會話在一定的時間內可用。這將允許團隊對數據存儲運行查詢來判斷,例如,在上週某個用戶創建了多少個會話,或一個用戶會話的持續時間,或與之類似報告類型的查詢。

 

在這些情形中,你可以關閉 invalid session deletion 項。例如,在 shiro.ini 中:

 

 
 

 

請注意!如果你關閉了它,你得爲確保你的會話數據存儲不耗盡它的空間複雜。你必須自己從你的數據存儲中刪除無效的會話!

 

還要注意,即使你阻止了 Shiro 刪除無效的會話,你仍然應該使用某種會話驗證方式——要沒通過 Shiro 的現有驗證機制,要麼通過一個你自己提供的自定義的機制(見上述的"Disabling  Session  Validation"獲取更多)。驗證機制將會更新你的會話記錄以反映無效的狀態(例如,什麼時候它是無效的,它最後一次被訪問是什麼時候,等等),即使你在其他的一些時間將手動刪除它們。

 

如果你配置 Shiro 來讓它不會刪除無效的會話,你得爲確保你的會話數據存儲不會耗盡它的空間負責。你必須親自從你的數據存儲刪除無效的會話!

另外請注意,禁用會話刪除並不等同於禁用 session validation schedule(會話驗證調度)。你應該總是使用一個會話驗證調度機制——無論是 Shiro 直接支持或者是你自己的。

 

Sessions 和主題狀態

 

有狀態 Applications(Sessions allowed)

默認地,Shiro 的 SecurityManager 實現使用一個 Subject 的 Session 作爲一種策略來爲接下來的引用存儲 Subject 的身份 ID(PrincipalCollection)和驗證狀態(subject.isAuthenticated())。這通常發生在一個 Subject 登錄後或當一個 Subject 的身份 ID 通過 Remember 服務被發現後。

 

下面是使用這種默認方式的好處:

  • 任何服務於請求,調用或消息的應用程序可以用請求/調用/消息的有效載荷關聯會話 ID,且這是 Shiro 用入站請求關聯用戶所有所必須的。例如,如果使用 Subject.Builder,這是需要獲取相關的 Subject 所需的一切:

Serializable sessionId = //get from the inbound request or remote method invocation payload Subject requestSubject = new Subject.Builder().sessionId(sessionId),buildSubject();

 

這給大多數 Web 應用程序及任何編寫遠程處理或消息框架的人帶來了令人難以置信的方便(這事實上是 Shiro

的 Web 支持在自己的框架代碼內關聯 Subject 和 ServletRequest)。

 

  • 任何"RememberMe"身份基於一個能夠在第一次訪問就能持久化到會話的初始請求。這確保了  Subject  被記住的身份可以跨請求保存而不需要反序列化及將它解釋到每個請求。例如,在一個 Web 應用程序中,沒有必要去讀取每一個請求的加密 RememberMe Cookie,如果該身份在會話中是已知的。這可是一個很好的性能提升。

 

無狀態的 Applications(Sessionless)

雖然上述的默認策略對於大多數應用程序而言是很好的(通常是可取的),但這對於嘗試儘可能無狀態的應用程序來說是不合適的。許多無狀態的架構規定在請求中不能存在持久狀態,這種情況下的  Sessions  不會被允許(一個會話其本質代表了持久狀態)。

 

但這一要求帶來一個便利的代價——Subject 狀態不能跨請求保留。這意味着有這一要求的應用程序必須確保Subject

狀態可以在每一個請求中以其他的方式代表。

 

這幾乎總是通過驗證每個由應用程序處理的請求/調用/消息來完成的。例如,大多數無狀態 Web 應用程序通常支持這一點通過執行 HTTP 基本驗證,允許瀏覽器驗證每一個代表最終用戶的請求。遠程或消息框架必須確保 Subject 的身份和憑證連接到每一個調用或消息的有效載荷,通常是由框架代碼執行。

 

禁用主題狀態會話存儲

在 Shiro 1.2 及以後開始,應用程序想禁用 Shiro 的內部實現策略——將 Subject 狀態持久化到會話,可以禁用所有

Subject 的這一項,通過下面的操作:

在 shiro.ini 中,在 securityManager 上配置下面的屬性:

 

 
 
 
 

 

 

 

這將防止 Shiro 使用 Subject 的會話來存儲所有跨請求/調用/消息的 Subject 狀態。只要確保你對每個請求進行了身份驗證,這樣 Shiro 將會對給定的請求/調用/消息知道它的 Subject 是誰。

 

Shiro的需求和你的需求

使用 Sessions 作爲存儲策略將禁用 Shiro 本身的實現。它沒有完全地禁用 Sessions。如果你的任何代碼顯式地調用subject.getSession()或 subject.getSession(true),一個 session 仍然會被創建。

 

一個混合的方式

上面的 shiro.ini 配置中的(securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled  =  false)這一行將會禁用 Shiro 爲所有的 Subject 使用 Session 作爲一種實現策略。

 

但,如果你想使用混合的方法呢?如果某些對象應該有會話而某些沒有?這種混合法方法能夠給許多應用程序帶來好處。例如:

  • 也許 human Subject(如 Web 瀏覽器用戶)由於上面提供的好處能夠使用 Session。
  • 也許 non-human Subject(如 API 客戶端或第三方應用程序)不應該創建 session 由於它們與軟件的交互可能會間歇或不穩定。
  • 也許所有某種確定類型的 Subject 或從某一確定位置訪問系統的應該將狀態保持在會話中,但所有其他的不應該。

 

如果你需要這個混合方法,你可以實現一個 SessionStorageEvaluator。

 

SessionStorageEvaluator

在你想究竟控制哪個 Subject  能夠在它們的 Session  中保存它們的狀態的情況下,你可以實現

org.apache.shiro.mgt.SessionStorageEvaluator 接口,並告訴 Shiro 哪個 Subject 支持會話存儲。

該接口只有一個方法:

 

 
 

 

關於更詳細的 API 說明,請參見 SessionStorageEvaluator 的 JavaDoc。

你可以實現這一接口,並檢查 Subject,爲了你可能做出這一決定的任何信息。

 

Subject 檢查

但實現 isSessionStorageEnabled(subject)接口方法時,你可以一直查看 Subject 並訪問任何你需要用來作出決定的東西。當然所有期望的 Subject 方法都是可用的(gePrincipals()等),但特定環境的 Subject 實例也是有價值的。

 

例如,在 Web 應用程序中,如果該決定必須基於當前 ServletRequest 中的數據,你可以獲取該 request 或該 response, 因爲運行時的 Subjce 實例實際上就是一個 WebSubject 實例:

public boolean isSessionStorageEnabled(Subject subject) { boolean enabled = false; if(WebUtils.isWeb(Subject)) {

HttpServletRequest request = WebUtils.getHttpRequest(subject);

//set 'enabled' based on the current request.

} else {

//not a web request - maybe a RMI or daemon invocation?

//set 'enabled' another way …

}

return enabled;

}

 

    1. .框架開發人員應該考慮到這種類型的訪問,並確保任何請求/調用/消息上下文對象可用是同過特定環境下的

Subject 實現的。聯繫 Shiro 用戶郵件列表,如果你想幫助設置它,爲了你的框架/環境。

 

配置

在你實現了 SessionStorageEvaluator 接口後,你可以在 shiro.ini 中配置它:

 

Web Applications

通常 Web 應用程序希望在每一個請求的基礎上容易地啓用或禁用會話的創建,不管是哪個 Subject 正在執行請求。這經常在支持 REST 及 Messaging/RMI 構架上使用來產生很好的效果。例如,也許正常的終端用戶(使用瀏覽器的人) 被允許創建和使用會話,但遠程的 API 客戶端使用 REST 或 SOAP,不該擁有會話(因爲它們在每一個請求上驗證, 常見 REST/SOAP 體系結構)。

 

爲了支持這種hybrid/per-request 的能力,noSessionCreation 過濾器被添加到Shiro 的默認爲Web 應用程序啓用的“池”。該過濾器將會阻止在請求期間創建新的會話來保證無狀態的體驗。在 shiro.ini 的[urls]項中,你通常定義該過濾器在所有其它過濾器之前來確保會話永遠不會被使用。

 

例如:

 

 
 

 

這個過濾器允許現有會話的任何會話操作,但不允許在過濾的請求創建新的會話。也就是說,一個請求或沒有會話存在的 Subject 調用下面四個方法中的任何一個時,將會自動地觸發一個 DisabledSessionException 異常:

      • httpServletRequest.getSession()
      • httpServletRequest.getSession(true)
      • subject.getSession()
      • subject.getSession(true)

 

如果一個 Subject 在訪問 noSessionCreation-protected-URL 之前已經有一個會話,則上述的四種調用仍然會如預期般工作。

 

最後,在所有情況下,下面的調用將始終被允許:

      • httpServletRequest.getSession(false)
      • subject.getSession(false)

 

 

Web

Configuration(配置)

將 Shiro 集成到任何 Web 應用程序的最簡單的方法是在 web.xml 中配置 ContextListener 和 Filter,理解如何讀取 Shiro的 INI 配置文件。大部分的 INI 配置格式定義在 Configuration 頁的 INI Sections 節,但我在這裏我們將介紹一些額外的 Web 的特定部分。

 

 
 

 

Web.xml

 

Shiro 1.2 and later

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

 

 

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

  1. /WEB-INF/shiro.ini
  2. 在 classpath 根目錄下 shiro.ini 文件

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

      • EnvironmentLoaderListener 初始化一個 Shiro WebEnvironment 實例(其中包含 Shiro 需要的一切操作,包括

SecurityManager),使得它在 ServletContext 中能夠被訪問。如果你需要在任何時候獲得 WebEnvironment 實例,你可以調用 WebUtils.getRequiredWebEnvironment(ServletContext)。

      • ShiroFilter 將使用此 WebEnvironment 對任何過濾的請求執行所有必要的安全操作。
      • 最後,filter-mapping 的定義確保了所有的請求被 ShiroFilter 過濾,建議大多數 Web 應用程序使用以確保任何請求是安全的。

 

 
 

 

自定義 WebEnvironment Class

默認情況下,EnvironmentLoaderListener 將創建一個 IniWebEnvironment 實例,呈現 Shiro 基於 INI 文件的配置。如果你願意,你可以在 web.xml 中指定一個自定義的 ServletContext context-param:

 

 
 

 

這允許你自定義一個如何解析和代表 WebEnvironment 實例的配置格式。你可以爲自定義的行爲對現有的

IniWebEnvironment 創建子類,或完全支持不同的配置格式。例如,如果有人想在 XML 中配置 Shiro 而不是在 INI 中, 他們可以創建一個基於 XML 的實現,如 com.foo.bar.shiro.XmlWebEnviroment。

 

自定義 Configuration Locations

 

IniWebEnvironment 將會去讀取和加載 INI 配置文件。默認情況下,這個類會自動地在下面兩個位置尋找 Shiro.ini 配置(按順序)。

  1. /WEB-INF/shiro.ini
  2. classpath:shiro.ini

它將使用最先發現的那個。

然而,如果你想把你的配置放在另一位置,你可以在 web.xml 中用 contex-param 指定該位置。

 

 
 

 

默認情況下,在 ServletContext.getResource 方法定義的規則下,param-value 是可以被解析的。例如,

/WEB-INF/some/path/shiro.ini。

但你也可以指定具體的文件系統,如 classpath 或 URL 位置,通過使用 Shiro 支持的合適的資源前綴,例如:

 

Shiro 1.1 and 早期

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

 

 
 

 

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

 

自定義 Path

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

 

 

不合格的(不完整的組合或'non-prefixed')configPath 值被假定爲 ServletContext 的資源路徑,通過

ServletContext.getResource 方法所定義的規則來解析。

 

 
 

 

通過分別地使用 classpath:,url:,或 file:前綴來指明 classpath,url,或 filesystem 位置,你也可以指定其他非

ServletContext 資源位置。例如:

 

 
 

 

內聯配置

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

 

 
 

 

內嵌配置對於小型的或簡單的應用程序通常是很好用的,但是由於以下原因一般把它具體化到一個專用的 Shiro.ini

文件中:

      • 你可能編輯了許多安全配置,不希望爲 web.xml 添加版本控制。
      • 你可能想從餘下的 web.xml 配置中分離安全配置。
      • 你的安全配置可能變得很大,你想保持 web.xml 的苗條並易於閱讀。
      • 你有個負責的編譯系統,相同的 shiro 配置可能需要在多個地方被引用。

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

 

Web INI configuration

除了在主要的 Configuration 章節描述的標準的[main],[user]和[roles]項外,你可以在 shiro.ini 文件中指定具有 web

特性的[urls]項:

 

 
 

 

[urls]項允許你做一些在我們已經見過的任何 Web 框架都不存在的東西:在你的應用程序中定義自適應過濾器鏈來匹配 URL 路徑!

 

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

 

[urls]

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

URL_Ant_Path_Expression = Path_Specific_Filter_Chain

 

例如:

 

 
 

 

接下來我們將討論這些行的具體含義。

 

URL Path Expressions

等號左邊是一個與 Web 應用程序上下文根目錄相關的 Ant 風格的路徑表達式。

例如,假設你有如下的[urls]行:

 

此行表明,“任何對我應用程序的/accout 或任何它的子路徑(/account/foo, account/bar/baz,等等)的請求都將觸發'ssl, authc'過濾器鏈”。我們將在下面討論過濾器鏈。

 

請注意,所有的路徑表達式都是相對於你的應用程序的上下文根目錄而言的。這意味着如果某一天你在某個位置部署了你的應用程序,如 www.somehost.com/myapp  ,然後又將它部署到了 www.anotherhost.com (沒有'myapp'子目錄),這樣的匹配模式仍將繼續工作。所有的路徑都是相對於 HttpServletRequest.getContextPath()的值來的。

 

 
 

 

過濾器鏈定義

等號右邊是逗號隔開的過濾器列表,用來執行匹配該路徑的請求。它必須符合以下格式:

filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN]

 

並且:

      • filterN 是一個定義在[main]項中的 filter bean 的名字
      • [optional_configN]是一個可選的括號內的對特定的路徑,特定的過濾器有特定含義的字符串(每個過濾器,每   個路徑的具體配置!)。若果該過濾器對該 URL 路徑並不需要特定的配置,你可以忽略括號,於是 filteNr[] 就變成了 filterN.

 

因爲過濾器標誌符定義了鏈(又名列表),所以請記住順序問題!請按順序定義好你的逗號分隔的列表,這樣請求就能夠流通這個鏈。

 

最後,每個過濾器按照它期望的方式自由的處理請求,即使不具備必要的條件(例如,執行一個重定向,響應一個

HTTP 錯誤代碼,直接渲染等)。否則,它有可能允許該請求繼續通過這個過濾器鏈到達最終的視圖。

 

 
 

 

可用的過濾器

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

 

 
 

 

 

Default Filters默認的過濾器

當運行一個 Web 應用程序時,Shiro 將會創建一些有用的默認 Filter 實例,並自動地在[main]項中將它們置爲可用。你可以在 main 中配置它們,當作在你的鏈的定義中你是否有任何其他的 bean 和 reference。例如:

 

 
 

 

自動地可用的默認的 Filter 實例是被 DefaultFilter 枚舉定義的,枚舉的名稱字段是可供配置的名稱。它們是:

 

Filter Name

Class

anon

org.apache.shiro.web.filter.authc.AnonymousFilter

authc

org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

logout

org.apache.shiro.web.filter.authc.LogoutFilter

noSessionCreation

org.apache.shiro.web.filter.session.NoSessionCreationFilter

perms

org.apache.shiro.web.filter.authz.PermissionAuthorizationFilter

port

org.apache.shiro.web.filter.authz.PortFilter

rest

org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles

org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl

org.apache.shiro.web.filter.authz.SslFilter

user

org.apache.shiro.web.filter.authz.UserFilter

 

 

啓動和禁用 Filters

 

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

 

但在 Shiro 1.2 中新增的一個新功能是不通過從過濾器鏈中移除過濾器來啓用或禁用過濾器。如果啓用(默認設置),那麼請求將如預期一樣過濾。如果禁用,那麼該過濾器將允許請求立即通過到 FilterChain 的下一個元素。你可以基於一般配置屬性觸發過濾器的啓用狀態,或者你甚至可以在每一個請求的基礎上觸發。

 

這是一個強大的概念,因爲基於特定需求啓用或禁用一個過濾器比更改靜態過濾器鏈(這是永久的且固定的)定義更爲方便。

 

Shiro 通過它的OncePerRequestFilter 抽象父類來完成這點。所有Shiro 的不受規範限制的過濾器實現子類實現這一點, 因此不需要從過濾器鏈移除它們實現啓用或禁用。如果你需要實現此功能,你可以爲自己的過濾器實現繼承這個類的子類。

 

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

 

一般的啓用和禁用

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)方法來執行更多特定的檢查。

 

路徑的啓用和過濾

Shiro 的 PathMatchingFilter(一個 OncePerRequestFilter 的子類)能夠對基於被過濾的特定路徑的配置作出反應。這意味着你可以啓用或禁用一個過濾器基於路徑和特定路徑配置,除了傳入的 request 和 response。

 

如果你需要能夠對匹配的路徑和特定路徑配置作出反應來判斷一個過濾器是否是啓用的或禁用的,而不是通過覆蓋

OncePerRequestFilter 的 isEnabled(request, reponse)方法,你應該是覆蓋 PathMatchingFilter 的 isEnabled(request,

response)方法。

 

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

 

 
 

 

如果該方法返回 true,Shiro 將會在整個會話中記住終端用戶的身份 ID。

 

 
 

 

編程支持

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

 

 
 

 

基於表格的登錄

對於 Web 應用程序而言,authc 過濾器默認是 FormAuthenticationFilter。它支持將'rememberMe'的布爾值作爲一個

form/request 參數讀取。默認地,它期望該 request 參數被命名爲 rememberMe。下面是一個支持這點的 shiro.ini 配置的例子:

 

 
 

 

 

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

 

 
 

 

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

 

 
 

 

Cookie 配置

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

 

 
 

 

請參見 CookieRememberMeManager 及 SimpleCookie 的 JavaDoc 支持來獲取更多的配置屬性。

 

自定義 RememberMeManager

應該注意到,默認基於 cookie 的 RememberMeManager 實現不符合你的需求,你可以插入任何你喜歡的插件到

securityManager 當中,就像你配置任何其他對象的引用一樣:

 

 
 

 

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 頁面(或任何你定義的頁面指令)的頂部。

 

 
 

 

我們使用 shiro 前綴用以表明 shiro 標籤庫命名空間,當然你可以指定任何你喜歡的名字。

現在我們將討論每一個標籤,並展示它是如何用來渲染頁面的。

 

The guest tag

guest 標籤將顯示它包含的內容,僅噹噹前的 Subject 被認爲是'guest'時。'guest'是指沒有身份 ID 的任何 Subject。也就是說,我們並不知道用戶是誰,因爲他們沒有登錄並且他們沒有在上一次的訪問中被記住(RememberMe 服務)。

 

例子:

 

 
 

 

guest 標籤與 user 標籤邏輯相反。

 

The user tag

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

 

例子:

 

 
 

 

usre 標籤與 guest 標籤邏輯相反。

 

The authenticated tag

僅僅只噹噹前用戶在當前會話中成功地通過了身份驗證 authenticated 標籤纔會顯示包含的內容。它比'user'標籤更爲嚴格。它在邏輯上與'notAuthenticated'標籤相反。

 

authenticated 標籤只有噹噹前 Subject 在其當前的會話中成功地通過了身份驗證纔會顯示包含的內容。它比 user 標籤更爲嚴格,authenticated 標籤通常在敏感的工作流中用來確保身份 ID 是可靠的。

 

例子:

 

 

authenticated 標籤與 notAuthenticated 標籤邏輯相反。

 

The notAuthenticated tag

notAuthenticated 標籤將會顯示它所包含的內容,如果當前 Subject 還沒有在其當前會話中成功地通過驗證。

例子:

 

 

 

notAuthenticated 標籤與 Authenticated 標籤邏輯相反。

 

The principal tag

principal 標籤將會輸出 Subject 的主體(標識屬性)或主要的屬性。

若沒有任何標籤屬性,則標籤將使用 principal 的 toString()值來呈現頁面。例如(假設 principal 是一個字符串的用戶名):

 

 
 

 

這(大部分地)等價於下面:

 

 
 

 

Typed principal

principal 標籤默認情況下,假定該 principal 輸出的是 subject.getPrincipal()的值。但若你想輸出一個不是主要 principal

的值,而是屬於另一個 Subject 的 principal collection,你可以通過類型來獲取該 principal 並輸出該值。

例如,輸出 Subject 的用戶 ID(並不是 username),假設該 ID 屬於 principal collection:

 

 
 

 

這(大部分地)等價於下面:

 

 
 

 

 

Principal property

但如果該 principa(l

 

是默認主要的 principal 或是上面的'typed' principal)是一個複雜的對象而不是一個簡單的字符串,

 

而且你希望引用該 principal 上的一個屬性該怎麼辦呢?你可以使用 property 屬性來來表示 property 的名稱來理解

(必須通過 JavaBeans 兼容的 getter 方法訪問)。例如(假主要的 principal 是一個 User 對象):

 

 

這(大部分地)等價於下面:

 

 
 

 

或者,結合類型屬性:

 

 
 

 

這也很大程度地等價於下面:

 

 
 

 

The hasRole tag

hasRole 標籤將會顯示它所包含的內容,僅噹噹前 Subject 被分配了具體的角色。

例如:

 

 
 

 

hasRole 標籤與 lacksRole 標籤邏輯相反。

 

The lacksRole tag

lacksRole 標籤將會顯示它所包含的內容,僅噹噹前 Subject 未被分配具體的角色。

例如:

 

 
 

 

lacksRole 標籤與 hasRole 標籤邏輯相反。

 

The hasAnyRole tag

hasAnyRole 標籤將會顯示它所包含的內容,如果當前的 Subject 被分配了任意一個來自於逗號分隔的角色名列表中的具體角色。

 

例如:

 

 
 

 

 

hasAnyRole 標籤目前還沒有與之邏輯相反的標籤。

 

The hasPermission tag

hasPermission 標籤將會顯示它所包含的內容,僅噹噹前 Subject“擁有”(蘊含)特定的權限。也就是說,用戶具有特定的能力。

 

例如:

 

 
 

 

hasPermission 標籤與 lacksPermission 標籤邏輯相反。

 

The lacksPermission tag

lacksPermission 標籤將會顯示它所包含的內容,僅噹噹前 Subject 沒有擁有(蘊含)特定的權限。也就是說,用戶沒有特定的能力。

 

例如:

 

 
 

 

lacksPermission 標籤與 hasPermission 標籤邏輯相反。

 

Lend a hand with documentation

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

 

Caching

 

Shiro 開發團隊明白在許多應用程序中性能是至關重要的。Caching 是從第一天開始第一個建立在 Shiro  中的一流功能,以確保安全操作保持儘可能的快。

 

然而,Caching 作爲一個概念是 Shiro 的基本組成部分,實現一個完整的緩存機制是安全框架核心能力之外的事情。爲此,Shiro 的緩存支持基本上是一個抽象的(包裝)API,它將“坐”在一個基本的緩存機制產品(例如,Ehcache, OSCache,Terracotta,Coherence,GigaSpaces,JBossCache 等)之上。這允許 Shiro 終端用戶配置他們喜歡的任何緩存機制。

 

Caching API

Shiro 有三個重要的緩存接口:

      • CacheManager - 負責所有緩存的主要管理組件,它返回 Cache 實例。
      • Cache - 維護 key/value 對。
      • CacheManagerAware - 通過想要接收和使用 CacheManager 實例的組件來實現。

CacheManager 返回 Cache 實例,各種不同的 Shiro 組件使用這些 Cache 實例來緩存必要的數據。任何實現了

CacheManagerAware 的 Shiro 組件將會自動地接收一個配置好的 CacheManager,該 CacheManager 能夠用來獲取

Cache 實例。

 

Shiro 的 SecurityManager 實現及所有 AuthorizingRealm 實現都實現了 CacheManagerAware。如果你在 SecurityManager 上設置了 CacheManger,它反過來也會將它設置到實現了 CacheManagerAware 的各種不同的 Realm 上(OO delegation)。例如,在 shiro.ini 中:

 

 

 
 

 

我們擁有一個立即可用的  EhCacheManager  實現,因此,如果你想的話,今天都可以使用。相反地,你可以實現自己的 CacheManager(如使用 Coherence 等),並像上面那樣配置它,你會取得很好的效果的。

 

Authorization Cache 失效

最後請注意 AuthorizingRealm 有一個 clearCachedAuthorizationInfo 方法能夠被子類調用,用來清除特殊賬戶緩存的授權信息。它通常被自定義邏輯調用,如果與之匹配的賬戶授權數據發生了改變(來確保下次的授權檢查能夠捕獲新數據)。

 

 

併發&多線程

 

TODO:未翻譯

 

Testing with Apache Shiro

 

文檔的這一部分介紹了在單元測試中如何使用 Shiro。

 

測試要知道什麼

由於我們已經涉及到了 Subject reference,我們知道 Subject 是“當前執行”用戶的特定安全視圖,且該 Subject 實例綁定到一個線程來確保我們知道在線程執行期間的任何時間是誰在執行邏輯。

 

這意味着三個基本的東西必須始終出現,爲了能夠支持訪問當前正在執行的 Subject:

 

  1. 必須創建一個 Subject 實例
  2. Subject 實例必須綁定當前執行的線程。
  3. 在線程完成執行後(或如果該線程執行拋出異常),該 Subject 必須解除綁定來確保該線程在任何線程池環境中保持'clean'。

 

Shiro 擁有爲正在運行的應用程序自動地執行綁定//解除綁定邏輯的建築組件。例如,在 Web 應用程序中,當過濾一個請求時,Shiro 的根過濾器執行該邏輯。但由於測試環境和框架不同,我們需要自己選擇自己的測試框架來執行此綁定/解除綁定邏輯。

 

Test 設置

我們知道在創建一個 Subject 實例後,它必須被綁定線程。在該線程(或在這個例子中,是一個 test)完成執行後, 我們必須解除 Subject 的綁定來保持線程的'clean'.

 

幸運的是,現代測試框架如 JUnit 和 TestNG 已經能夠在本地支持'setup'和'teardown'的概念。我們可以利用這一支持來模擬 Shiro 在一個“完整的”應用程序中會做些什麼。我們已經在下面創建了一個你能夠在你自己的測試中使用 的抽象基類——隨意複製和修改如果你覺得合適的話。它能夠在單元測試和集成測試中使用(我在本例中使用 JUnit, 但 TestNG 也能夠工作得很好):

 

AbstractShiroTest

 

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject;

import org.apache.shiro.subject.support.SubjectThreadState; import org.apache.shiro.util.LifecycleUtils;

import org.apache.shiro.util.ThreadState; import org.junit.AfterClass;

 

/**

*Abstract test case enabling Shiro in test environments.

*/

public abstract class AbstractShiroTest {

private static ThreadState subjectThreadState; public AbstractShiroTest() {

}

 

/**

  • Allows subclasses to set the currently executing {@link Subject} instance.

*

  • @param subject the Subject instance

*/

protected void setSubject(Subject subject) { clearSubject();

subjectThreadState = createThreadState(subject); subjectThreadState.bind();

}

 

protected Subject getSubject() {

return SecurityUtils.getSubject();

 

}

 

protected ThreadState createThreadState(Subject subject) { return new SubjectThreadState(subject);

}

 

/**

  • Clears Shiro's thread state, ensuring the thread remains clean for future test execution.

*/

protected void clearSubject() { doClearSubject();

}

 

private static void doClearSubject() { if (subjectThreadState != null) {

subjectThreadState.clear(); subjectThreadState = null;

}

}

 

protected static void setSecurityManager(SecurityManager securityManager) { SecurityUtils.setSecurityManager(securityManager);

}

 

protected static SecurityManager getSecurityManager() { return SecurityUtils.getSecurityManager();

}

 

@AfterClass

public static void tearDownShiro() { doClearSubject();

try {

SecurityManager securityManager = getSecurityManager(); LifecycleUtils.destroy(securityManager);

} catch (UnavailableSecurityManagerException e) {

//we don't care about this when cleaning up the test environment

//(for example, maybe the subclass is a unit test and it didn't

//need a SecurityManager instance because it was using only

//mock Subject instances)

}

setSecurityManager(null);

}

}

 

Testing & Frameworks

在 AbstractShiroTest 類中的代碼使用 Shiro 的 ThreadState 概念及一個靜態的 SecurityManager。這些技術在測試和框架代碼中是很有用的,但幾乎不曾在應用程序代碼中使用。

大多數使用 Shiro 工作的需要確保線程的一致性的終端用戶,幾乎總是使用 Shiro 的自動管理機制,即

Subject.associateWith 和 Subject.execute 方法。這些方法包含在 Subject thread association 參考文獻中。

 

Unit Testing

單元測試主要是測試你的代碼,且你的代碼是在有限的作用域內。當你考慮到 Shiro 時,你真正要關注的是你的代碼能夠與 Shiro 的 API 正確的運行——你不會想做關於 Shiro 的實現是否工作正常(這是 Shiro 開發團隊在 Shiro 的代碼庫必須確保的東西)的必要測試。

 

測試 Shiro 的實現是否與你的實現協同工作是真實的集成測試(下面討論)。

 

ExampleShiroUnitTest

由於單元測試適用於測試你的邏輯(而不是你可能調用的任何實現),這對於模擬你邏輯所依賴的任何 API 來說是個很好的主意。這能夠與 Shiro 工作得很好——你可以模擬 Subject 實例,並使它反映任何情況下你所需的反應,這些反應是處於測試的代碼做出的。

 

但正如上文所述,在 Shiro 測試中關鍵是要記住在測試執行期間任何 Subject 實例(模擬的或真實的)必須綁定到線程。因此,我們所需要做的是綁定模擬的 Subject 以確保如預期進行。

 

(這個例子使用 EasyMock,但 Mockito 也同樣地工作得很好):

 

import org.apache.shiro.subject.Subject; import org.junit.After;

import org.junit.Test;

 

import static org.easymock.EasyMock.*;

 

/**

* Simple example test class showing how one may perform unit tests for code that requires Shiro APIs

*/

public class ExampleShiroUnitTest extends AbstractShiroTest { @Test

public void testSimple() {

//1. Create a mock authenticated Subject instance for the test to run: Sujbect subjectUnderTest = createNiceMock(Subject.class); expect(subjectUnderTest.isAuthenticated()).andReturn(true);

 

//2. Bind the subject to the current thread: setSubject(subjectUnderTest);

 

//perform test logic here. Any call to

//SecurityUtils.getSubject() directly (or nested in the

//call stack) will work properly.

}

 

@After

public void tearDownSubject() {

//3. Unbind the subject from the current thread: clearSubject();

}

}

 

正如你所看到的,我們沒有設立一個 Shiro SecurityManager 實例或配置一個 Realm 或任何像這樣的東西。我們簡單地創建一個模擬 Subject 實例,並通過調用 setSubject 方法將它綁定到線程。這將確保任何在我們測試代碼中的調用或在代碼中我們正測試的 SecurityUtils.getSubject()正常工作。

 

請注意,setSubject 方法實現將綁定你的模擬 Subject 到線程,且它仍將存在,直到你通過一個不同的 Subject 調用

setSubject 或直到你明確地通過調用 clearSubject()將它從線程中清除。

保持 Subject 綁定到該線程多長時間(或在一個不同的測試中用來交換一個新的實例)取決於你及你的測試需求。

 

tearDownSubject()

在實例中的 tearDownSubject()方法使用了 Junit 4 的註釋來確保該 Subject 在每個測試方法執行後被清除,不管發生什麼。這要求你設立一個新的 Subject 實例並將它設置到每個需要執行的測試中。

 

然而這也不是絕對必要的。例如,你可以只每個測試開始時綁定一個新的 Subject 實例(通過 setSubject),也就是說,使用@Before-annotated 方法。但如果你將要這麼做,你可以同時使用@After tearDownSubject()  方法來保持對稱及'clean'。

 

你可以手動地在每個方法中混合及匹配該 setup/teardown 邏輯或使用@Before 和@After 註釋只要你認爲合適。所有測試完成後,AbstractShiroTest 超類在無論怎樣都會將 Subject 從線程解除綁定,因爲@After 註釋在它的

tearDownShiro()方法中。

 

綜合 Testing

現在我們討論了單元測試的設置,讓我們討論一些關於集成測試的東西。集成測試是指測試跨 API 邊界的實現。例如,測試當調用 B 實現時 A 實現是否工作,且 B 實現是否做它該做的事情。

 

你同樣也可以在 Shiro 中輕鬆地執行集成測試。Shiro 的 SecurityManager 實例及它所包含的東西(如 Realms 和

SessionManager 等)都是佔用很少內存的非常輕量級的 POJO。這意味着你可以爲每一個你執行的測試類創建並銷燬一個 SecurityManager 實例。當你的集成測試運行時,它們將使用“真實的”SecurityManager,且與你應用程序中相像的 Subject 實例將會在運行時使用。

 

ExampleShiroIntegrationTest

下面的實例代碼看起來與上面的單元測試實例幾乎相同,但這 3 個步驟卻有些不同:

  1. 現在有了 step '0',它用來設立一個“真實的”SecurityManager 實例。
  2. Step 1 現在通過 Subject.Builder 構造一個“真實的”Subject 實例,並將它綁定到線程。

 

線程的綁定與解除綁定(step 2 和 3)與單元測試實例中的作用一樣。import org.apache.shiro.config.IniSecurityManagerFactory;

import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory;

import org.junit.After; import org.junit.BeforeClass; import org.junit.Test;

 

public class ExampleShiroIntegrationTest extends AbstractShiroTest { @BeforeClass

public static void beforeClass() {

//0. Build and set the SecurityManager used to build Subject instances used in your tests

 

// This typically only needs to be done once per class if your shiro.ini doesn't change,

// otherwise, you'll need to do this logic in each test that is different Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:test.shiro.ini"); setSecurityManager(factory.getInstance());

}

 

@Test

public void testSimple() {

//1. Build the Subject instance for the test to run:

Subject subjectUnderTest = new Subject.Builder(getSecurityManager()).buildSubject();

 

//2. Bind the subject to the current thread: setSubject(subjectUnderTest);

 

//perform test logic here. Any call to

//SecurityUtils.getSubject() directly (or nested in the

//call stack) will work properly.

}

 

@After

public void tearDownSubject() {

//3. Unbind the subject from the current thread: clearSubject();

}

}

 

正如你所看到的,一個具體的 SecurityManager 實現被實例化,並通過 setSecurityManager 方法使其餘的測試能夠對其進行訪問。然後測試方法能夠使用該 SecurityManager,當使用 Subject.Builder 後通過調用 getSecurityManager()方法。

 

還要注意 SecurityManager  實例在@BeforeClass  設置方法中只被設置一次——一個對於大多數測試類較爲普遍的做法。如果你想,你可以創建一個新的 SecurityManager 實例並在任何時候從任何測試方法通過 setSerurityManager 來設置它——例如,你可能會引用兩個不同的.ini 文件來構建一個根據你的測試需求而來的新 SecurityManager。

 

最後,與單元測試例子一樣,AbstractShiroTest 超類將會清除所有 Shiro 產物(任何存在的 SecurityManager 及 Subject

實例)通過它的@AfterClass tearDownShiro()方法來確保該線程在下個測試類運行時是'clean'的。

 

Lend a hand with documentation

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

 

自定義 Subjects

Understanding Subjects in Apache Shiro(理解 Apache Shiro 中的 Subject)

 

毫無疑問,在 Apache Shiro 中最重要的概念就是 Subject。'Subject'僅僅是一個安全術語,是指應用程序用戶的特定安全的“視圖”。一個 Shiro Subject 實例代表了一個單一應用程序用戶的安全狀態和操作。

 

這些操作包括:

  • authentication(login)
  • authorization(access control)
  • session access
  • logout

 

我們原本希望把它稱爲"User"由於這樣“很有意義”,但是我們決定不這樣做:太多的應用程序現有的 API 已經有自己的 User classes/frameworks,我們不希望和這些起衝突。此外,在安全領域,"Subject"這一詞實際上是公認的術語。

 

Shiro 的 API 爲應用程序提供 Subject 爲中心的編程範式支持。當編碼應用程序邏輯時,大多數應用程序開發人員想知道誰纔是當前正在執行的用戶。雖然應用程序通常能夠通過它們自己的機制(UserService    等)來查找任何用戶, 但涉及到安全性時,最重要的問題是“誰纔是當前的用戶?”。

 

雖然通過使用SecurityManager 可以捕獲任何Subject,但只有基於當前用戶/Subject 的應用程序代碼更自然,更直觀。

The Currently Executing Subject(當前執行的 Subject)

幾乎在所有環境下,你能夠獲得當前執行的 Subject 通過使用 org.apache.shiro.SecurityUtils:

 

 
 

 

getSubject()方法調用一個獨立的應用程序,該應用程序可以返回一個在應用程序特有位置上基於用戶數據的  Subject, 在服務器環境中(如,Web 應用程序),它基於與當前線程或傳入的請求相關的用戶數據上獲得 Subject。

 

當你獲得了當前的 Subject 後,你能夠拿它做些什麼?

如果你想在他們當前的 session 中使事情對用戶變得可用,你可得的他們的 session:

 

 
 

 

Session 是一個 Shiro 的具體實例,它提供了大多數你經常要和 HttpSessions 用到的東西,但有一些額外的好處和一個很大的區別:它不需要一個 HTTP 環境!

 

如果在 Web 應用程序內部部署,默認的 Session 將會是基於 HttpSession 的。但是,在一個非 Web 環境中,像這個簡單的 Quickstart,Shiro 將會默認自動地使用它的 Enterprise Session Management。這意味着你可以在你的應用程序中使用相同的 API,在任何層,無論部署環境。這打開了應用程序的全新世界,由於任何需要 session 的應用程序不再被強迫使用 HttpSession 或 EJB Stateful Session Beans。而且,任何客戶端技術現在能夠共享會話數據。

 

所以,你現在可以獲取一個 Subject 以及他們的 Session。對於真正有用的東西像檢查會怎麼樣呢,如果他們被允許做某些事——如對角色和權限的檢查?

 

嗯,我只能對已知的用戶做這些檢查。我們的 Subject 實例代表了當前的用戶,但誰又是實際上的當前用戶呢?呃, 他們都是匿名的——也就是說,直到他們至少登錄一次。那麼,讓我們像下面這樣做:

 

 

那就是了!它再簡單不過了。

但如果他們的登錄嘗試失敗了會怎麼樣?你可以捕獲各種各樣的具體的異常來告訴你到底發生了什麼:

 

 
 

 

你,作爲應用程序/GUI 開發人員,可以基於異常選擇是否顯示消息給終端用戶(例如,“在系統中沒有與該用戶名對應的帳戶。”)。有許多不同種類的異常供你檢查,或者你可以拋出你自己自定義的異常,這些異常可能是 Shiro還未提供的。有關詳情,請查看 AuthenticationException 的 JavaDoc。

 

 

 
 

好了,現在,我們有了一個登錄的用戶,我們還有什麼可以做的呢? 比方說,他們是誰:

 

我們還可以測試他們是否有特定的角色:

 

 
 

 

 

我們還能夠判斷他們是否有權限對一個確定類型的實體進行操作:

 

 
 

 

此外,我們可以執行一個非常強大的實例級權限檢查——它能夠判斷用戶是否能夠訪問一個類型的具體實例:

 

 
 

 

小菜一碟,對吧?

最後,當用戶完成了對應用程序的使用時,他們可以註銷:

 

 
 

 

這個簡單的 API 包含了 90%的 Shiro 終端用戶在使用 Shiro 時將會處理的東西。

Custom Subject Instances(自定義 Subject 實例)

Shiro 1.0 中添加了一個新特性,能夠在特殊情況下構造自定義/臨時的 subject 實例。

 

 
 

 

當一些“特殊情況”是,這是可以很有用的:

  • 系統啓動/引導——當沒有用戶月系統交互時,代碼應該作爲一個'system'或 daemon 用戶來執行。創建 Subject

實例來代表一個特定的用戶是值得的,這樣引導代碼能夠以該用戶(如 admin)來執行。

鼓勵這種做法是由於它能保證 utility/system 代碼作爲一個普通用戶以同樣的方式執行,以確保代碼是一致的。這使得代碼更易於維護,因爲你不必擔心 system/daemon 方案的自定義代碼塊。

  • 集成測試——你可能想創建Subject 實例,在必要時可以在集成測試中使用。請參閱測試文檔獲取更多的內容。
  • Daemon/background 進程的工作——當一個 daemon 或 background 進程執行時,它可能需要作爲一個特定的用戶來執行。

 

 

好了,假設你仍然需要創建自定義的 Subject 實例的情況下,讓我們看看如何來做:

 

Subject.Builder

Subject.Builder 被制定得非常容易創建 Subject 實例,而無需知道構造細節。

Builder 最簡單的用法是構造一個匿名的,session-less 的實例。

 

 
 

 

上面所展示的默認的 Subject.Builder 無參構造函數將通過 SecurityUtils.getSubject()方法使用應用程序當前可訪問的

SecurityManager。你也可以指定被額外的構造函數使用的 SecurityManager 實例,如果你需要的話:

 

 
 

 

所有其他的 Subject.Builder 方法可以在 buildSubject()方法之前被調用,它們來提供關於如何構造 Subject 實例的上下文。例如,假如你擁有一個 session ID ,想取得“擁有”該 session 的 Subject(假設該 session 存在且未過期):

 

 
 

 

同樣地,如你想創建一個 Subject 實例來反映一個確定的身份:

 

 
 

 

然後,你可以使用構造的 Subject 實例,如預期一樣對它進行調用。但請注意:

 

構造的 Subject 實例不會由於應用程序(線程)的進一步使用而自動地綁定到應用程序(線程)。如果你想讓它對於任何代碼都能夠方便地調用 SecurityUtils.getSubject(),你必須確保創建好的 Subject 有一個線程與之關聯。

 

Thread Association(線程關聯)

如上所述,只是構建一個 Subject 實例,並不與一個線程相關聯——一個普通的必要條件是在線程執行期間任何對

SecurityUtils.getSubject()的調用是否能正常工作。確保一個線程與一個 Subject 關聯有三種途徑:

  • Automatic Association(自動關聯)—— 通過 Sujbect.execute*方法執行一個 Callable 或 Runnable 方法會自動地綁定和解除綁定到線程的 Subject,在 Callable/Runnable 異常的前後。
  • Manual Association(手動關聯)——你可以在當前執行的線程中手動地對 Subject 實例進行綁定和解除綁定。這通常對框架開發人員非常有用。
  • Different Thread(不同的線程)——通過調用 Subject.associateWith*方法將 Callable 或 Runnable 方法關聯到

Subject,然後返回的 Callable/Runnable 方法在另一個線程中被執行。如果你需要爲 Subject 在另一個線程上執行工作的話,這是首選的方法。

 

瞭解線程關聯最重要的是,兩件事情必須始終發生:

  1. Subject 綁定到線程,所以它在線程的所有執行點都是可用的。Shiro 做到這點通過它的 ThreadState  機制,該機制是在 ThreadLocal 上的一個抽象。
  2. Subject 將在某點解除綁定,即使線程的執行結果是錯誤的。這將確保線程保持乾淨,並在 pooled/reusable 線程環境中清除任何之前的 Subject 狀態。

 

這些原則保證在上述三個機制中發生。接下來闡述它們的用法。

Automatic Association(自動關聯)

如果你只需要一個 Subject 暫時與當前的線程相關聯,同時你希望線程綁定和清理自動發生,Subject 的 Callable 或

Runnable 的直接執行正是你所需要的。在 Subject.execute 調用返回後,當前線程被保證當前狀態與執行前的狀態是一樣的。這個機制是這三個中使用最廣泛的。

 

例如,讓我們假定你有一些邏輯在系統啓動時需要執行。你希望作爲一個特定用戶執行代碼塊,但一旦邏輯完成後, 你想確保線程/環境自動地恢復到正常。你可以通過調用 Subject.execute*方法來做到:

 

 
 

 

當然,Callable 的實例也能夠被支持,所以你能夠擁有返回值並捕獲異常:

 

 
 

 

這種方法在框架開發中也是很有用的。例如,Shiro 對 secure Spring remoting 的支持確保了遠程調用能夠作爲一個特定的 Subject 來執行:

 

 

Manual Association(手動關聯)

雖然 Subject.execute*方法能夠在它們返回後自動地清理線程的狀態,但有可能在一些情況下,你想自己管理

ThreadState。當結合 w/Shiro 時,這幾乎總是在框架開發層次使用,但它很少在 bootstrap/daemon 情景下使用(上面 Subject.execute(callable)例子使用得更爲頻繁)。

 

 
 

 

最好的做法是在 try/finally 塊保證清理:

 

 
 

 

有趣的是,這正是 Subject.execute*方法實際上所做的——它們只是在 Callable 或 Runnable 執行前後自動地執行這個邏輯。Shiro 的 ShiroFilter 爲 Web 應用程序執行幾乎相同的邏輯(ShiroFilter 使用 Web 特定的 ThreadState 的實現, 超出了本節的範圍)。

 

 
 

 

默認的線程

如果你有一個 Callable 或 Runnable 實例要以 Subject 來執行,你將自己執行 Callable 或 Runnable(或這將它移交給線程池或執行者或 ExcutorService),你應該使用 Subject.associateWith*方法。這些方法確保在最終執行的線程中保留 Subject,且該 Subject 是可訪問的。

 

Callable 例子:

 

 
 

 

Runnable 例子:

 

 
 
 
 

 

 

Lend a hand with documentation

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

 

Spring Framework

 

I將shiro集成於到基於spring的應用程序中

本頁涵蓋了將 Shiro 集成到基於 Spring 的應用程序的方法。

 

Shiro 的 JavaBean 兼容性使得它非常適合通過 Spring XML 或其他基於 Spring 的配置機制。Shiro 應用程序需要一個具有單例 SecurityManager 實例的應用程序。請注意,這不會是一個靜態的單例,但應該只有一個應用程序能夠使用的實例,無論它是否是靜態單例的。

 

獨立的應用程序

這裏是在 Spring 應用程序中啓用應用程序單例 SecurityManager 的最簡單的方法:

 

 

Web Applications

Shiro 擁有對 Spring Web 應用程序的一流支持。在 Web 應用程序中,所有 Shiro 可訪問的萬惡不請求必須通過一個主要的 Shiro 過濾器。該過濾器本身是極爲強大的,允許臨時的自定義過濾器鏈基於任何 URL 路徑表達式執行。

 

在 Shiro 1.0 之前,你不得不在 Spring web 應用程序中使用一個混合的方式,來定義 Shiro 過濾器及所有它在 web.xml 中的配置屬性,但在 Spring XML 中定義 SecurityManager。這有些令人沮喪,由於你不能把你的配置固定在一個地方, 以及利用更爲先進的 Spring 功能的配置能力,如 PropertyPlaceholderConfigurer 或抽象 bean 來固定通用配置。

 

現在在 Shiro 1.0 及以後版本中,所有 Shiro 配置都是在 Spring XML 中完成的,用來提供更爲強健的 Spring 配置機制。以下是如何在基於 Spring web 應用程序中配置 Shiro:

web.xml

除了其他 Spring web.xml 中的元素(ContextLoaderListener,Log4jConfigListener 等等),定義下面的過濾器及過濾器映射:

 

applicationContext.xml

在你的 applicationContext.xml 文件中,定義 web 支持的 SecurityManager 和'shiroFilter' bean 將會被 web.xml 引用。

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

<property name="securityManager" ref="securityManager"/>

<!-- override these for application-specific URLs if you like:

<property name="loginUrl" value="/login.jsp"/>

<property name="successUrl" value="/home.jsp"/>

<property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->

<!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean -->

<!-- defined will be automatically acquired and available via its beanName in chain -->

<!-- definitions, but you can perform instance overrides or name aliases here if you like: -->

<!-- <propery name="filters">

<util:map>

<entry key="anAlias" value-ref="someFilter"/>

</util:map>

</property> -->

<propery name="filterChainDefinitions">

<value>

# some example chain definitions

/admin/** = authc, roles[admin]

/docs/** = authc, perms[document:read]

/** = authc

# more URL-to-FilterChain definitions here

</value>

</property>

</bean>

 

<!-- Define any javax.servlet.Filter beans you want anywhere in this application context. -->

<!-- They will automatically be acquired by the 'shiroFilter' bean above and made available -->

<!-- to the 'filterChainDefinitions' property. Or you can manully/explicitly add them -->

<!-- to the shiroFilter's 'filter' Map if desired. See its JavaDoc for more details. -->

<bean id="someFilter" class="…"/>

<bean id="anotherFilter" class="…"> … </bean>

 

<bean    id="securityManager"    class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<!-- Single realm app. If you have multiple realms, use the 'realms' property instead. --->

<!-- By default the servlet container sessions will be used. Uncomment this line to use shiro's native sessions (see the JavaDoc for more): -->

<!-- <property name="sessionMode" value="native"/> -->

</bean>

<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

 

<!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->

<!-- security datasource: -->

<bean id="myRealm" class="…">

</bean>

 

Enabling Shiro Annotations

在獨立應用程序和 Web 應用程序中,你可能想爲安全檢查使用 Shiro 的註釋(例如,@RequiresRoles,

@RequiresPermissions 等等)。這需要 Shiro 的 Spring AOP 集成來掃描合適的註解類以及執行必要的安全邏輯。

以下是如何使用這些註解的。只需添加這兩個 bean 定義到 applicationContext.xml 中:

 

 

 

 

 

 

Secure Spring Remoting

Shiro 的 Spring 遠程支持有兩部分:配置客戶端遠程調用和配置服務器接收及處理遠程調用。

 

Server-side Configuration

當一個遠程調用方法到達啓用 Shiro 的服務器時,與該 RPC 調用關聯的 Subject 在線程執行時必須綁定到訪問的接收線程。這是通過在 applicationContext.xml 中定義 SecureRemotInvocationExecutor bean 來完成的:

 

當你定義這個 bean 之後,你必須將其插入到任何你正在用來 export/expose 你服務的遠程 Exporter。Exporter 實現是根據使用的遠程處理機制/協議來定義的。請參閱 Sping 的 Remoting 章節關於定義 Exporter bean 的內容。

 

例如,如果使用基於 HTTP 的遠程調用(注意 secureRemoteInvocationExecutor bean 的相關屬性):

 

 

 

 

 

Client-side Configuration

當遠程調用被執行後,Subject 的識別信息必須附加到遠程調用的負載上使服務器知道是誰作出了該調用。若客戶端是一個基於 Spring 的客戶端,該關聯是通過 Shiro 的 SecureRemoteInvocationFactory 來完成的:

 

在你定義好這個 bean 後,你需要將它插入到你正在使用的基於特定協議的 Spring remoting ProxyFactoryBean 中。

例如,如果你正在使用基於 HTTP 的遠程調用(注意上面定義的 secureRemoteInvocationFactory bean 的相關屬性):

 

 

Lend a hand with documentation

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

 

 

Guice

 

TODO:未翻譯

 

 

Apache Shiro 術語

 

請花 2 分鐘來閱讀和理解它——這很重要。真的。這裏的術語和概念在文檔的任何地方都被涉及到,它將在總體上大大簡化你對 Shiro 和安全的理解。

 

由於所使用的術語使得安全可能令人困惑。我們將通過澄清一些核心概念使生活更容易,你將會看到 Shiro API 是如何很好地反映了它們:

  • Authentication

身份驗證是驗證 Subject 身份的過程——實質上是證明某些人是否真的是他們所說的他們是誰。當認證嘗試成功後,應用程序能夠相信該 subject 被保證是其所期望的。

 

  • Authorization

授權,又稱爲訪問控制,是決定一個 user/Subject 是否被允許做某事的過程。它通常是通過檢查和解釋 Subject

的角色和權限(見下文),然後允許或拒絕到一個請求的資源或功能來完成的。

 

  • Cipher

密碼是進行加密或解密的一種算法。該算法一般依賴於一塊被稱爲 key 的信息。基於不同的 key 的加密算法也是不一樣的,所有解密沒有它是非常困難的。

 

密碼有不同的表現形式。分組密碼致力於符號塊,通常是固定大小的,而流密碼致力於連續的符號流。對稱性密碼加密和解密使用相同的密鑰(key),而非對稱性加密使用不同的密鑰。如果非對稱性加密的密鑰不能從其他地方得到,那麼可以創建公鑰/私鑰對公開共享。

 

  • Credential

憑證是一塊信息,用來驗證 user/Subject 的身份。在認證嘗試期間,一個(或多個)憑證與 Principals(s)被一同提交,來驗證 user/Subject 所提交的確實是所關聯的用戶。證書通常是非常祕密的東西,只有特定的

user/Subject 才知道,如密碼或 PGP 密鑰或生物屬性或類似的機制。

 

這個想法是爲 principal 設置的,只有一個人會知道正確的證書來“匹配”該 principal。如果當前 user/Subject 提供了正確的憑證匹配了存儲在系統中的,那麼系統可以假定並信任當前 user/Subject 是真的他們所說的他們是誰。信任度隨着更安全的憑證類型加深(如,生物識別簽名 > 密碼)。

 

  • Cryptography

加密是保護信息不受不希望的訪問的習慣做法,通過隱藏信息或將它轉化成無意義的東西,這樣沒人可以理解它。Shiro 致力於加密的兩個核心要素:加密數據的密碼,如使用公鑰或私鑰的郵件,以及散列表(也稱消息摘要),它對數據進行不可逆的加密,如密碼。

 

  • Hash

散列函數是單向的,不可逆轉的輸入源,有時也被稱爲消息,在一個編碼的哈希值內部,有時也被稱爲消息摘要。它通常用於密碼,數字指紋,或以字節數組爲基礎的數據。

 

  • Permission

權限,至少按照 Shiro 的解釋,是在應用程序中描述原始功能的一份聲明並沒有更多的功能。權限是在安全策略中最低級別的概念。它們僅定義了應用程序能夠做“什麼”。它們沒有說明“誰”能夠執行這些操作。權限只是行爲的聲明,僅此而已。

 

一些權限的例子:

    • 打開文件
    • 瀏覽'/user/list'頁面
    • 打印文檔
    • 刪除'jsmith'用戶
  • Principal

Principal 是一個應用程序用戶(Subject)的任何標誌屬性。“標誌屬性”可以是任何對你應用程序有意義的東西——用戶名,姓,名,社會安全號碼,用戶 ID 等。這就是它——沒什麼古怪的。

 

Shiro 也引用一些我們稱之爲 Subject 的 primary principal 的東西。一個 primary principal 是在整個應用程序中唯一標識 Subject 的 principal。理想的 primary principal 是用戶名或 RDBMS 用戶表主鍵——用戶 ID。對於在應用程序中的用戶(Subject)來說,只有一個 primary principal

 

 

  • Realm

Realm 是一個能夠訪問應用程序特定的安全數據(如用戶,角色和權限)的組件。它可以被看作是一個特定安全的 DAO(Data Access Object)。Realm 將這些應用程序特定的數據轉換成 Shiro 能夠理解的格式,這樣 Shiro 反過來能夠提供一個單一的易於理解的 Subject 編程 API,無論有多少數據源存在或無論你的數據是什麼樣的應用程序特定的格式。

 

Realm    通常和數據源是一對一的對應關係,如關係數據庫,LDAP    目錄,文件系統,或其他類似資源。因此,

Realm 接口的實現使用數據源特定的 API 來展示授權數據(角色,權限等),如 JDBC,文件 IO,Hibernate 或

JPA,或其他數據訪問 API。

 

  • Role

基於你對話的對象,一個角色的定義是可以多變的。在許多應用程序中,它充其量是個模糊不清的概念,人們用它來隱式定義安全策略。Shiro 偏向於把角色簡單地解釋爲一組命名的權限的集合。這就是它——一個應用程序的唯一名稱,聚集一個或多個權限聲明。

 

這是一個比許多應用程序使用的隱式的定義更爲具體的定義。如果你選擇了你的數據模型反映 Shiro 的假設, 你會發現將有更多控制安全策略的權力。

 

  • Session

會話是一個在一段時間內有狀態的數據,其上下文與一個單一的與軟件系統交互的 user/Subject 相關聯。當

Subject 使用應用程序時,能夠從會話中添加/讀取/刪除數據,並且應用程序稍後能夠在需要的地方使用該數據。會話會被終止,由於 user/Subject 註銷或會話不活動而超時。

 

對於那些熟悉 HttpSession 的,Shiro Session 服務於同一目標,除了 Shiro 會話能夠在任何環境下使用,甚至在沒有 Servlet 容器或 EJB 容器的環境。

 

  • Subject

Subject 只是一個精挑細選的安全術語,基本上的意思是一個應用程序用戶的安全特定的“視圖”。然而 Subject 不總是需要反映爲一個人——它可以代表一個調用你應用程序的外部進程,或許是一個系統帳戶的守護進程, 在一段時間內執行一些間歇性的東西(如一個 cron job)。它基本上是任何使用應用程序做某事的實體的一個代表。

 

Lend a hand with documentation

我們希望本文檔可以幫助你及你用 Apache Shiro 所做的工作,我們的團體在不斷改善和擴展該文檔。如果你想幫助

Shiro 項目,請考慮修改,擴展,或者添加文檔到你認爲需要的地方。您的每一點幫助都壯大了我們的團體,從而改進了 Shiro。

貢獻您的文檔最簡單的方法是將其發送到用戶論壇或者用戶的通訊錄。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章