什么是Shiro?
Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。使用Shiro的易於理解的API,您可以快速、輕松地獲得任何應用程序,從最小的移動應用程序到最大的網絡和企業應用程序。
shiro不依賴於spring,shiro不僅可以實現 web應用的權限管理,還可以實現c/s系統,分布式系統權限管理,shiro屬於輕量框架,越來越多企業項目開始使用shiro。
Shiro組成部分

- subject:主體,可以是用戶也可以是程序,主體要訪問系統,系統需要對主體進行認證、授權。
- securityManager:安全管理器,主體進行認證和授權都是通過securityManager進行。securityManager是一個集合,真正做事的不是securityManager而是它里面的東西。
- authenticator:認證器,主體進行認證最終通過authenticator進行的。
- authorizer:授權器,主體進行授權最終通過authorizer進行的。
- sessionManager:web應用中一般是用web容器(中間件tomcat)對session進行管理,shiro也提供一套session管理的方式。
- shiro不僅僅可以用於web管理也可以用於cs管理,所以他不用web容器的session管理。
- SessionDao: 通過SessionDao管理session數據,針對個性化的session數據存儲需要使用sessionDao(如果用tomcat管理session就不用SessionDao,如果要分布式的統一管理session就要用到SessionDao)。
- cache Manager:緩存管理器,主要對session和授權數據進行緩存(權限管理框架主要就是對認證和授權進行管理,session是在服務器緩存中的),比如將授權數據通過cacheManager進行緩存管理,和ehcache整合對緩存數據進行管理(redis是緩存框架)。
- realm:域,領域,相當於數據源,通過realm存取認證、授權相關數據(原來是通過數據庫取的)。注意:authenticator認證器和authorizer授權器調用realm中存儲授權和認證的數據和邏輯。
- cryptography:密碼管理,比如md5加密,提供了一套加密/解密的組件,方便開發。比如提供常用的散列、加/解密等功能。比如 md5散列算法(md5只有加密沒有解密)。
核心組件
在shiro中核心的概念有三個:
1.subject
即“當前操作用發戶”。但是,在Shiro中,Subject這一概念並不僅僅指人,也可以是第三方進程(比如微信QQ等等)、后台帳戶(Daemon Account)或其他類似事物。它僅僅意味着“當前跟軟件交互的東西”。但考慮到大多數目的和用途,你可以把它認為是Shiro的“用戶”概念。Subject代表了當前用戶的安全操作,SecurityManager則管理所有用戶的安全操作。
2.Security Manager
它是Shiro框架的核心,典型的Facade模式,Shiro通過SecurityManager來管理內部組件實例,並通過它來提供安全管理的各種服務,且它管理着所有Subject;可以看出它是Shiro 的核心,它負責與后邊介紹的其他組件進行交互,如果學習過SpringMVC,你可以把它看成DispatcherServlet前端控制器。
3.Realm
域,Shiro從從Realm獲取安全數據(如用戶、角色、權限),就是說SecurityManager要驗證用戶身份,那么它需要從Realm獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從Realm得到用戶相應的角色/權限進行驗證用戶是否能進行操作;可以把Realm看成DataSource,即安全數據源,同時也可以是配置文件。
從這個意義上講,Realm實質上是一個安全相關的DAO:它封裝了數據源的連接細節,並在需要時將相關數據提供給Shiro。當配置Shiro時,你必須至少指定一個Realm,用於認證和(或)授權。配置多個Realm是可以的,但是至少需要一個。
Shiro 特點
(1)易於理解的 Java Security API;
(2)簡單的身份認證(登錄),支持多種數據源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
(3)對角色的簡單的簽權(訪問控制),支持細粒度的簽權;
(4)支持一級緩存,以提升應用程序的性能;
(5)內置的基於 POJO 企業會話管理,適用於 Web 以及非 Web 的環境;
(6)異構客戶端會話訪問;
(7)非常簡單的加密 API,自帶的加密API完全夠用;
(8)不跟任何的框架或者容器捆綁,可以獨立運行
實現簡單案例
1.添加依賴:
<!-- shiro核心包 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <!-- 添加shiro web支持 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency>
2.添加安全數據源shiro-permission.ini文件(官方推薦使用ini文件格式):
[users] zs=123,role1,role2,role3 ls=123,role2 ww=123,role3 admin=123,role1,role2,role3,admin [roles] role1=user:create,user:update,user:delete,user:view,user:load role2=user:create,user:delete role3=user:create admin=user:*
3.添加Demo測試類:
package com.star.demo; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Demo { public static void main(String[] args) { //org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token 密碼錯誤認證失敗 //org.apache.shiro.authc.UnknownAccountException: Realm [org.apache.shiro...] was unable to find account data // 賬戶不存在錯誤 //1:獲取securityFactory工廠類 IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:shiro" + "-permission" + ".ini"); //2.獲取安全管理器對象 SecurityManager instance = iniSecurityManagerFactory.getInstance(); //3.設置subjectManager對象,把subjec對象交給SecurityUtils管理 SecurityUtils.setSecurityManager(instance); //4.獲取subject對象 Subject subject = SecurityUtils.getSubject(); //5.生成token UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("admin","123"); //6.進行驗證 try { //7.登陸(身份認證) subject.login(usernamePasswordToken); System.out.println("身份認證成功!"); } catch (AuthenticationException e) { e.printStackTrace(); System.out.println("身份認證失敗!"); } //8.用戶授權 try { // if(subject.hasRole("role6")){//1.普通授權方式,檢查單個授權 //2.傳集合用戶授權,有一個未授權的就返回false // List<String> list= new ArrayList<>(); // list.add("role1"); // list.add("role6"); // list.add("role3"); // if(subject.hasAllRoles(list)){//傳集合判斷權限 //3.hasRoles方式判斷權限 boolean[] booleans = subject.hasRoles(Arrays.asList("role1", "role2", "role3")); boolean f = false; int n = 0; for (int i = booleans.length - 1; i >= 0; i--) { if(booleans[i]) ++n; if(n==booleans.length){ f = true; break; } } if(f){ System.out.println("用戶授權成功!"); }else { System.out.println("用戶未授權!"); } } catch (Exception e) { e.printStackTrace(); } //9.check方式檢查授權,未授權直接報錯 try { //1.checkRole方式不存在權限直接報錯org.apache.shiro.authz.UnauthorizedException: Subject does not have role [role7] // subject.checkRole("role7"); //2.checkRoles方式不存在直接報錯,可以傳可變的String參數或者是數組 subject.checkRoles(Arrays.asList("role1","role2","role3")); subject.checkRoles("role1","role2","role3"); System.out.println("用戶已授權~"); } catch (Exception e) { e.printStackTrace(); System.out.println("用戶未授權!"); } //10.permission方式檢查權限 try { // //isPermitted可以接受String可變參數,集合等 // if(subject.isPermitted("user:*")) // System.out.println("用戶已授權!"); // else // System.out.println("用戶未授權!"); //checkPermitted方式(*代表所有權限不管權限是否存在) subject.checkPermission("user:234234"); System.out.println("用戶已授權!"); } catch (Exception e) { e.printStackTrace(); System.out.println("用戶未授權!"); } } }
