Shiro源码研究之处理一次完整的请求
2017-12-14 09:36
330 查看
经过上一篇博客里的讨论,我们知道了最终被并入到Servlet的FilterChain中的Filter实例为
1.
现在让我们来看看
代码很简单,只有
2.
作为一个Filter,其核心方法当然就是
3.
处理请求时的核心方法
4.
5.
6.
7.
Shiro 对Servlet 容器的FilterChain 进行了代理,即ShiroFilter 在继续Servlet 容器的Filter链的执行之前,通过ProxiedFilterChain`对Servlet 容器的FilterChain 进行了代理;即先走Shiro 自己的Filter 体系,然后才会委托给Servlet 容器的FilterChain 进行Servlet 容器级别的Filter链执行; 《跟开涛学Shiro》P77
ShiroFilterFactoryBean.SpringShiroFilter类型。而这篇博客就让我们来看看Shiro是如何借助这个Filter来完成权限校验这个庞大功能的?
1. ShiroFilterFactoryBean.SpringShiroFilter
类
现在让我们来看看ShiroFilterFactoryBean.SpringShiroFilter类的类定义。
// ShiroFilterFactoryBean.SpringShiroFilter private static final class SpringShiroFilter extends AbstractShiroFilter { protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) { super(); if (webSecurityManager == null) { throw new IllegalArgumentException("WebSecurityManager property cannot be null."); } setSecurityManager(webSecurityManager); if (resolver != null) { setFilterChainResolver(resolver); } } }
代码很简单,只有
securityManager和
filterChainResolver字段的设置,因此主要逻辑肯定就在其基类
AbstractShiroFilter中了。
2. AbstractShiroFilter.doFilterInternal
类
作为一个Filter,其核心方法当然就是doFilter了。
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain) throws ServletException, IOException { Throwable t = null; try { // 将接收到的servletRequest,servletResponse使用Wrapper模式封装为自定义的ShiroHttpServletRequest, ShiroHttpServletResponse final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain); final ServletResponse response = prepareServletResponse(request, servletResponse, chain); // 构建一个Subject // 这里的subject的实际类型是WebDelegatingSubject // 详细解释参见本人的另外一篇博客: http://blog.csdn.net/lqzkcx3/article/details/78801403 final Subject subject = createSubject(request, response); //noinspection unchecked 4000 // 这个execute方法实际被定义在DelegatingSubject类中 // 这个execute将方法的调用者subject存入到当前Thread的线程本地存储中,也就是将该Subject实例与当前Thread进行绑定。具体细节参见本人的另一篇博客http://blog.csdn.net/lqzkcx3/article/details/78801403的第6节。 // 那样在我们调用`SecurityUtils.getSubject()`就能直接获取到该Subject实例了。 subject.execute(new Callable() { public Object call() throws Exception { // 更新最后读取session的时间 updateSessionLastAccessTime(request, response); // 核心,下文进行讲解 executeChain(request, response, chain); return null; } }); } catch (ExecutionException ex) { t = ex.getCause(); } catch (Throwable throwable) { t = throwable; } if (t != null) { if (t instanceof ServletException) { throw (ServletException) t; } if (t instanceof IOException) { throw (IOException) t; } //otherwise it's not one of the two exceptions expected by the filter method signature - wrap it in one: String msg = "Filtered request failed."; throw new ServletException(msg, t); } }
3. AbstractShiroFilter.executeChain
方法
处理请求时的核心方法// AbstractShiroFilter.executeChain protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain) throws IOException, ServletException { FilterChain chain = getExecutionChain(request, response, origChain); chain.doFilter(request, response); }
4. AbstractShiroFilter.getExecutionChain
方法
// AbstractShiroFilter.getExecutionChain protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) { FilterChain chain = origChain; //上一篇文章提到了,filterChainResolver字段被硬编码为PathMatchingFilterChainResolver;当然我们是可以自定义,不过很少有这个必要。 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."); chain = resolved; } else { log.trace("No FilterChain configured for the current request. Using the default."); } return chain; }
5. PathMatchingFilterChainResolver.getChain
方法
// PathMatchingFilterChainResolver.getChain public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) { // DefaultFilterChainManager FilterChainManager filterChainManager = getFilterChainManager(); if (!filterChainManager.hasChains()) { return null; } // 以 http://www.somehost.com/myapp/my/url.jsp举例的话;这里返回的requestURI, 其值为 /my/url.jsp String requestURI = getPathWithinApplication(request); //the 'chain names' in this implementation are actually path patterns defined by the user. We just use them //as the chain name for the FilterChainManager's requirements /* 这里我们以上一篇博客的例子里的配置来进行讲解 /login.jsp = authc /logout = logout /unauthorized.jsp = anon /main** = authc /admin/list** = authc,perms[admin:manage] /user/info-anon** = anon /user/info** = authc /** = anon 每次迭代的pathPattern就是上面的"/login.jsp", "/logout","/unauthorized.jsp"等等 */ // 一个pathPattern可能被配置多个Filter, 而shiro采用了继承自List<Filter>的NamedFilterList接口来存储这些Filter // 所以DefaultFilterChainManager类中有一个Map<String, NamedFilterList>类型的filterChains字段, 其中存放的正式上面用户配置的关系 // NamedFilterList接口到1.3.2为止, 唯一的实现依然是SimpleNamedFilterList // DefaultFilterChainManager类中另外一个Map<String, Filter>类型的字段 filters ,让我们看看其内部的存储: // {anon=anon, authc=authc, authcBasic=authcBasic, logout=logout, noSessionCreation=noSessionCreation, perms=perms, port=port, rest=rest, roles=roles, ssl=ssl, user=user, xxxxModuleAuthc=xxxxModuleAuthc, upmsFormAuthenticationFilter=upmsFormAuthenticationFilter, upmsSessionForceLogout=upmsSessionForceLogout} // 所以它里面存储的是默认的Filter + 我们自定义注册的Filter // 筛选出与当前请求链接匹配的FilterChain for (String pathPattern : filterChainManager.getChainNames()) { // If the path does match, then pass on to the subclass implementation for specific checks: // 这里默认的匹配模式为org.apache.shiro.util.AntPathMatcher。 if (pathMatches(pathPattern, requestURI)) { // 一旦匹配, 则对Servlet的FilterChain进行代理。保证Shiro自定义的Filter执行完毕之后,再执行Servlet的Filter。 // 下文进行细节讲解 return filterChainManager.proxy(originalChain, pathPattern); } } return null; }
6. DefaultFilterChainManager.proxy
方法
public FilterChain proxy(FilterChain original, String chainName) { // 从上一小节里讲解的filterChains字段中取出对应的NamedFilterList NamedFilterList configured = getChain(chainName); if (configured == null) { String msg = "There is no configured chain under the name/key [" + chainName + "]."; throw new IllegalArgumentException(msg); } // proxy实现是方法的实现如下 : // return new ProxiedFilterChain(orig, this); return configured.proxy(original); }
7. ProxiedFilterChain
类
ProxiedFilterChain类 实现了Servlet规范中的
FilterChain接口,
// ProxiedFilterChain.doFilter public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Shiro自身的Filter执行完毕之后; 开始执行Servlet源生的Filter if (this.filters == null || this.filters.size() == this.index) { //we've reached the end of the wrapped chain, so invoke the original one: if (log.isTraceEnabled()) { log.trace("Invoking original filter chain."); } this.orig.doFilter(request, response); } else { if (log.isTraceEnabled()) { log.trace("Invoking wrapped filter at index [" + this.index + "]"); } // 执行Shiro自身定义的Filter。 // 关于filters字段在上一小节中已经谈及过;更具体的还请参见下一篇博客。Shiro针对Filter的继承链还是比较深的,值得专门写一篇。 this.filters.get(this.index++).doFilter(request, response, this); } }
8. 总结
相比较本人之前对Filter的扩展,一直考虑的都是向
FilterChain中插入自定义
Filter; Shiro则直接来了个更狠的,直接将整个
FilterChain代理掉,先执行完我的
FilterChain,才考虑
Servlet的。
Shiro 对Servlet 容器的FilterChain 进行了代理,即ShiroFilter 在继续Servlet 容器的Filter链的执行之前,通过ProxiedFilterChain`对Servlet 容器的FilterChain 进行了代理;即先走Shiro 自己的Filter 体系,然后才会委托给Servlet 容器的FilterChain 进行Servlet 容器级别的Filter链执行; 《跟开涛学Shiro》P77
相关文章推荐
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- Mybatis工作机制源码分析—一次insert请求处理流程
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- Tomcat源码分析-- 一次完整请求的里里外外
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- Mybatis工作机制源码分析—一次select请求处理流程
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- Tomcat源码分析(二)------ 一次完整请求的里里外外 .
- 一次完整的HTTP请求处理过程
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- http服务详解(1)——一次完整的http服务请求处理过程
- Tomcat源码分析(二)------ 一次完整请求的里里外外
- Tomcat源码分析------ 一次完整请求的里里外外
- Yii2.0源码阅读-一次请求的完整过程
- HTTP 之 一次完整的http请求处理过程
- 一次完整的http请求处理过程
- Tomcat源码分析(二)------ 一次完整请求的里里外外