1.授權,也叫訪問控制,即在應用中控制誰訪問那些資源(如訪問頁面/編輯數據/頁面操作等)。在授權中需要了解的幾個關鍵對象:主體(Subject),資源(Resource),權限(Permission),角色(Role)。
2.主體(Subject):訪問應用的用戶,在Shiro中使用Subject代表該用戶。用戶只有授權後才允許訪問相應的資源。
3.資源(Resource):在應用中用戶可以訪問的url,比如訪問jsp頁面,查看/編輯某些數據,訪問某個業務方法,打印文本等等都是資源。用戶只有授權後才能訪問。
4.權限(Permission):安全策略中的原子授權單位,通過權限我們可以表示用戶在應用中用戶有沒有操作這個資源的權利。即權限表示在應用中能不能訪問某個資源,如:訪問用戶列表頁面查看/新增/修改/刪除用戶數據(很多時候都是CRUD式權限控制等)。權限代表了用戶有沒有操作某個資源的權利,即反映在某個資源上的操作允不允許。
5.shiro支持粗粒度權限(如用戶模塊的所有權限)和細粒度權限(操作某個用戶的權限,即實例級別的)
6.角色(Role):權限的集合,一般情況下會賦予用戶角色而不是權限,即這樣的用戶可以擁有一組權限,賦予權限時比較方便。典型的如:項目經理,技術總監,CTO,開發工程師等都是角色,不同的角色擁有一組不同的權限。
二、Shiro支持三種授權方式
- 編程式:通過寫if/else授權代碼塊完成
- 註解式:通過在執行的java方法上放置相應的註解完成,沒有權限將拋出相應的異常
- jsp/GSP標籤:在JSP/GSP頁面通過相應的標籤完成
三、Shiro授權的Realm編寫
1).需要繼承自AuthorizingRealm類,並實現doGetAuthorizationInfo方法
2).AuthorizingRealm 類繼承自 AuthenticatingRealm ,但沒有實現 AuthenticatingRealm 的 doGetAuthenticationInfo 方法。所以認證和授權只需要繼承 AuthorizingRealm 就可以了,同時實現他的兩個抽象方法。
實例代碼:
package cn.com.shiro.realm;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class ShiroRealm extends AuthorizingRealm {
//用於認證的方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("info--> : FirstRealm is lodding");
//1.把 token 轉化爲 UsernamePasswordToken 對象
UsernamePasswordToken upToken = (UsernamePasswordToken)token;
//2.從 UsernamePasswordToken 中獲取用戶名 username
String username = upToken.getUsername();
//3.從數據庫中查詢 username 對應的記錄
System.out.println("從數據庫中獲得username: " + username + " 所對應的用戶信息.");
//4.若用戶不存在,則可以拋出 UnknownAccountException 異常
if("unknown".equals(username)){
throw new UnknownAccountException("用戶不存在!");
}
//5.根據用戶情況 ,決定是否拋出其他 AuthenticationException 異常
if("monster".equals(username)){
throw new LockedAccountException("用戶被鎖定!");
}
//6.根據用戶的情況,構建 AuthenticationInfo 對象並返回,通常使用的實現類是 SimpleAuthenticationInfo
//一下信息是從數據庫中獲取的
//1).principal:認證的實體信息,可以是username,也可以是實體類的對象
Object principal = username;
//2).credentials:數據庫中獲取的密碼
Object credentials = null;//"fc1709d0a95a6be30bc5926fdb7f22f4";
if(username.equals("admin")){
credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
}else if(username.equals("user")){
credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
}
//3).realmName:當前realm對象的name, 調用父類對象的 getName()方法即可
String realmName = getName();
//4).鹽值
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
SimpleAuthenticationInfo info = null;//new SimpleAuthenticationInfo(principal, credentials, realmName);
info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
return info;
}
public static void main(String[] args) {
String algorithmName = "MD5";
Object credentials = "123456";
Object salt = ByteSource.Util.bytes("user");
int hashIterations = 1024;
Object result = new SimpleHash(algorithmName, credentials, salt, hashIterations);
System.out.println(result);
}
//用於授權的方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("授權 --> doGetAuthorizationInfo...");
//1.從PrincipalCollection中來獲取登錄用戶的信息
Object principal = principals.getPrimaryPrincipal();
//2.利用登錄的用戶信息來獲取當前用戶的角色或權限(可能需要查詢數據庫)
Set<String> roles = new HashSet<>();
roles.add("user");
if(principal.equals("admin")){
roles.add("admin");
}
//3.創建SimpleAuthorizationInfo , 並設置其roles屬性
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//4.返回 SimpleAuthorizationInfo 對象
return info;
}
}