目錄
一、Shiro簡介
Apache
Shiro 是 Java 的一個安全(權限)框架
。- Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在JavaSE 環境,也可以用在 JavaEE 環境。• Shiro 可以完成:認證、授權、加密、會話管理、與Web 集成、緩存等。
- 下載: http://shiro.apache.org/
1、功能簡介
Authentication
:身份認證/(登錄),驗證用戶是不是擁有相應的身份;Authorization
:授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能進行什麼操作,如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個權限;Session Manager
:會話管理,即用戶登錄後就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通 JavaSE 環境,也可以是 Web 環境的;Cryptography
:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;Web Support
:Web 支持,可以非常容易的集成到Web 環境;Caching
:緩存,比如用戶登錄後,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;Concurrency
:Shiro 支持多線程應用的併發驗證,即如在一個線程中開啓另一個線程,能
把權限自動傳播過去;Testing
:提供測試支持;Run As
:允許一個用戶假裝爲另一個用戶(如果他們允許)的身份進行訪問;Remember Me
:記住我,這個是非常常見的功能,即一次登錄後,下次再來的話不用登錄了
2、Shiro 架構
跳轉到目錄
從外部來看Shiro ,即從應用程序角度的來觀察如何使用 Shiro 完成工作:
Subject
:應用代碼直接交互的對象是 Subject,也就是說 Shiro 的對外API 核心就是 Subject。Subject 代表了當前“用戶”, 這個用戶不一定是一個具體的人,與當前應用交互的任何東西都是 Subject,如網絡爬蟲,機器人等;與 Subject 的所有交互都會委託給 SecurityManager;Subject 其實是一個門面,SecurityManager 纔是實際的執行者;SecurityManager
:安全管理器
;即所有與安全有關的操作都會與SecurityManager 交互;且其管理着所有 Subject;可以看出它是Shiro的核心
,它負責與 Shiro 的其他組件進行交互,它相當於 SpringMVC 中DispatcherServlet 的角色Realm
:Shiro 從 Realm 獲取安全數據(如用戶、角色、權限),就是說SecurityManager 要驗證用戶身份,那麼它需要從 Realm 獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應的角色/權限
進行驗證用戶是否能進行操作;可以把 Realm 看成 DataSource
從內部來看Shiro架構
- Subject:任何可以與應用交互的
“用戶”
; - SecurityManager :相當於SpringMVC 中的 DispatcherServlet;是 Shiro 的心臟;所有具體的交互都通過 SecurityManager 進行控制;它管理着所有 Subject、且負責進行認證、授權、會話及緩存的管理。
Authenticator
:負責 Subject 認證,是一個擴展點,可以自定義實現;可以使用認證策略
(Authentication Strategy),即什麼情況下算用戶認證通過了;Authorizer
:授權器、即訪問控制器,用來決定主體是否有權限進行相應的操作;即控制着用戶能訪問應用中的哪些功能;- Realm:可以有 1 個或多個 Realm,可以認爲是安全實體數據源,即用於獲取安全實體的;可以是JDBC 實現,也可以是內存實現等等;由用戶提供;所以一般在應用中都需要
實現自己的 Realm
; SessionManager
:管理 Session 生命週期的組件;而 Shiro 並不僅僅可以用在 Web 環境,也可以用在如普通的 JavaSE 環境- CacheManager:緩存控制器,來管理如用戶、角色、權限等的緩存的;因爲這些數據基本上很少改變,放到緩存中後可以提高訪問的性能
Cryptography
:密碼模塊,Shiro 提高了一些常見的加密組件用於如密碼加密/解密。
二、Shiro的HelloWorld程序
跳轉到目錄
首先創建一個普通的maven項目
1、在官網/github中下載最新穩定版的"zip"壓縮包,打開之後找到之中的示例文件夾"samples"
將上圖中resources中的log4j的配置文件
和shiro.ini
文件拷到項目的resources下, 將java中的Quickstart.java
拷貝到我們的項目中;
2、導入pom.xml中的座標
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!-- shiro默認使用的日誌框架是, commons-logging -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
3、shiro.ini、log4j.properties文件
shiro.ini文件
log4j.properties文件
3、Quickstart類的分析
/**
* Simple Quickstart application showing how to use Shiro's API.
*
* @since 0.9 RC2
*/
public class Quickstart {
// 使用日誌門面
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// 從classpath路徑下, 加載shiro.ini文件,創建一個生產SecurityManager工廠
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
// 通過這個工程生產SecurityManager實例
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 獲取當前的用戶對象subject
Subject currentUser = SecurityUtils.getSubject();
// 通過當前用戶拿到shiro的session--測試session
Session session = currentUser.getSession();
session.setAttribute("someKey", "桂朝陽");
String value = (String) session.getAttribute("someKey");
if (value.equals("桂朝陽")) {
log.info("Retrieved the correct value! [" + value + "]");
}
// 判斷當前的用戶是否被認證(即是否已經登錄)
if (!currentUser.isAuthenticated()) {
// 沒有登錄,把用戶名和密碼封裝爲UsernamePasswordToken對象
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true); // 設置記住我
try {
// 執行登錄,上面token中的用戶名密碼在shiro.ini中已經配置,可以登錄成功
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 (AuthenticationException ae) {
//unexpected condition? error?
}
}
// 獲取當前用戶名稱(這裏是lonestarr,已經存在了,在shiro.ini中)
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
// 當前用戶是否有`schwartz`這個角色(可以在shiro.ini中看到,`lonestarr`用戶有這個角色
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
// 粗粒度的訪問某資源(可以看shiro.ini中第二種角色: 訪問權限分的不是很細緻)
// 判斷當前用戶是否擁有"lightsaber:wield"權限
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
// 細粒度 (可以看shiro.ini中第三種角色: 訪問權限分的比較細緻)
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!");
}
// 註銷
currentUser.logout();
// 結束
System.exit(0);
}
}
總結:
// 獲取當前的用戶對象subject
Subject currentUser = SecurityUtils.getSubject();
//使用Session做一些操作,此時不需要web容器也可以進行
Session session = currentUser.getSession();
// 判斷當前的用戶是否被認證(是否已經登錄)
currentUser.isAuthenticated();
// 獲取當前用戶名稱(這裏是lonestarr,已經存在了,在shiro.ini中)
currentUser.getPrincipal();
// 當前用戶是否有schwartz這個角色
currentUser.hasRole(“schwartz”);
// 判斷當前用戶是否擁有"lightsaber:wield"權限
currentUser.isPermitted(“lightsaber:wield”);
// 註銷
currentUser.logout();