安全认证框架Shiro(三)- 源码角度解析shiro的权限验证

来源:互联网 发布:大数据的4v特性是指 编辑:程序博客网 时间:2024/05/22 04:11

安全认证框架Shiro(三)- 源码角度解析shiro的权限验证

    • 安全认证框架Shiro三- 源码角度解析shiro的权限验证
  • 第一前言
  • 第二走下去
  • shiro没配置权限验证的Url怎么处理
  • 结论

第一:前言

承接上篇文章安全认证框架Shiro (二)- shiro过滤器工作原理
权限验证,一直是个难点,看起来是个很高大尚的概念,其实我理解的权限其实就是字符串,把它当成字符串,只要和用户对应的字符串匹配成功,则验证通过。验证权限可以理解为字符串匹配即可通过授权,url对应的权限是个字符串数组集合,所以同一个请求可能配置成多个权限,比如老板和总经理都可以查询员工的信息。访问该url需要一组权限集,只要能从realm里取到你拥有的权限,只要某个匹配即可授权通过。

第二:走下去

权限验证和身份验证前半部分流程一样直到走到PathMatchingFilter.onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)这个方法都是一样的。
onPreHandler是处理权限验证的部分,不同的权限验证有一同的实现方式。
权限验证器包括如下:
这里写图片描述
其中较常用的是PermissionsAuthorizationFilter权限过滤器,如果自定义权限验证,一般继承自AuthorizationFilter。

前面流程这里不做介绍,不懂的可看安全认证框架Shiro (二)- shiro过滤器工作原理,
从AccessControlFilter.onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)开始,不同类型过滤器不同逻辑。
参数mappedValue是对应的权限字符串。
如下图的“user:admin”:
这里写图片描述

我们先看看PermissionsAuthorizationFilter和FormAuthenticationFilter的appliedPaths数据有什么不同?如下

FormAuthenticationFilter.appliedPaths:

{    /login=null,    /authenticated=null}

PermissionsAuthorizationFilter.appliedPaths:

{    /index.jsp=[Ljava.lang.String;@5a24afbf,    /admin.jsp=[Ljava.lang.String;@54e35a8b}

其中权限里key对应的value值可参考如下:
这里写图片描述

解释:
key即是对应的需要处理的url,重点是value值,权限验证的value是有值的,非null,而value值即是权限字符串。
收到一个请求,先找到url对应的过滤器链,然后按序执行过滤器,同时mappedValue值也会一同传下去,通过realm查找用户对应的权限与mappedValue值比对,比对成功则继承下个过滤器验证。

讲那么多废话,下面上代码:
PermissionsAuthorizationFilter.isAccessAllowed():

 public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {        //取得subject        Subject subject = getSubject(request, response);        //取的访问该url需要的权限,比如:[user:admin],注意是字符串数组        String[] perms = (String[]) mappedValue;        boolean isPermitted = true;        if (perms != null && perms.length > 0) {            if (perms.length == 1) {                //单个权限验证,                if (!subject.isPermitted(perms[0])) {                    isPermitted = false;                }            } else {                //多个权限验证                if (!subject.isPermittedAll(perms)) {                    isPermitted = false;                }            }        }        return isPermitted;    }

subject.isPermitted()方法先验证是否登陆过,没登陆过的用户根本没必要权限验证,直接失败返回false。若登陆过,通过securityManager.isPermitted(getPrincipals(), permission);执行从数据库或缓存查询用户权限操作,和permission比对(注意这时候permission还是类似“user:admin”的字符串),继续往下走直到AuthorizingRealm.isPermitted()方法:

 public boolean isPermitted(PrincipalCollection principals, Permission permission) {         //获得验证,角色等操作,即数据提供源        AuthorizationInfo info = getAuthorizationInfo(principals);        return isPermitted(permission, info);    }

调用getAuthorizationInfo()拿到AuthorizationInfo对象:

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;    }

getAuthorizationInfo先从缓存管理器里拿权限组,存在直接返回,如果没有则调用Realm实现子类的doGetAuthorizationInfo()方法,一般自定义的Reaml基本上都是继承AuthorizingRealm类,而且需要实现doGetAuthorizationInfo()方法,该方法从数据库查询数据组装到AuthorizationInfo子类对象里返回,且会保存到缓存里供下次调用。
查询出来的权限字符会和url需要的权限字符串比较,比较方法如下isPermitted():

 private 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;    }

如果匹配成功则有权限访问该url,否则无权限。

shiro没配置权限验证的Url怎么处理

在shiro里,如果没配置url过滤器,默认url在shiro里不会做任何验证的,会走spring过虑器。
看看AbstractShiroFilter.getExecutionChain()查询Url对应的过滤器链方法。默认是origChain,如果在过滤器管理器里找到再重新赋值,否则是默认值

 protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {         //默认过滤器,即ApplicationFilterChain        FilterChain chain = origChain;        FilterChainResolver resolver = getFilterChainResolver();        if (resolver == null) {            log.debug("No FilterChainResolver configured.  Returning original FilterChain.");            return origChain;        }        FilterChain resolved = resolver.getChain(request, response, origChain);        if (resolved != null) {            log.trace("Resolved a configured FilterChain for the current request.");            //不为null才赋值,否则取默认值            chain = resolved;        } else {            log.trace("No FilterChain configured for the current request.  Using the default.");        }        return chain;    }

结论

shiro在启动后,权限过滤器会维护一个类似

{    /index.jsp=[Ljava.lang.String;@5a24afbf,    /admin.jsp=[Ljava.lang.String;@54e35a8b}

的集合,当接收到请求后,拿key对应的value值去与缓存或数据库里用户的权限对比。我们注意到,事先权限过滤器已经知道访问某个url对应哪个权限了,这是重点,慢慢品位。

未完待续….

原创粉丝点击