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