What is Apache Shiro? Apache Shiro是一個功能強大、靈活的,開源的安全框架。它可以乾凈利落地處理身份驗證、授權、企業會話管理和加密。 Apache Shiro的首要目標是易於使用和理解。安全通常很複雜,甚至讓人感到很痛苦,但是Shiro卻不是這樣子的。一個好的安全框架 ...
What is Apache Shiro?
Apache Shiro是一個功能強大、靈活的,開源的安全框架。它可以乾凈利落地處理身份驗證、授權、企業會話管理和加密。
Apache Shiro的首要目標是易於使用和理解。安全通常很複雜,甚至讓人感到很痛苦,但是Shiro卻不是這樣子的。一個好的安全框架應該屏蔽複雜性,向外暴露簡單、直觀的API,來簡化開發人員實現應用程式安全所花費的時間和精力。
Shiro能做什麼呢?
- 驗證用戶身份
- 用戶訪問許可權控制,比如:1、判斷用戶是否分配了一定的安全形色。2、判斷用戶是否被授予完成某個操作的許可權
- 在非 web 或 EJB 容器的環境下可以任意使用Session API
- 可以響應認證、訪問控制,或者 Session 生命周期中發生的事件
- 可將一個或以上用戶安全數據源數據組合成一個複合的用戶 "view"(視圖)
- 支持單點登錄(SSO)功能
- 支持提供“Remember Me”服務,獲取用戶關聯信息而無需登錄
…
等等——都集成到一個有凝聚力的易於使用的API。
Shiro 致力在所有應用環境下實現上述功能,小到命令行應用程式,大到企業應用中,而且不需要藉助第三方框架、容器、應用伺服器等。當然 Shiro 的目的是儘量的融入到這樣的應用環境中去,但也可以在它們之外的任何環境下開箱即用。
Apache Shiro Features 特性
Apache Shiro是一個全面的、蘊含豐富功能的安全框架。下圖為描述Shiro功能的框架圖:
Authentication(認證), Authorization(授權), Session Management(會話管理), Cryptography(加密)被 Shiro 框架的開發團隊稱之為應用安全的四大基石。那麼就讓我們來看看它們吧:
- Authentication(認證):用戶身份識別,通常被稱為用戶“登錄”
- Authorization(授權):訪問控制。比如某個用戶是否具有某個操作的使用許可權。
- Session Management(會話管理):特定於用戶的會話管理,甚至在非web 或 EJB 應用程式。
- Cryptography(加密):在對數據源使用加密演算法加密的同時,保證易於使用。
還有其他的功能來支持和加強這些不同應用環境下安全領域的關註點。特別是對以下的功能支持:
- Web支持:Shiro 提供的 web 支持 api ,可以很輕鬆的保護 web 應用程式的安全。
- 緩存:緩存是 Apache Shiro 保證安全操作快速、高效的重要手段。
- 併發:Apache Shiro 支持多線程應用程式的併發特性。
- 測試:支持單元測試和集成測試,確保代碼和預想的一樣安全。
- "Run As":這個功能允許用戶假設另一個用戶的身份(在許可的前提下)。
- "Remember Me":跨 session 記錄用戶的身份,只有在強制需要時才需要登錄。
註意: Shiro不會去維護用戶、維護許可權,這些需要我們自己去設計/提供,然後通過相應的介面註入給Shiro。
High-Level Overview 高級概述
在概念層,Shiro 架構包含三個主要的理念:Subject,SecurityManager和 Realm。下麵的圖展示了這些組件如何相互作用,我們將在下麵依次對其進行描述。
- Subject:當前用戶,Subject 可以是一個人,但也可以是第三方服務、守護進程帳戶、時鐘守護任務或者其它--當前和軟體交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架構的核心,配合內部安全組件共同組成安全傘。
- Realms:用於進行許可權信息的驗證,我們自己實現。Realm 本質上是一個特定的安全 DAO:它封裝與數據源連接的細節,得到Shiro 所需的相關的數據。在配置 Shiro 的時候,你必須指定至少一個Realm 來實現認證(authentication)和/或授權(authorization)。
我們需要實現Realms的Authentication 和 Authorization。其中 Authentication 是用來驗證用戶身份,Authorization 是授權訪問控制,用於對用戶進行的操作授權,證明該用戶是否允許進行當前操作,如訪問某個鏈接,某個資源文件等。
快速上手
第一步:
話不多說,我們先構建一個最簡單的項目,結構如下:
第二步:
編輯shiro.ini
這裡面定義了和安全相關的數據: 用戶,角色和許可權
註釋很詳細了,挨個看就能理解了
#定義用戶 [users] #用戶名 zhang3 密碼是 12345, 角色是 admin zhang3 = 12345, admin #用戶名 li4 密碼是 abcde, 角色是 產品經理 li4 = abcde,productManager #定義角色 [roles] #管理員什麼都能做 admin = * #產品經理只能做產品管理 productManager = addProduct,deleteProduct,editProduct,updateProduct,listProduct #訂單經理只能做訂單管理 orderManager = addOrder,deleteOrder,editOrder,updateOrder,listOrder
第三步:
準備用戶類User,用於存放賬號密碼
public class User { private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
第四步:
編輯TestShiro
準備3個用戶,前兩個能在 shiro.ini 中找到,第3個不存在
然後測試登錄
接著測試是否包含角色
最後測試是否擁有許可權
註:Subject 在 Shiro 這個安全框架下, Subject 就是當前用戶
1 public class TestShiro { 2 public static void main(String[] args) { 3 //用戶們 4 User zhang3 = new User(); 5 zhang3.setName("zhang3"); 6 zhang3.setPassword("12345"); 7 8 User li4 = new User(); 9 li4.setName("li4"); 10 li4.setPassword("abcde"); 11 12 13 User wang5 = new User(); 14 wang5.setName("wang5"); 15 wang5.setPassword("wrongpassword"); 16 17 List<User> users = new ArrayList<>(); 18 19 users.add(zhang3); 20 users.add(li4); 21 users.add(wang5); 22 //角色們 23 String roleAdmin = "admin"; 24 String roleProductManager ="productManager"; 25 26 List<String> roles = new ArrayList<>(); 27 roles.add(roleAdmin); 28 roles.add(roleProductManager); 29 30 //許可權們 31 String permitAddProduct = "addProduct"; 32 String permitAddOrder = "addOrder"; 33 34 List<String> permits = new ArrayList<>(); 35 permits.add(permitAddProduct); 36 permits.add(permitAddOrder); 37 38 39 40 41 42 //登陸每個用戶 43 for (User user : users) { 44 if(login(user)) 45 System.out.printf("%s \t成功登陸,用的密碼是 %s\t %n",user.getName(),user.getPassword()); 46 else 47 System.out.printf("%s \t成功失敗,用的密碼是 %s\t %n",user.getName(),user.getPassword()); 48 } 49 50 51 System.out.println("-------how2j 分割線------"); 52 53 //判斷能夠登錄的用戶是否擁有某個角色 54 for (User user : users) { 55 for (String role : roles) { 56 if(login(user)) { 57 if(hasRole(user, role)) 58 System.out.printf("%s\t 擁有角色: %s\t%n",user.getName(),role); 59 else 60 System.out.printf("%s\t 不擁有角色: %s\t%n",user.getName(),role); 61 } 62 } 63 } 64 System.out.println("-------how2j 分割線------"); 65 66 //判斷能夠登錄的用戶,是否擁有某種許可權 67 for (User user : users) { 68 for (String permit : permits) { 69 if(login(user)) { 70 if(isPermitted(user, permit)) 71 System.out.printf("%s\t 擁有許可權: %s\t%n",user.getName(),permit); 72 else 73 System.out.printf("%s\t 不擁有許可權: %s\t%n",user.getName(),permit); 74 } 75 } 76 } 77 } 78 79 private static boolean hasRole(User user, String role) { 80 Subject subject = getSubject(user); 81 return subject.hasRole(role); 82 } 83 84 private static boolean isPermitted(User user, String permit) { 85 Subject subject = getSubject(user); 86 return subject.isPermitted(permit); 87 } 88 89 private static Subject getSubject(User user) { 90 //載入配置文件,並獲取工廠 91 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); 92 //獲取安全管理者實例 93 SecurityManager sm = factory.getInstance(); 94 //將安全管理者放入全局對象 95 SecurityUtils.setSecurityManager(sm); 96 //全局對象通過安全管理者生成Subject對象 97 Subject subject = SecurityUtils.getSubject(); 98 99 return subject; 100 } 101 102 103 private static boolean login(User user) { 104 Subject subject= getSubject(user); 105 //如果已經登錄過了,退出 106 107 if(subject.isAuthenticated()) { 108 109 subject.logout(); 110 } 111 112 //封裝用戶的數據 113 UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getPassword()); 114 try { 115 //將用戶的數據token 最終傳遞到Realm中進行對比 116 subject.login(token); 117 } catch (AuthenticationException e) { 118 //驗證錯誤 119 return false; 120 } 121 122 return subject.isAuthenticated(); 123 } 124 125 126 127 128 }代碼行數較多,點擊展開
第五步:
運行 TestShiro,可以觀察到如圖所示的效果
某個用戶是否登陸成功
某個用戶是否擁有某個角色
某個用戶是否擁有某種許可權
最後
代碼下載地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/shiro.rar
在這裡,賬號密碼,角色信息都是放在配置文件里的,真實工作的時候,肯定都是放在資料庫里的。那麼怎麼做呢?請看下期!!!