受權:也叫訪問控制,即在應用中控制誰能訪問哪些資源(如訪問頁面/編輯數據/頁面操做等)。數據庫
主體:即訪問應用的用戶,在Shiro中使用Subject表明該用戶。用戶只有受權後才容許訪問相應的資源。編程
資源:在應用中用戶能夠訪問的 URL,好比訪問 JSP 頁面、查看/編輯某些數據、訪問某個業務方法、打印文本等等都是資源。用戶只要受權後才能訪問。安全
權限:安全策略中的原子受權單位,經過權限咱們能夠表示在應用中用戶有沒有操做某個資源的權力。即權限表示在應用中用戶能不能訪問某個資源。ide
角色:表明了操做集合,能夠理解爲權限的集合,通常狀況下咱們會賦予用戶角色而不是權限,即這樣用戶能夠擁有一組權限,賦予權限時比較方便。測試
隱式角色:即直接經過角色來驗證用戶有沒有操做權限,如在應用中CTO、技術總監、開發工程師可使用打印機,假設某天不容許開發工程師使用打印機,此時須要從應用中刪除相應代碼;再如在應用中CTO、技術總監能夠查看用戶、查看權限;忽然有一天不容許技術總監查看用戶、查看權限了,須要在相關代碼中把技術總監角色從判斷邏輯中刪除掉;即粒度是以角色爲單位進行訪問控制的,粒度較粗;若是進行修改可能形成多處代碼修改。ui
顯示角色:在程序中經過權限控制誰能訪問某個資源,角色聚合一組權限集合;這樣假設哪一個角色不能訪問某個資源,只須要從角色表明的權限集合中移除便可;無須修改多處代碼;即粒度是以資源/實例爲單位的;粒度較細。spa
編程式:經過寫if/else受權代碼塊完成: 3d
Subject subject = SecurityUtils.getSubject(); if(subject.hasRole(「admin」)) { //有權限 } else { //無權限 }
註解式:經過在執行的Java方法上放置相應的註解完成: code
@RequiresRoles("admin") public void hello() { //有權限 }
JSP/GSP標籤:在JSP/GSP頁面經過相應的標籤完成:orm
<shiro:hasRole name="admin"> <!— 有權限 —> </shiro:hasRole>
基於角色的訪問控制(隱式角色)
一、在ini配置文件配置用戶擁有的角色(shiro-role.ini)
[users] zhang=123,role1,role2 wang=123,role1
規則即:「用戶名=密碼,角色1,角色2」,若是須要在應用中判斷用戶是否有相應角色,就須要在相應的Realm中返回角色信息,也就是說Shiro不負責維護用戶-角色信息,須要應用提供,Shiro只是提供相應的接口方便驗證,後續會介紹如何動態的獲取用戶角色。
二、測試用例
//Shiro提供了用於判斷用戶是否擁有某個角色的方法;
@Test public void testHasRole() { login("classpath:shiro-role.ini", "zhang", "123"); //判斷擁有角色:role1 Assert.assertTrue(subject().hasRole("role1")); //判斷擁有角色:role1 and role2 Assert.assertTrue(subject().hasAllRoles(Arrays.asList("role1", "role2"))); //判斷擁有角色:role1 and role2 and !role3 boolean[] result = subject().hasRoles(Arrays.asList("role1", "role2", "role3")); Assert.assertEquals(true, result[0]); Assert.assertEquals(true, result[1]); Assert.assertEquals(false, result[2]); } //Shiro也提供了用於斷言用戶是否擁有某個角色的方法; @Test public void testCheckRole() { login("classpath:shiro-role.ini", "zhang", "123"); //斷言擁有角色:role1 subject().checkRole("role1"); //斷言擁有角色:role1 and role3 失敗拋出異常 subject().checkRoles("role1", "role3"); }
基於資源的訪問控制(顯示角色)
一、在ini配置文件配置用戶擁有的角色及角色-權限關係(shiro-permission.ini)
[users] zhang=123,role1,role2 wang=123,role1 [roles] role1=user:create,user:update role2=user:create,user:delete
規則:「用戶名=密碼,角色1,角色2」「角色=權限1,權限2」,即首先根據用戶名找到角色,而後根據角色再找到權限;即角色是權限集合;Shiro一樣不進行權限的維護,須要咱們經過Realm返回相應的權限信息。只須要維護「用戶——角色」之間的關係便可。
二、測試用例
//Shiro提供了用於判斷用戶是否擁有某個權限或全部權限 @Test public void testIsPermitted() { login("classpath:shiro-permission.ini", "zhang", "123"); //判斷擁有權限:user:create Assert.assertTrue(subject().isPermitted("user:create")); //判斷擁有權限:user:update and user:delete Assert.assertTrue(subject().isPermittedAll("user:update", "user:delete")); //判斷沒有權限:user:view Assert.assertFalse(subject().isPermitted("user:view")); } //Shiro提供了用於斷言用戶是否擁有某個權限或全部權限 @Test(expected = UnauthorizedException.class) public void testCheckPermission () { login("classpath:shiro-permission.ini", "zhang", "123"); //斷言擁有權限:user:create subject().checkPermission("user:create"); //斷言擁有權限:user:delete and user:update subject().checkPermissions("user:delete", "user:update"); //斷言擁有權限:user:view 失敗拋出異常 subject().checkPermissions("user:view"); }
規則:「資源標識符:操做:對象實例ID」 即對哪一個資源的哪一個實例能夠進行什麼操做。其默認支持通配符權限字符串,「:」表示資源/操做/實例的分割;「,」表示操做的分割;「*」表示任意資源/操做/實例。
1、單個資源單個權限
subject().checkPermissions("system:user:update");
用戶擁有資源「system:user」的「update」權限。
2、單個資源多個權限
ini配置文件
role4=system:user:update,system:user:delete
而後經過以下代碼判斷
subject().checkPermissions("system:user:update","system:user:delete");
用戶擁有資源「system:user」的「update」和「delete」權限。如上能夠簡寫成:
role4="system:user:update,delete"
3、單個資源所有權限
ini配置文件
role51="system:user:create,update,delete,view"
而後經過以下代碼判斷
subject().checkPermissions("system:user:create,delete,update:view");
用戶擁有資源「system:user」的「create」、「update」、「delete」和「view」全部權限。如上能夠簡寫成:
或者:role52=system:user:*
或者:role53=system:user
而後經過以下代碼判斷
subject().checkPermissions("system:user:*");
subject().checkPermissions("system:user");
4、全部資源所有權限
ini配置文件
role61=*:view
而後經過以下代碼判斷
subject().checkPermissions("user:view");
用戶擁有全部資源的「view」全部權限。假設判斷的權限是「"system:user:view」,那麼須要「role5=*:*:view」這樣寫才行。
5、實例級別的權限
5.1、單個實例單個權限
ini配置 文件
role71=user:view:1
對資源user的1實例擁有view權限。
而後經過以下代碼判斷
subject().checkPermissions("user:view:1");
5.2、單個實例多個權限
ini配置 文件
role72=user:update,delete:1
對資源user的1實例擁有update、delete權限。
而後經過以下代碼判斷
subject().checkPermissions("user:update,delete:1 ");
subject().checkPermissions("user:update:1,user:delete:1 ");
5.3、單個實例全部權限
ini配置 文件
role71=user:*:1
對資源user的1實例擁有view權限。
而後經過以下代碼判斷
subject().checkPermissions("user:view,update,delete,create:1 ");
5.4、全部實例單個權限
ini配置 文件
role74=user:auth:*
對資源user的1實例擁有全部權限。
而後經過以下代碼判斷
subject().checkPermissions("user:auth:1", "user:auth:2");
5.5、全部實例全部權限
ini配置 文件
role74=user:*:*
對資源user的1實例擁有全部權限。
而後經過以下代碼判斷
subject().checkPermissions("user:view:1", "user:auth:2");
流程以下:
一、首先調用Subject.isPermitted*/hasRole*接口,其會委託給SecurityManager,而SecurityManager接着會委託給Authorizer;
二、Authorizer是真正的受權者,若是咱們調用如isPermitted(「user:view」),其首先會經過PermissionResolver把字符串轉換成相應的Permission實例;
三、在進行受權以前,其會調用相應的Realm獲取Subject相應的角色/權限用於匹配傳入的角色/權限;
四、Authorizer會判斷Realm的角色/權限是否和傳入的匹配,若是有多個Realm,會委託給ModularRealmAuthorizer進行循環判斷,若是匹配如isPermitted*/hasRole*會返回true,不然返回false表示受權失敗。
ModularRealmAuthorizer進行多Realm匹配流程:
一、首先檢查相應的Realm是否實現了實現了Authorizer;
二、若是實現了Authorizer,那麼接着調用其相應的isPermitted*/hasRole*接口進行匹配;
三、若是有一個Realm匹配那麼將返回true,不然返回false。
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //1. 從 PrincipalCollection 中來獲取登陸用戶的信息 Object principal = principals.getPrimaryPrincipal(); //2. 利用登陸的用戶的信息來查詢當前用戶的角色或權限(可能須要查詢數據庫) Set<String> roles = new HashSet<String>(); //3. 建立 SimpleAuthorizationInfo, 並設置其 reles 屬性. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); //4.設置當前用戶的權限 Set<String> permissions = new HashSet<String>(); info.setStringPermissions(permissions); //5.返回 SimpleAuthorizationInfo 對象. return info; }
Shiro 經常使用註解:
@RequiresAuthentication
驗證用戶是否登陸,等同於方法subject.isAuthenticated() 結果爲true時。
@RequiresUser
驗證用戶是否被記憶,user有兩種含義:
一種是成功登陸的(subject.isAuthenticated() 結果爲true);
另一種是被記憶的(subject.isRemembered()結果爲true)。
@RequiresGuest
驗證是不是一個guest的請求,與@RequiresUser徹底相反。
換言之,RequiresUser == !RequiresGuest。
此時subject.getPrincipal() 結果爲null.
@RequiresRoles
例如:@RequiresRoles("aRoleName");
若是subject中有aRoleName角色才能夠訪問被修飾方法。
@RequiresPermissions
例如: @RequiresPermissions({"file:read", "write:aFile.txt"} )
要求subject中必須同時含有file:read和write:aFile.txt的權限才能執行方法
Shiro 經常使用標籤
guest 標籤:用戶沒有身份驗證時顯示相應信息,即遊客訪問信息
user 標籤:用戶已經通過認證/記住我登陸後顯示相應的信息。
authenticated標籤:即Subject.login登陸成功,不是記住我登陸的
notAuthenticated 標籤:沒有調用Subject.login進行登陸,包括記住我自動登陸的
pincipal 標籤:顯示用戶身份信息,默認調用Subject.getPrincipal() 獲取
hasRole 標籤:若是當前 Subject 有角色將顯示 body 體內容:
hasAnyRoles 標籤:若是當前Subject有任意一個角色將顯示body體內容
lacksRole:若是當前 Subject 沒有角色將顯示 body 體內容
hasPermission:若是當前 Subject 有權限將顯示 body 體內容
lacksPermission:若是當前Subject沒有權限將顯示body體內容