Shiro源码研究之Filter

Filter作为Shiro无可争议的特色之一,完全值得上写一篇博客。

1. 概述

书接前一篇Shiro源码研究之处理一次完整的请求,在前一篇博客中我们了解到:Shiro 对Servlet 容器的FilterChain 进行了代理,即ShiroFilter 在继续Servlet 容器的Filter链的执行之,通过ProxiedFilterChain对Servlet 容器的FilterChain 进行了代理;即走Shiro 自己的Filter 体系,然后才会委托给Servlet 容器的FilterChain 进行Servlet 容器级别的Filter链执行;

2. 体系结构

让我们先看看Shiro中的Filter的继承体系。继承链如图
Shiro中的Filter继承链

让我们沿着继承链进行讨论。

2.1 OncePerRequestFilter

这个类和Spring实现的那个同名类;功能上一模一样。都是保证此filter只调用一次。不必多说。

2.2 AdviceFilter

作为相当底层的Filter基类,执行的总体流程就是在AdviceFilter中被规定好了的AdviceFilter.doFilterInternal

  1. preHandle 返回true继续之后的Filter流程。
  2. executeChain(request, response, chain);继续之后的Filter。
  3. postHandle(request, response);本Filter之后的全部Filter都执行完毕之后, 没有发生异常时才会执行。
  4. cleanup(request, response, exception); finally执行的,如果exception不为null 表明执行过程中发生了异常。
    1. cleanup中又预留出了一个afterCompletion 的空函数,供子类覆写。
  5. 其实本类连doFilterInternal都没有final化。可以说留出了最大的扩展空间。

总结
1. Shiro在这里使用了继承方式来实现了AOP效果,子类通过覆写自身感兴趣的方法来参与到Filter的执行逻辑中,其他通用的逻辑扭转和重复性代码被放在基类中避免冗余。
2. 通观核心的AdviceFilter.doFilterInternal方法,AdviceFilter只是在其中规定了每个Filter执行时的逻辑扭转骨架,而将具体的实现完全交给子类,自身不作任何假设,这样就保证了框架最大的灵活性。

2.3 PathMatchingFilter

  1. 覆写了直接基类AdviceFilterpreHandle 方法; 在其中暴露了isEnabledonPreHandle 供子类覆写。
  2. 其子类主要覆写的是onPreHandle ; 返回true继续之后的Filter流程。
  3. 提供了简化方法 getPathWithinApplication(获取当前请求的路径地址)。

总结
1. 此类是所有默认Filter的基类。
2. url的路径权限的关系(即在ShiroFilterFactoryBean中配置的filterChainDefinitions属性值)就是在此类的appliedPaths字段中存储着。
3. 内部默认使用 Ant 语法(AntPathMatcher)来进行路径匹配工作。

2.4 AccessControlFilter

  1. 覆写了直接基类PathMatchingFilteronPreHandle方法 ,并在其中暴露了isAccessAllowedonAccessDenied 供子类覆写。( 注意这两个方法之间是 的关系,即有一个返回true即可;而且前者返回true, 后者就不会执行了 )。
    1. isAccessAllowed:即是否允许访问,返回true 表示允许;
    2. onAccessDenied:表示访问拒绝时是否自己处理,如果返回true 表示自己不处理且继续拦截器链执行,返回false表示自己已经处理了(比如重定向到另一个页面)。
  2. 提供了简化方法 getSubject()getLoginUrl()isLoginRequest()redirectToLogin(注意Shiro提供的默认只能处理页面请求,对于Ajax请求无法跳转), saveRequestsaveRequestAndRedirectToLogin

总结
1. 本类主要的职责是进行读取资源时的权限控制,以及非授权用户的退出到登录地址的操作。

2.5 AuthenticationFilter(鉴权)

  1. 覆写了直接基类AccessControlFilterisAccessAllowed方法。 默认实现逻辑就是验证当前subject是否isAuthenticated。 所以子类还有机会去实现onAccessDenied 来在前者返回false时扭转整个逻辑,当然你直接去覆写isAccessAllowed也是允许的(逻辑优先)。
  2. 提供了简化方法 getSuccessUrl()issueSuccessRedirect(Redirects to user to the previously attempted URL after a successful login. 重定向到用户上一次的访问的页面,前提是要成功登录)。

2.6 AuthenticatingFilter

  1. 直接继承自AuthenticationFilter
  2. 覆写了AuthenticationFilterisAccessAllowed(Permissive就是在这里被校验的),以及AdviceFiltercleanup
  3. 提供了简化方法 getHostexecuteLogin(其中回调自身定义的createToken方法,所以子类主要是复写createToken),isPermissiveisRememberMe(直接返回false)。
  4. 暴露给子类 onLoginFailure(默认返回false),onLoginSuccess(默认返回true) — 这两个方法在本类中的executeLogin中被调用。
  5. 官方注释 : An AuthenticationFilter that is capable of automatically performing an authentication attempt based on the incoming request. 对过来的请求自动作一次鉴权操作。

2.7 FormAuthenticationFilter

  1. 直接继承自AuthenticatingFilter
  2. 覆写了AuthenticatingFiltercreateTokenonAccessDeniedonLoginFailureonLoginSuccess
  3. 提供了简化方法 getPasswordgetUsernameisRememberMeisLoginSubmissionsetLoginUrl

2.8 AuthorizationFilter(授权)

  1. 覆写了AccessControlFilteronAccessDenied
  2. 提供了简化方法 getUnauthorizedUrl()
  3. 继承自该类,必须覆写其从AccessControlFilter继承来的抽象方法isAccessAllowed

更具体的Filter就不再这里进行详细描述了,感兴趣的读者可以找个Eclipse或者IDEA查看下继承链即可。

3. 默认Filter

这里我就不挨个解释了,只是列举一些本人在阅读源码过程中的细节问题。

扫描二维码关注公众号,回复: 1519956 查看本文章
  1. Shiro中,默认Filter由DefaultFilter枚举中定义。
  2. DefaultFilterChainManager构造函数中会将DefaultFilter枚举中默认的Filter进行载入。
  3. FormAuthenticationFilter为表单校验, 这会要求所有的请求以post发送; 源码参见FormAuthenticationFilter.onAccessDenied方法。
  4. 注意 user 和 authc 不同
    1. 当应用开启了rememberMe时,用户下次访问时可以是一个user,但绝不会是authc,因为authc是需要重新认证的
    2. user表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
    3. 说白了:以前的一个用户登录时开启了rememberMe,然后他关闭浏览器,下次再访问时他就是一个user,而不会authc
  1. 内置的FilterChain
  2. 《跟我学Shiro教程.pdf》 P87

猜你喜欢

转载自blog.csdn.net/lqzkcx3/article/details/78785780