在配置shiro的时候第一件事情就是在web.xml文件中配置一个由spring提供的类:org.springframework.web.filter.DelegatingFilterProxy 按照字面的翻译这应该是一个代理过滤器的策略。css
<filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <async-supported>true</async-supported> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
这个类实际上是将上下文中名称为shiroFilter的类作成一个代理过滤器。该类将从spring上下文中找到本身要代理的过滤器类,并负责初始化和销毁该过滤器。并在每次发起的拦截请求是先走该类的方法invokeDelegate()。java
##初始化web
protected Filter initDelegate(WebApplicationContext wac) throws ServletException { Filter delegate = wac.getBean(getTargetBeanName(), Filter.class); if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); } return delegate; }
初始化过程就是将配置文件中的参数注入到ShiroFilterFactoryBean中的SpringShiroFilter中。spring
##请求调用 当咱们在浏览器中打印了一个可以被shiro拦截的uri的时候首先会进入到DelegatingFilterProxy的doFilter方法中。该方法首先判断被代理的拦截器是否被初始化,若是没有则实行懒加载策略初始化shiro拦截器。不然则开始调用shiro拦截器去执行拦截。设计模式
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Lazily initialize the delegate if necessary. Filter delegateToUse = this.delegate; if (delegateToUse == null) {//懒加载过程 synchronized (this.delegateMonitor) { if (this.delegate == null) { WebApplicationContext wac = findWebApplicationContext(); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?"); } this.delegate = initDelegate(wac); } delegateToUse = this.delegate; } } invokeDelegate(delegateToUse, request, response, filterChain); } protected void invokeDelegate( Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 调用真正shirofilter delegate.doFilter(request, response, filterChain); }
delegate实例最终类型是SpringShiroFilter。该类集成在AbstractShiroFilter。浏览器
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); } } }
AbstractShiroFilter类继承自OncePerRequestFiltersession
public abstract class AbstractShiroFilter extends OncePerRequestFilter{}
因此咱们首先来看看OncePerRequestFilter。mvc
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {//不拦截已经被拦截处理的请求 log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName()); filterChain.doFilter(request, response); } else //不过滤被配置不过滤的请求 if (/* added in 1.2: */ !isEnabled(request, response) || /* retain backwards compatibility: */ shouldNotFilter(request) ) { log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.", getName()); filterChain.doFilter(request, response); } else { //未来自该过滤请求(如shiroFitler)设置成已经被过滤 log.trace("Filter '{}' not yet executed. Executing now.", getName()); request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { //执行shiro的过滤逻辑。 doFilterInternal(request, response, filterChain); } finally { // Once the request has finished, we're done and we don't // need to mark as 'already filtered' any more. request.removeAttribute(alreadyFilteredAttributeName); } } }
若是到达的请求没有被OncePerRequestFilter过滤掉,则会走shiro的拦截请求。值得一块儿的是,FilterChain chain实际上是spring的servlet上下文。这个是为了包装request、response的时候将servlet上下文也放置进来。app
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain) throws ServletException, IOException { Throwable t = null; try { final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);//包装request final ServletResponse response = prepareServletResponse(request, servletResponse, chain);//包装response final Subject subject = createSubject(request, response);//建立subject对象 //执行回调函数。 //一、更新session的最后访问时间 //二、执行拦截器链。 subject.execute(new Callable() { public Object call() throws Exception { 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); } }
shiro的拦截器首先将request和response进行了包装。并建立subject执行回调函数。该函数主要执行了两个部分:一、更新session的最后访问时间 二、执行拦截器链。重点看下执行拦截器链逻辑。框架
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain) throws IOException, ServletException { FilterChain chain = getExecutionChain(request, response, origChain);//得到拦截器链 chain.doFilter(request, response);//执行拦截器链 }
拦截器逻辑主要分红了两个部分:一、得到拦截器链 二、执行拦截器链。 一、得到拦截器链
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) { FilterChain chain = origChain; //1.1得到拦截器链解析器 FilterChainResolver resolver = getFilterChainResolver(); if (resolver == null) { log.debug("No FilterChainResolver configured. Returning original FilterChain."); return origChain; } //1.2得到最终的拦截器执行链。 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; }
1.一、得到拦截器链解析器最终得到了PathMatchingFilterChainResolver的实例。 1.二、得到最终的拦截器执行链是执行PathMatchingFilterChainResolver中的getChain方法。
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) { //得到过滤链管理器。该管理器是在初始化SpringShiroFilter时候注入进来的。 FilterChainManager filterChainManager = getFilterChainManager(); if (!filterChainManager.hasChains()) { return null; } //得到请求uri String requestURI = getPathWithinApplication(request); //uri和拦截器链作匹配。 for (String pathPattern : filterChainManager.getChainNames()) { //若是匹配成功 if (pathMatches(pathPattern, requestURI)) { //1.3返回一个代理过滤器 return filterChainManager.proxy(originalChain, pathPattern); } } return null; } ``` 1.3是将当前的servlet上下文和请求uri一块儿包装成为一个过滤器代理。 ```java FilterChainManager类 public FilterChain proxy(FilterChain original, String chainName) { NamedFilterList configured = getChain(chainName); if (configured == null) { String msg = "There is no configured chain under the name/key [" + chainName + "]."; throw new IllegalArgumentException(msg); } return configured.proxy(original); } public class SimpleNamedFilterList implements NamedFilterList { public FilterChain proxy(FilterChain orig) { return new ProxiedFilterChain(orig, this); } }
二、执行拦截器链 刚才看到得到拦截器的过程其实就是生成了一个类型为ProxiedFilterChain的实例。那么执行天然就是该类的doFilter方法。
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { //若是过滤器链为空,则执行servlet上下文的过滤器。 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 + "]"); } this.filters.get(this.index++).doFilter(request, response, this); } }
这里面涵盖了一个过滤器的设计模式。当咱们给某个uri设计多个拦截器的时候,这几个拦截器是怎么连续执行的呢?换句话说上面的index是如何一个一个累加直到this.filters.size() == this.index条件成立跳出循环的呢?咱们来看下shiro里面的拦截器链实现。 首先观察一下shiro配置文章的这个段
/static/** = anon /js/** =anon /css/** =anon /favicon.ico =anon /images/** = anon /logout = logout /user/login=authc /** =sysUser,onlineSession,syncOnlineSession,perms,roles
咱们不难发现这里面的拦截器都扩展自接口AdviceFilter或接口AdviceFilter的实现。给接口做用就是在执行拦截器先后添加一些逻辑。
public abstract class AdviceFilter extends OncePerRequestFilter { protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { return true; } protected void postHandle(ServletRequest request, ServletResponse response) throws Exception { } public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception { } protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception { chain.doFilter(request, response); } //执行逻辑 public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { Exception exception = null; try { //执行前的逻辑,若是未返回true则不执行过滤器逻辑。 boolean continueChain = preHandle(request, response); if (log.isTraceEnabled()) { log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]"); } //执行过滤器逻辑 if (continueChain) { executeChain(request, response, chain); } //执行后逻辑。 postHandle(request, response); if (log.isTraceEnabled()) { log.trace("Successfully invoked postHandle method"); } } catch (Exception e) { exception = e; } finally { cleanup(request, response, exception); } } protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) throws ServletException, IOException { } }
AdviceFilter扩展自OncePerRequestFilter类。
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); if ( request.getAttribute(alreadyFilteredAttributeName) != null ) { log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName()); //执行过滤器链下一个过滤器 filterChain.doFilter(request, response); } else //noinspection deprecation if (/* added in 1.2: */ !isEnabled(request, response) || /* retain backwards compatibility: */ shouldNotFilter(request) ) { log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.", getName()); //执行过滤器链下一个过滤器 filterChain.doFilter(request, response); } else { // Do invoke this filter... log.trace("Filter '{}' not yet executed. Executing now.", getName()); request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { //执行本过滤器。 doFilterInternal(request, response, filterChain); } finally { // Once the request has finished, we're done and we don't // need to mark as 'already filtered' any more. request.removeAttribute(alreadyFilteredAttributeName); } } }
这两个类的共同特色就是执行doFilterInternal方法是执行本过滤器的过滤逻辑。执行filterChain是执行过利器链里面下一个过滤器的执行逻辑。filterChain的类型都是ProxiedFilterChain。
public class ProxiedFilterChain implements FilterChain { //TODO - complete JavaDoc private static final Logger log = LoggerFactory.getLogger(ProxiedFilterChain.class); private FilterChain orig; private List<Filter> filters; private int index = 0; public ProxiedFilterChain(FilterChain orig, List<Filter> filters) { if (orig == null) { throw new NullPointerException("original FilterChain cannot be null."); } this.orig = orig; this.filters = filters; this.index = 0; } public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { 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."); } //若是过滤器为空,或者过滤器链已经执行完,则orig里面若是有过滤器逻辑则执行。 this.orig.doFilter(request, response); } else { if (log.isTraceEnabled()) { log.trace("Invoking wrapped filter at index [" + this.index + "]"); } //得到第index个过滤器并执行。 this.filters.get(this.index++).doFilter(request, response, this); } }
过滤器模式简单地说就是:到了某个过滤器他会执行本身的逻辑(doFilterInternal方法),执行完本身的逻辑以后,他还会到刚刚传过来的过滤器链的doFilter方法返回到过滤器链下一个过滤器继续执行。 这里也有个疑惑,就是过滤器链执行完毕以后应该怎么返回到springmvc的DispatcherServlet里面的service()方法继续spring的逻辑呢? 答案就在这行: this.orig.doFilter(request, response); 咱们在配置到web.xml文件中的过滤器和servlet的执行顺序首先按照配置的前后顺序执行过滤器,当过滤器执行完毕的时候会调用 FilterChain的doFilter()方法返回到servlet环境,而后再原路返回。那么为何orig.doFilter可以返回到servlet呢,查看源码filterChian既不是servlet的代理也不是servlet的包装器。那什么时机让orig成为了servlet返回路径的呢? dubug到这里咱们发现了如图的类:ServletHandler。原来这个ServletHandler是jetty用于管理Filter、FilterMapping、Servlet、ServletMapping的容器。(我用的是jetty,本段环境为jetty环境)。以及用于实现一次请求所对应的Filter链和Servlet执行流程的类。对Servlet的框架实现中,它也被认为是Handler链的末端,于是在它的doHandle()方法中没有调用nextHandle()方法。至于具体实现就等分析jetty源码的时候阐述了。