上一篇博客,寫了 shiro認證流程分析 ,shiro主要功能是認證和授權,接下來,看看授權是如何進行的,既然要授權,當然會執行我們自定義的授權方法,所以在授權方法打個斷點看看情況,當訪問需要角色或者權限的接口時,就會來到授權方法(這裏暫時不考慮緩存的因素,可以看做是第一次請求):
我們看一下方法調用棧:
可以看到控制器AdminController是Cglib的一個代理對象,這裏會涉及到aop東西,我會適當的跳過,不影響理解shiro的授權流程。我們直接跳到方法調用棧中DelegatingSubject的checkRole()方法,從這裏開始看:
public void checkRole(String role) throws AuthorizationException {
assertAuthzCheckPossible();
//檢查主體是否擁有role這個角色,關注這個
securityManager.checkRole(getPrincipals(), role);
}
來到AuthorizingSecurityManager的checkRole()方法:
public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
this.authorizer.checkRole(principals, role);
}
這裏的授權器authorizer是ModularRealmAuthorizer類型的,從構造函數可以知道:
所以接下來調用ModularRealmAuthorizer的checkRole()方法:
public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
assertRealmsConfigured();
if (!hasRole(principals, role)) {
throw new UnauthorizedException("Subject does not have role [" + role + "]");
}
}
而checkRole又通過hasRole來判斷:
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {
return true;
}
}
return false;
}
可以發現,hasRole方法中,會先獲取到所有的realm,判斷是否屬於Authorizer類型的,如果是,則強轉爲Authorizer類型,並調用realm的hasRole方法,我們自定義的realm就是Authorizer類型的,所以會被調用
來到AuthorizingRealm的hasRole方法:
public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
//獲取授權信息
AuthorizationInfo info = getAuthorizationInfo(principal);
//判斷info中是否有roleIdentifier這個角色
return hasRole(roleIdentifier, info);
}
它會分兩步進行:
(1)獲取授權信息
(2)判斷授權信息中,是否包含所需要的角色(因爲這裏是以角色 爲例,如果是權限的話,就會判斷是否有權限)
一個個看:
(1)獲取授權信息:
//獲取授權信息
protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
return null;
}
AuthorizationInfo info = null;
if (log.isTraceEnabled()) {
log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
}
Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
if (cache != null) {
if (log.isTraceEnabled()) {
log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
}
Object key = getAuthorizationCacheKey(principals);
info = cache.get(key);
if (log.isTraceEnabled()) {
if (info == null) {
log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
} else {
log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
}
}
}
if (info == null) {
// Call template method if the info was not found in a cache
info = doGetAuthorizationInfo(principals);
// If the info is not null and the cache has been created, then cache the authorization info.
if (info != null && cache != null) {
if (log.isTraceEnabled()) {
log.trace("Caching authorization info for principals: [" + principals + "].");
}
Object key = getAuthorizationCacheKey(principals);
cache.put(key, info);
}
}
return info;
}
這個思路和獲取認證信息是一樣的:
a) 先從緩存找,如果有,則返回
b)緩存沒有,則執行 info = doGetAuthorizationInfo(principals);獲取
第一次獲取緩存,肯定是沒有的,所以會走 info = doGetAuthorizationInfo(principals);這個方法,這樣就會調用我們自定義的授權方法了:
//授權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User)SecurityUtils.getSubject().getPrincipal(); //獲取當前登錄的用戶信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//這裏模擬數據庫,進行角色和權限的處理
//這裏只是簡單模擬,角色使用是用戶名,權限是包含用戶名的路徑
simpleAuthorizationInfo.addRole(user.getUsername());
simpleAuthorizationInfo.addStringPermission("/"+user.getUsername());
return simpleAuthorizationInfo;
}
(2)判斷是否擁有某個角色
這個很簡單,就是獲取到所有的角色集合,看看是否包含要的角色:
protected boolean hasRole(String roleIdentifier, AuthorizationInfo info) {
//info.getRoles()返回的是一個集合
return info != null && info.getRoles() != null && info.getRoles().contains(roleIdentifier);
}
這樣子,也就知道了當前主體是否包含某個角色信息,有的話,就有權訪問,否則就是沒有權限訪問。