Shiro使用和原理分析---2

Shiro使用和原理分析—2

为了方便,这里继续引用一下applicationContext.xml中的配置文件

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="customAuthenticationFilter"/>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>  
                /test = anon
                /** = authc
            </value>
        </property>
    </bean>

假设将web项目部署至tomcat,tomcat会启动socket监听客户端的连接,然后经过层层处理(这里是设计模式中的责任链模式),最后会调用doFilter函数。ShiroFilterFactoryBean经过层层继承,最上层实现了javax.servlet.Filter接口,该接口主要有三个函数init、destroy和doFilter。

上一章讲过ShiroFilterFactoryBean最最终会构造SpringShiroFilter。首先看一下SpringShiroFilter的类结构,

private static final class SpringShiroFilter extends AbstractShiroFilter public abstract class AbstractShiroFilter extends OncePerRequestFilter public abstract class OncePerRequestFilter extends NameableFilter public abstract class NameableFilter extends AbstractFilter implements Nameable public abstract class AbstractFilter extends ServletContextSupport implements Filter public class ServletContextSupport

简单来说,AbstractShiroFilter就是处理一些doFilter的逻辑部分,OncePerRequestFilter用来保证shiro对request只过滤一次,NameableFilter和Filter的名字相关,AbstractFilter则是一些基础的配置,ServletContextSupport则是web环境上下文。

SpringShiroFilter的init函数定义在AbstractShiroFilter中,为空函数。
接下来看SpringShiroFilter的doFilter函数,tomcat最后会调用到这个函数,doFilter定义在OncePerRequestFilter中。

doFilter

    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 (!isEnabled(request, response) || shouldNotFilter(request) ) {
            log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.",
                    getName());
            filterChain.doFilter(request, response);
        } else {
            log.trace("Filter '{}' not yet executed. Executing now.", getName());
            request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

            try {
                doFilterInternal(request, response, filterChain);
            } finally {
                request.removeAttribute(alreadyFilteredAttributeName);
            }
        }
    }

该函数通过request中的alreadyFilteredAttributeName属性判断该request是否已经被该filter过滤过,如果已经被过滤了,就跳过该过滤器。另外,过滤器必须设置为enable即开启,如果关闭了该过滤器,isEnabled函数判断enable为false,则也跳过该过滤器。其他情况下,则进入该filter过滤器。首先设置request属性alreadyFilteredAttributeName为true,表是已经通过该过滤器了,下次不经过了,然后调用doFilterInternal。

doFilterInternal定义在AbstractShiroFilter中,如下

    protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
            throws ServletException, IOException {

        Throwable t = null;

        try {
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);

            final Subject subject = createSubject(request, response);

            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;
            }
            String msg = "Filtered request failed.";
            throw new ServletException(msg, t);
        }
    }

prepareServletRequest和prepareServletResponse都是对上层传入的参数ServletRequest和ServletResponse进行再包装。
createSubject构造了一个WebSubject,updateSessionLastAccessTime用来更新时间,这里就不往下分析了,后面的章节再来看这些函数。
本章重点分析executeChain这个函数,先看一下

    protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
            throws IOException, ServletException {
        FilterChain chain = getExecutionChain(request, response, origChain);
        chain.doFilter(request, response);
    }

origChain是上层传来的一个FilterChaingetExecutionChain获得一个新的FilterChain,它和origChain有什么区别呢?往下看。

getExecutionChain

    protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
        FilterChain chain = origChain;

        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;
    }

首先,getFilterChainResolver获得上一章中构造的PathMatchingFilterChainResolver,所以接下来getChain的函数就定义在PathMatchingFilterChainResolver中,如下所示

    public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
        FilterChainManager filterChainManager = getFilterChainManager();
        if (!filterChainManager.hasChains()) {
            return null;
        }

        String requestURI = getPathWithinApplication(request);

        for (String pathPattern : filterChainManager.getChainNames()) {
            if (pathMatches(pathPattern, requestURI)) {
                if (log.isTraceEnabled()) {
                    log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "]. " +
                            "Utilizing corresponding filter chain...");
                }
                return filterChainManager.proxy(originalChain, pathPattern);
            }
        }

        return null;
    }

filterChainManager也是上一章在createInstance函数中构造的DefaultFilterChainManager。接下来,从filterChainManager中依次取出chainName(pathPattern)进行比较,如果匹配,就调用proxy函数。filterChainManager中的chainName便是上一章中设置的“/test”和“/**”。

proxy

    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);
    }

首先看一下getChain函数,

    public NamedFilterList getChain(String chainName) {
        return this.filterChains.get(chainName);
    }

上一章中,在filterChains中根据过滤器链名chainName设置的NamedFilterList为SimpleNamedFilterList,因此继续看SimpleNamedFilterList的proxy函数。

    public FilterChain proxy(FilterChain orig) {
        return new ProxiedFilterChain(orig, this);
    }

好了,到这里,回头,层层往上再到前面的executeChain函数,开始调用doFilter函数。总结一下,getExecutionChain返回的其实是一个ProxiedFilterChain实例,这个实例是根据request中取得本次客户端请求的相对路径再和chainName匹配得到的。因此,看一下ProxiedFilterChain的doFilter函数。

doFilter

    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (this.filters == null || this.filters.size() == this.index) {
            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);
        }
    }

简单来说,这里就是在原本的过滤器中插入shiro的过滤器。shiro的过滤器有啥,便是配置的anon和authc对应的过滤器,一个是默认的过滤器AnonymousFilter(上一章),另一个则是自定义的customAuthenticationFilter。下一章继续介绍它们。

你可能感兴趣的:(Shiro使用和原理分析---2)