目錄
1. 授權入口
subject.isPermitted(url); 這就是入口,如果用戶有這個url權限,那麼就返回true,如果沒有,則返回false。
2. 源碼追蹤
由於我們的Subject默認是由DelegatingSubject 類實現的,所以調用的是DelegatingSubject類的isPermitted(String url) 方法。
public boolean isPermitted(String permission) {
return this.hasPrincipals() && this.securityManager.isPermitted(this.getPrincipals(), permission);
}
判斷該用戶是否有身份標識信息(多半是用戶名、電話等等,能夠唯一標識用戶),如果沒有,那肯定授權失敗,用戶無法訪問url。如果有,就繼續判斷,this.securityManager.isPermitted(this.getPrincipals(), permission);
這裏的securityManager 是我們的shiro配置文件中配置了的,本文是配置的DefaultSecurityManager類來實現的, 查看了DefaultSecurityManager類裏面,並沒有isPermitted方法,原來DefaultSecurityManager類繼承了AuthorizingSecurityManager類,而isPermitted方法是在AuthorizingSecurityManager類中,AuthorizingSecurityManager中的部分源碼:
private Authorizer authorizer = new ModularRealmAuthorizer();
public boolean isPermitted(PrincipalCollection principals, String permissionString) {
return this.authorizer.isPermitted(principals, permissionString);
}
可以看到調用的是ModularRealmAuthorizer 類中的isPermitted方法,說明Authorizer 接口默認是由ModularRealmAuthorizer類實現的。
再看ModularRealmAuthorizer類中的isPermitted方法:
public boolean isPermitted(PrincipalCollection principals, String permission) {
this.assertRealmsConfigured();
Iterator var3 = this.getRealms().iterator();
Realm realm;
do {
if (!var3.hasNext()) {
return false;
}
realm = (Realm)var3.next();
} while(!(realm instanceof Authorizer) || !((Authorizer)realm).isPermitted(principals, permission));
return true;
}
可以看到,是調用的Realm中的isPermitted方法去授權驗證,那麼此時就要看我們自定義的Realm了。一般自定義的Realm都是繼承AuthorizingRealm類,而自定義Realm中就重寫了兩個方法:doGetAuthorizationInfo( ) 和 doGetAuthenticationInfo ( ) 方法。至於isPermitted方法,還是在AuthorizingRealm 類中,查看一下:
public boolean isPermitted(PrincipalCollection principals, String permission) {
Permission p = this.getPermissionResolver().resolvePermission(permission);
return this.isPermitted(principals, p);
}
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
AuthorizationInfo info = this.getAuthorizationInfo(principals);
return this.isPermitted(permission, info);
}
protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
Collection<Permission> perms = this.getPermissions(info);
if (perms != null && !perms.isEmpty()) {
Iterator var4 = perms.iterator();
while(var4.hasNext()) {
Permission perm = (Permission)var4.next();
if (perm.implies(permission)) {
return true;
}
}
}
return false;
}
protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
return null;
} else {
AuthorizationInfo info = null;
if (log.isTraceEnabled()) {
log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
}
Cache<Object, AuthorizationInfo> cache = this.getAvailableAuthorizationCache();
Object key;
if (cache != null) {
if (log.isTraceEnabled()) {
log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
}
key = this.getAuthorizationCacheKey(principals);
info = (AuthorizationInfo)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) {
info = this.doGetAuthorizationInfo(principals);
if (info != null && cache != null) {
if (log.isTraceEnabled()) {
log.trace("Caching authorization info for principals: [" + principals + "].");
}
key = this.getAuthorizationCacheKey(principals);
cache.put(key, info);
}
}
return info;
}
}
它是將String類型的url 封裝成了 Permission對象型的 url,然後調用過程,請看上面的代碼調用,大概說一下流程:先去看是否有緩存機制,如果有,則去緩存裏找一找AuthorizationInfo(注意,這個info裏面是包含了用戶所擁有的全部權限url ,是個集合),如果找到了,那就直接用這個info,將我們要授權驗證的那個url去這個info中匹配,看是否info中包含了這個url,如果包含了,則返回true,授權驗證通過,如果沒有包含,則返回false,授權驗證失敗。 那如果沒有緩存機制的話,就去調用我們自定義Realm中的doGetAuthorizationInfo( )方法,所以重點來了(我們在自定義Realm的doGetAuthorizationInfo( ) 方法中,就應該將用戶的全部權限查出來——從數據庫查,封裝在info中,再返回)。