今天看了教程的第三章...是關(guān)于授權(quán)的......和以前一樣.....自己也研究了下....我覺得看那篇教程怎么說呢.....總體上是為數(shù)不多的精品教程了吧....但是有些地方確實(shí)是講的太少了....而這些地方又是蠻難的..比如3.5節(jié)Authorizer、PermissionResolver及RolePermissionResolver...可能作者覺得講清楚要花太多的篇幅涉及太多的類吧.....但是我看起來就很不爽0.0....既然提到了就想弄明白.....不然太糾結(jié)了....所以就有了這篇學(xué)習(xí)記錄...記錄我對Shiro授權(quán)的理解
Subject我是從subject.isPermittedAll("system:view")這里開始研究的...
和我的上一篇學(xué)習(xí)記錄寫到的一樣Subject只是一個(gè)接口,它的實(shí)現(xiàn)類是DelegatingSubject類.
1 public boolean isPermittedAll(String... permissions) {2 return hasprincipals() && securityManager.isPermittedAll(getPRincipals(), permissions);3 }
授權(quán)和認(rèn)證太像啦,這里subject其實(shí)也是委托securityManager來具體處理的(第2行).
SecurityManagersecurityManager的實(shí)現(xiàn)類是DefaultSecurityManager...DefaultSecurityManager的N層父類AuthorizingSecurityManager里有isPermitted方法
1 public boolean isPermittedAll(PrincipalCollection principals, String... permissions) {2 return this.authorizer.isPermittedAll(principals, permissions);3 }
從中可以看出....Shiro的認(rèn)證和授權(quán)真的是太像了...這里授權(quán)是委托授權(quán)器authorizer來做具體的授權(quán),就像我學(xué)習(xí)認(rèn)證那篇文章講的,認(rèn)證是委托authenticator來做一樣.
ModularRealmAuthorizer我想再吐槽一下.....ModularRealmAuthorizer和ModularRealmAuthenticator真是親兄弟啊......我差點(diǎn)就看錯名字了....
1 public boolean isPermittedAll(PrincipalCollection principals, String... permissions) { 2 assertRealmsConfigured(); 3 if (permissions != null && permissions.length > 0) { 4 for (String perm : permissions) { 5 if (!isPermitted(principals, perm)) { 6 return false; 7 } 8 } 9 }10 return true;11 }
這里就是把permissions一個(gè)一個(gè)取出來,調(diào)用isPermitted(principals, perm)方法去看看用戶是否有每一個(gè)傳入的權(quán)限perm(第5行)...........
全部包含就是返回true,否則返回false.
那我們就去看看isPermitted方法好了
1 public boolean isPermitted(PrincipalCollection principals, String permission) { 2 assertRealmsConfigured(); 3 for (Realm realm : getRealms()) { 4 if (!(realm instanceof Authorizer)) continue; 5 if (((Authorizer) realm).isPermitted(principals, permission)) { 6 return true; 7 } 8 } 9 return false;10 }
從這里(第5行)可以看出其實(shí)ModularRealmAuthorizer是調(diào)用了Realm的isPermitted的....
AuthorizingRealm1 public boolean isPermitted(PrincipalCollection principals, String permission) { 2 Permission p = getPermissionResolver().resolvePermission(permission); 3 return isPermitted(principals, p); 4 } 5 6 public boolean isPermitted(PrincipalCollection principals, Permission permission) { 7 AuthorizationInfo info = getAuthorizationInfo(principals); 8 return isPermitted(permission, info); 9 }10 11 private boolean isPermitted(Permission permission, AuthorizationInfo info) {12 Collection<Permission> perms = getPermissions(info);13 if (perms != null && !perms.isEmpty()) {14 for (Permission perm : perms) {15 if (perm.implies(permission)) {16 return true;17 }18 }19 }20 return false;21 }
那么Realm又是怎么處理的呢?
在第一個(gè)方法中調(diào)用PermissionResolver去把要驗(yàn)證的字符串的permission轉(zhuǎn)化成了實(shí)實(shí)在在的Permission對象...這是很重要的一步(但是PermissionResolver在第三個(gè)方法中也有用到,所以這里就不提了)....
然后再第二個(gè)方法中根據(jù)傳入的principals調(diào)用Realm的getAuthorizationInfo(principals)得到AuthorizationInfo ....這里大家一定很熟悉...因?yàn)樽约簩慠ealm的話一定會去覆蓋這個(gè)getAuthorizationInfo方法..返回的AuthenorizationInfo里可以得到用戶的權(quán)限...只不過也是字符串形式......
然后就到了第三個(gè)方法....第12行,把info再轉(zhuǎn)化成Permission對象的集合...然后遍歷這個(gè)集合,去和傳入的permission比較,看看用戶的permission是否包含(implies)傳入的permission....
1 private Collection<Permission> getPermissions(AuthorizationInfo info) { 2 Set<Permission> permissions = new HashSet<Permission>(); 3 4 if (info != null) { 5 Collection<Permission> perms = info.getObjectPermissions(); 6 if (!CollectionUtils.isEmpty(perms)) { 7 permissions.addAll(perms); 8 } 9 perms = resolvePermissions(info.getStringPermissions());10 if (!CollectionUtils.isEmpty(perms)) {11 permissions.addAll(perms);12 }13 14 perms = resolveRolePermissions(info.getRoles());15 if (!CollectionUtils.isEmpty(perms)) {16 permissions.addAll(perms);17 }18 }19 20 if (permissions.isEmpty()) {21 return Collections.emptySet();22 } else {23 return Collections.unmodifiableSet(permissions);24 }25 }
AuthorizationInfo對象(包含用戶字符串形式的權(quán)限)是如何提取出Permission對象的呢?
從上面的方法可以看出權(quán)限是來自3個(gè)方法和合并..info.getObjectPermissions().....resolvePermissions(info.getStringPermissions())和resolveRolePermissions(info.getRoles())....
第一個(gè)方法info.getObjectPermissions()我也不知道它是做啥的....看了源文檔的注釋還是不懂(%>_<%)......我Shiro也僅僅只寫過一個(gè)demo.......實(shí)在是不了解......
第二個(gè)方法resolvePermissions(info.getStringPermissions())很重要..要提到WildcardPermissionResolver和WildcardPermission....等會再說....
第三個(gè)方法resolveRolePermissions(info.getRoles())我覺得是個(gè)預(yù)留的方法....要用到RolePermissionResolver接口的子類來做具體的處理....但是Shiro沒有提供任何實(shí)現(xiàn)...大家可以去看看教程...開濤哥的教程里有他的實(shí)現(xiàn).....我看了下源文檔的注釋....以我英語6級500都不到的渣渣功力來解讀的話(=.=)大致意思就是說有些時(shí)候只能獲取到subject的role,但是沒有permission,這個(gè)時(shí)候用RolePermissionResolver可以根據(jù)role解讀出permission...嗯....大致就是這么個(gè)意思...錯了別打我....Σ( ° △ °|||)︴
然后重點(diǎn)自然就是第二個(gè)方法resolvePermissions(info.getStringPermissions())啦....
1 private Collection<Permission> resolvePermissions(Collection<String> stringPerms) { 2 Collection<Permission> perms = Collections.emptySet(); 3 PermissionResolver resolver = getPermissionResolver(); 4 if (resolver != null && !CollectionUtils.isEmpty(stringPerms)) { 5 perms = new LinkedHashSet<Permission>(stringPerms.size()); 6 for (String strPermission : stringPerms) { 7 Permission permission = getPermissionResolver().resolvePermission(strPermission); 8 perms.add(permission); 9 }10 }11 return perms;12 }
那么Shiro是如何把String的permission轉(zhuǎn)化成實(shí)實(shí)在在的Permission對象的呢?
那就要用到PermissionResolver 的resolvePermission方法了(從上面第7行可以看出)...
WildcardPermissionResolverWildcardPermissionResolver在Shiro里是PermissionResolver 的默認(rèn)實(shí)現(xiàn)....
看看這個(gè)類名真是高大上...但是其實(shí)代碼超級短....
1 public class WildcardPermissionResolver implements PermissionResolver { 2 3 /** 4 * Returns a new {@link WildcardPermission WildcardPermission} instance constructed based on the specified 5 * <tt>permissionString</tt>. 6 * 7 * @param permissionString the permission string to convert to a {@link Permission Permission} instance. 8 * @return a new {@link WildcardPermission WildcardPermission} instance constructed based on the specified 9 * <tt>permissionString</tt>10 */11 public Permission resolvePermission(String permissionString) {12 return new WildcardPermission(permissionString);13 }14 }
就是返回new了一個(gè)WildcardPermission僅此而已....
WildcardPermission1 public WildcardPermission(String wildcardString) {2 this(wildcardString, DEFAULT_CASE_SENSITIVE);3 }4 5 public WildcardPermission(String wildcardString, boolean caseSensitive) {6 setParts(wildcardString, caseSensitive);7 }
構(gòu)造方法主要就是調(diào)用了setParts方法....另外稍微提一下....DEFAULT_CASE_SENSITIVE默認(rèn)是false....那就是不區(qū)分字符串權(quán)限大小寫咯~(system:view和System:vIew是一會事情)
1 protected void setParts(String wildcardString, boolean caseSensitive) { 2 if (wildcardString == null || wildcardString.trim().length() == 0) { 3 throw new IllegalArgumentException("Wildcard string cannot be null or empty. Make sure permission strings are properly formatted."); 4 } 5 6 wildcardString = wildcardString.trim(); 7 8 List<String> parts = CollectionUtils.asList(wildcardString.split(PART_DIVIDER_TOKEN)); 9 10 this.parts = new ArrayList<Set<String>>();11 for (String part : parts) {12 Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));13 if (!caseSensitive) {14 subparts = lowercase(subparts);15 }16 if (subparts.isEmpty()) {17 throw new IllegalArgumentException("Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted.");18 }19 this.parts.add(subparts);20 }21 22 if (this.parts.isEmpty()) {23 throw new IllegalArgumentException("Wildcard string cannot contain only dividers. Make sure permission strings are properly formatted.");24 }25 }
那么setParts方法做了些什么呢?
這個(gè)方法主要就是為WildcardPermission的成員域List<Set<String>> parts去賦值....我們來看看分隔符....
protected static final String WILDCARD_TOKEN = "*"; protected static final String PART_DIVIDER_TOKEN = ":"; protected static final String SUBPART_DIVIDER_TOKEN = ",";
然后大家再看代碼應(yīng)該就沒什么問題了...這個(gè)代碼我覺得沒啥好說的...我就舉幾個(gè)例子吧...可能更直觀點(diǎn)....
如果用戶的字符串權(quán)限是"system:user:create,view"
那么parts這個(gè)成員域List的size是3.第一個(gè)set包含system...第二個(gè)set包含user....第三個(gè)set的size是2,包含create和view
構(gòu)造方法和setParts就做了這些事情! 其實(shí)一點(diǎn)也不多....
好了...現(xiàn)在要檢測的傳入的字符串權(quán)限被轉(zhuǎn)化成了Permission對象....用戶擁有的字符串權(quán)限也轉(zhuǎn)化成了Permission對象....那么如何判斷用戶是否有傳入的權(quán)限呢?
在前面AuthorizingRealm那小節(jié)里我看看到了是調(diào)用perm.implies(permission)來判斷是否包含的....
請注意..perm是用戶的權(quán)限...permission是傳入要檢測的權(quán)限...
我們來看看implies方法吧....
1 public boolean implies(Permission p) { 2 // By default only supports comparisons with other WildcardPermissions 3 if (!(p instanceof WildcardPermission)) { 4 return false; 5 } 6 7 WildcardPermission wp = (WildcardPermission) p; 8 9 List<Set<String>> otherParts = wp.getParts();10 11 int i = 0;12 for (Set<String> otherPart : otherParts) {13 // If this permission has less parts than the other permission, everything after the number of parts contained14 // in this permission is automatically implied, so return true15 if (getParts().size() - 1 < i) {16 return true;17 } else {18 Set<String> part = getParts().get(i);19 if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {20 return false;21 }22 i++;23 }24 }25 26 // If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards27 for (; i < getParts().size(); i++) {28 Set<String> part = getParts().get(i);29 if (!part.contains(WILDCARD_TOKEN)) {30 return false;31 }32 }33 34 return true;35 }
代碼就這么點(diǎn)...但是解釋起來可能要很多篇幅....所以我還是覺得舉例子比較快......其實(shí)這個(gè)方法就是比較2個(gè)Permission的parts部分....
比較是一部分一部分進(jìn)行的...先List[0]和List[0]比較(第一個(gè)for的第一次循環(huán))...再List[1]和List[1]比較(第一個(gè)for的第二次循環(huán))....
比如用戶權(quán)限是"*:user,admin:view" 傳入要檢測的權(quán)限是"system:guest:view"
第一個(gè)for的第一輪比較...看代碼第19行....因?yàn)橛脩魀arts的[0]是* !part.contains(WILDCARD_TOKEN)是false..所以繼續(xù)下一輪for循環(huán)...意思就是*肯定包含system
然后進(jìn)入for的第二輪!part.contains(WILDCARD_TOKEN)是true..所以要看!part.containsAll(otherPart)...
變量part是字符串user和admin的集合...otherPart是字符串guest的集合..!part.containsAll(otherPart)返回是true..所以if成立...所以return false..即用戶是不包含傳入檢測的權(quán)限的(當(dāng)然這里其實(shí)沒有這么快就能得出這個(gè)結(jié)論..ModularRealmAuthorizer中如果有多個(gè)realm,要所有realm都返回false才能判斷用戶沒有這個(gè)權(quán)限..因?yàn)槌绦蚩赡軙渲枚鄠€(gè)Realm...多個(gè)Realm可能會給一個(gè)用戶取到不同的權(quán)限...所以每個(gè)Realm都要檢測過來才知道用戶到底包不包含傳入要檢測的權(quán)限...這個(gè)道理蠻簡單的..大家都懂的..我就是稍微提醒一下..我只是假設(shè)我這里就配置了一個(gè)realm.....哈哈)...
看了這個(gè)implies方法的流程...我想大家也能明白為什么user:*是能匹配user:view:123而*:view不能匹配system:user:view的道理吧....
因?yàn)樵谟脩魴?quán)限長度m小于傳入權(quán)限長度n的時(shí)候,implies方法的第一個(gè)for最多做m次就能知道返回是false還是true了..后面的n-m次比較是不會做的...
而用戶權(quán)限長度m大于傳入權(quán)限長度n的時(shí)候,先做implies方法的第一個(gè)for的n次比較,如果還是沒結(jié)果...再做第2個(gè)for..檢測用戶權(quán)限patrs[n-m]到parts[m-1]的部分..這些部分只要有任意一個(gè)不是*那就說明用戶不具有傳入待檢測的權(quán)限...
以上就是我今天對Shiro授權(quán)的理解了吧....
新聞熱點(diǎn)
疑難解答
圖片精選