Shiro源码研究之Filter
Filter
作为Shiro无可争议的特色之一,完全值得上写一篇博客。
1. 概述
书接前一篇Shiro源码研究之处理一次完整的请求,在前一篇博客中我们了解到:Shiro 对Servlet 容器的FilterChain 进行了代理,即ShiroFilter 在继续Servlet 容器的Filter链的执行之前,通过ProxiedFilterChain
对Servlet 容器的FilterChain 进行了代理;即先走Shiro 自己的Filter 体系,然后才会委托给Servlet 容器的FilterChain 进行Servlet 容器级别的Filter链执行;
2. 体系结构
让我们先看看Shiro中的Filter的继承体系。继承链如图
让我们沿着继承链进行讨论。
2.1 OncePerRequestFilter
这个类和Spring实现的那个同名类;功能上一模一样。都是保证此filter只调用一次。不必多说。
2.2 AdviceFilter
作为相当底层的Filter基类,执行的总体流程就是在AdviceFilter
中被规定好了的(AdviceFilter.doFilterInternal
)
-
preHandle
返回true继续之后的Filter流程。 -
executeChain(request, response, chain);
继续之后的Filter。 -
postHandle(request, response);
本Filter之后的全部Filter都执行完毕之后, 没有发生异常时才会执行。 -
cleanup(request, response, exception);
finally执行的,如果exception不为null 表明执行过程中发生了异常。
1.cleanup
中又预留出了一个afterCompletion
的空函数,供子类覆写。 - 其实本类连
doFilterInternal
都没有final化。可以说留出了最大的扩展空间。
总结
1. Shiro在这里使用了继承方式来实现了AOP效果,子类通过覆写自身感兴趣的方法来参与到Filter的执行逻辑中,其他通用的逻辑扭转和重复性代码被放在基类中避免冗余。
2. 通观核心的AdviceFilter.doFilterInternal
方法,AdviceFilter
只是在其中规定了每个Filter执行时的逻辑扭转骨架,而将具体的实现完全交给子类,自身不作任何假设,这样就保证了框架最大的灵活性。
2.3 PathMatchingFilter
- 覆写了直接基类
AdviceFilter
的preHandle
方法; 在其中暴露了isEnabled
,onPreHandle
供子类覆写。 - 其子类主要覆写的是onPreHandle ; 返回true继续之后的Filter流程。
- 提供了简化方法
getPathWithinApplication
(获取当前请求的路径地址)。
总结
1. 此类是所有默认Filter的基类。
2. url的路径权限的关系(即在ShiroFilterFactoryBean
中配置的filterChainDefinitions
属性值)就是在此类的appliedPaths
字段中存储着。
3. 内部默认使用 Ant 语法(AntPathMatcher
)来进行路径匹配工作。
2.4 AccessControlFilter
- 覆写了直接基类
PathMatchingFilter
的onPreHandle
方法 ,并在其中暴露了isAccessAllowed
和onAccessDenied
供子类覆写。( 注意这两个方法之间是 或 的关系,即有一个返回true即可;而且前者返回true, 后者就不会执行了 )。-
isAccessAllowed
:即是否允许访问,返回true 表示允许; -
onAccessDenied
:表示访问拒绝时是否自己处理,如果返回true 表示自己不处理且继续拦截器链执行,返回false表示自己已经处理了(比如重定向到另一个页面)。
-
- 提供了简化方法
getSubject()
,getLoginUrl()
,isLoginRequest()
,redirectToLogin
(注意Shiro提供的默认只能处理页面请求,对于Ajax请求无法跳转),saveRequest
,saveRequestAndRedirectToLogin
。
总结
1. 本类主要的职责是进行读取资源时的权限控制,以及非授权用户的退出到登录地址的操作。
2.5 AuthenticationFilter(鉴权)
- 覆写了直接基类
AccessControlFilter
的isAccessAllowed
方法。 默认实现逻辑就是验证当前subject是否isAuthenticated。 所以子类还有机会去实现onAccessDenied 来在前者返回false时扭转整个逻辑,当然你直接去覆写isAccessAllowed
也是允许的(逻辑优先)。 - 提供了简化方法
getSuccessUrl()
,issueSuccessRedirect
(Redirects to user to the previously attempted URL after a successful login. 重定向到用户上一次的访问的页面,前提是要成功登录)。
2.6 AuthenticatingFilter
- 直接继承自
AuthenticationFilter
。 - 覆写了
AuthenticationFilter
的isAccessAllowed
(Permissive就是在这里被校验的),以及AdviceFilter
的cleanup
。 - 提供了简化方法
getHost
,executeLogin
(其中回调自身定义的createToken
方法,所以子类主要是复写createToken
),isPermissive
,isRememberMe
(直接返回false)。 - 暴露给子类
onLoginFailure
(默认返回false),onLoginSuccess
(默认返回true) — 这两个方法在本类中的executeLogin
中被调用。 - 官方注释 : An AuthenticationFilter that is capable of automatically performing an authentication attempt based on the incoming request. 对过来的请求自动作一次鉴权操作。
2.7 FormAuthenticationFilter
- 直接继承自
AuthenticatingFilter
。 - 覆写了
AuthenticatingFilter
的createToken
;onAccessDenied
,onLoginFailure
,onLoginSuccess
。 - 提供了简化方法
getPassword
,getUsername
,isRememberMe
,isLoginSubmission
,setLoginUrl
。
2.8 AuthorizationFilter(授权)
- 覆写了
AccessControlFilter
的onAccessDenied
。 - 提供了简化方法
getUnauthorizedUrl()
- 继承自该类,必须覆写其从
AccessControlFilter
继承来的抽象方法isAccessAllowed
。
更具体的Filter就不再这里进行详细描述了,感兴趣的读者可以找个Eclipse或者IDEA查看下继承链即可。
3. 默认Filter
这里我就不挨个解释了,只是列举一些本人在阅读源码过程中的细节问题。
- Shiro中,默认Filter由
DefaultFilter
枚举中定义。 - 在
DefaultFilterChainManager
构造函数中会将DefaultFilter
枚举中默认的Filter进行载入。 -
FormAuthenticationFilter
为表单校验, 这会要求所有的请求以post发送; 源码参见FormAuthenticationFilter.onAccessDenied
方法。 - 注意 user 和 authc 不同
- 当应用开启了rememberMe时,用户下次访问时可以是一个user,但绝不会是authc,因为authc是需要重新认证的
- user表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
- 说白了:以前的一个用户登录时开启了rememberMe,然后他关闭浏览器,下次再访问时他就是一个user,而不会authc
Links
- 内置的FilterChain
- 《跟我学Shiro教程.pdf》 P87