shiro的代理过滤器

在配置shiro的时候第一件事情就是在web.xml文件中配置一个由spring提供的类:org.springframework.web.filter.DelegatingFilterProxy 按照字面的翻译这应该是一个代理过滤器的策略。

<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()。

##初始化

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}

初始化过程就是将配置文件中的参数注入到ShiroFilterFactoryBean中的SpringShiroFilter中。

##请求调用 当我们在浏览器中打印了一个能够被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类继承自OncePerRequestFilter

public abstract class AbstractShiroFilter extends OncePerRequestFilter{}

所以我们首先来看看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 //不过滤被配置不过滤的请求
            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上下文也放置进来。

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对象

            //执行回调函数。
            //1、更新session的最后访问时间
            //2、执行拦截器链。
            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执行回调函数。该函数主要执行了两个部分:1、更新session的最后访问时间 2、执行拦截器链。重点看下执行拦截器链逻辑。

 protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        FilterChain chain = getExecutionChain(request, response, origChain);//获得拦截器链
        chain.doFilter(request, response);//执行拦截器链
    }

拦截器逻辑主要分成了两个部分:1、获得拦截器链 2、执行拦截器链。 1、获得拦截器链

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.1、获得拦截器链解析器最终获得了PathMatchingFilterChainResolver的实例。 1.2、获得最终的拦截器执行链是执行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);
    }
   }

2、执行拦截器链 刚才看到获得拦截器的过程其实就是生成了一个类型为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源码的时候阐述了。

转载于:https://my.oschina.net/zjItLife/blog/713675

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值