shiro的授权流程,源码分析
一切的源头都是从subject开始,所以,打开看看
首先有两个概念需要搞清楚
隐式角色:直接通过身份验证是否有权限
显示角色:在程序中通过权限控制谁能访问某个资源
shiro的权限设计有很多种
RBAC所代表的是对角色进行控制,即只控制资源与角色之间的关系,并且一般来说这里的资源粒度只是细化到页面,但是对于一些要求更加细致的权限控制(控制到按钮)单纯的架构层面就无法满足,此时就需要程序员对页面进行处理,架构层面的需要后期处理就说明设计模式有所欠缺。
RBAC新解所代表的是粒度比RBAC对资源划分更加细化,它直接对访问资源的任何操作做权限控制
参看博文:https://blog.csdn.net/xk147258/article/details/100701256
这里我们只看在源码中,shiro的授权流程是怎样的,下面是subject中所有关于权限验证的方法
//判断是否拥有权限 boolean isPermitted(String permission); //传入权限通配符,判断对某个资源可以进行某些操作 boolean isPermitted(Permission permission); //这里可以一次性传入权限组来验证是否拥有权限 boolean[] isPermitted(String... permissions); boolean[] isPermitted(List<Permission> permissions); //传入权限组判断是否拥有所有权限 boolean isPermittedAll(String... permissions); boolean isPermittedAll(Collection<Permission> permissions); void checkPermission(String permission) throws AuthorizationException; void checkPermission(Permission permission) throws AuthorizationException; void checkPermissions(String... permissions) throws AuthorizationException; void checkPermissions(Collection<Permission> permissions) throws AuthorizationException; boolean hasRole(String roleIdentifier); boolean[] hasRoles(List<String> roleIdentifiers); boolean hasAllRoles(Collection<String> roleIdentifiers); void checkRole(String roleIdentifier) throws AuthorizationException; void checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException; void checkRoles(String... roleIdentifiers) throws AuthorizationException;
现在使用的比较多的是基于资源的访问控制即显示角色,这种策略的意思是譬如某个资源为user,这个user的所有find,update,insert,delete操作都归由shiro管理,至于隐式角色大家可以参考quick4j的开源项目
所以我们这里只看和Permission有关的方法
sunbject依然是交给securitymanager进行授权
然后securitymanager委托给Authorizer
找到实现方法在AuthorizingRealm中
public boolean isPermitted(PrincipalCollection principals, String permission) { Permission p = getPermissionResolver().resolvePermission(permission); return isPermitted(principals, p); } public boolean isPermitted(PrincipalCollection principals, Permission permission) { AuthorizationInfo info = getAuthorizationInfo(principals); return isPermitted(permission, info); } //changed visibility from private to protected for SHIRO-332 protected boolean isPermitted(Permission permission, AuthorizationInfo info) { Collection<Permission> perms = getPermissions(info); if (perms != null && !perms.isEmpty()) { for (Permission perm : perms) { if (perm.implies(permission)) { return true; } } } return false; }
这段代码的意思我尽量写详细点
PrincipalCollection:subject里的主题集合,
permission:传入的权限字符串,或permission实例
如果传入的是字符串,会先通过PermissionResolver将字符串转换为permission实例
看看实现过程
起作用的是WildcardPermission中的setparts
protected void setParts(String wildcardString, boolean caseSensitive) { wildcardString = StringUtils.clean(wildcardString); if (wildcardString == null || wildcardString.isEmpty()) { throw new IllegalArgumentException("Wildcard string cannot be null or empty. Make sure permission strings are properly formatted."); } //判断传入的是否是字符串,是则转换为小写(总感觉老外的思维比较奇特。这个caseSensitive默认值是true这里还做个判断,一开始就对参数个数做了限制,还要做个反向判断) if (!caseSensitive) { wildcardString = wildcardString.toLowerCase(); } //根据,分割字符串 List<String> parts = CollectionUtils.asList(wildcardString.split(PART_DIVIDER_TOKEN)); //封装为set集合 this.parts = new ArrayList<Set<String>>(); for (String part : parts) { Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN)); if (subparts.isEmpty()) { throw new IllegalArgumentException("Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted."); } this.parts.add(subparts); } if (this.parts.isEmpty()) { throw new IllegalArgumentException("Wildcard string cannot contain only dividers. Make sure permission strings are properly formatted."); } }
转换为permission实例后,需要对应的realm根据subject即principals获取AuthorizationInfo,看看获取过程
shiro默认都是从缓存中获取信息,所以我们的权限系统都需要自定义这些东西
最后将permission实例和AuthorizationInfo传入permiss中的implies方法逐个对比,这个方法有点不容易看懂
public boolean implies(Permission p) { // By default only supports comparisons with other WildcardPermissions if (!(p instanceof WildcardPermission)) { return false; } WildcardPermission wp = (WildcardPermission) p; List<Set<String>> otherParts = wp.getParts(); int i = 0; for (Set<String> otherPart : otherParts) { //如果传入的permission实例长度小于info的长度,通过 if (getParts().size() - 1 < i) { return true; } else { //如果传入的permision后缀不是*,且info不是传入permission的子集,就是内容不一致返回false Set<String> part = getParts().get(i); if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) { return false; } i++; } } // 如果多出来的part不是*不通过 for (; i < getParts().size(); i++) { Set<String> part = getParts().get(i); if (!part.contains(WILDCARD_TOKEN)) { return false; } } return true; }
参考博文https://blog.csdn.net/sharetop/article/details/50250651
匹配完成返回true,false为失败