Shiro Filter的设计概念

Filter

AbstractFilter

顶端Filter抽象类,继承了ServletContextSupport,将javax.servlet.ServletContext交给其管理,实现了Filter则成为了一个过滤器

具备了javax.servlet.FilterConfig,既可以获得javax.servlet.ServletContext也可以获得web.xml配置文件中Filter的init-param的信息

具体实现Filter的行为

public final void init(FilterConfig filterConfig) throws ServletException {
    setFilterConfig(filterConfig);
    try {
    // 空方法留给子类具体实现 onFilterConfigSet(); }
catch (Exception e) { if (e instanceof ServletException) { throw (ServletException) e; } else { if (log.isErrorEnabled()) { log.error("Unable to start Filter: [" + e.getMessage() + "].", e); } throw new ServletException(e); } } } public void destroy() { }

NameableFilter

其主要职责就是存取FilterName

protected String getName() {
    if (this.name == null) {
        FilterConfig config = getFilterConfig();
        if (config != null) {
            this.name = config.getFilterName();
        }
    }

    return this.name;
}

OncePerRequestFilter

其主要职责就是具体实现Filter的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 //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);
        }
    }
}

其中这个FilterChain为org.apache.catalina.core.ApplicationFilterChain

第0个为org.springframework.web.filter.DelegatingFilterProxy,第1个可能为encodingFilter,最后一个为Tomcat Filter(org.apache.tomcat.websocket.server.WsFilter)

AbstractShiroFilter

其具备了WebSecurityManager和FilterChainResolver

使用FilterChainResolver中的FilterChainManager获得所有FilterChain的信息(Chain Name、Chain(Filter))、FilterChainManager获得包装为ProxiedFilterChain的FilterChain

其主要职责是执行Filter的doFilter方法

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

    Throwable t = null;

    try {
        // 包装ServletRequest为ShiroHttpServletRequest
        final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
        // 包装ServletRequest为ShiroHttpServletResponse
        final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
        // 每次都会重新创建一个Subject
        final Subject subject = createSubject(request, response);

        // 异步线程执行Chain链条
        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);
    }
}

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

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;
    }
    
    // 使用FilterChainResolver解析并包装获得FilterChain
    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;
}

一个请求过来后执行的过程

执行第一个Filter

假如第一个Filter为org.springframework.web.filter.DelegatingFilterProxy,执行doFilter方法

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    // 委派对象即org.apache.shiro.spring.web.ShiroFilterFactoryBean.SpringShiroFilter
    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 {

    delegate.doFilter(request, response, filterChain);
}

执行ShiroFilterFactoryBean$SpringShiroFilter

执行doFilter方法,实为OncePerRequestFilter的doFilter方法

异步执行吗?

AbstractShiroFilter的doFilterInternal方法中

subject.execute(new Callable() {
    public Object call() throws Exception {
        updateSessionLastAccessTime(request, response);
        executeChain(request, response, chain);
        return null;
    }
});

DelegatingSubject中

public <V> V execute(Callable<V> callable) throws ExecutionException {
    Callable<V> associated = associateWith(callable);
    try {
        return associated.call();
    } catch (Throwable t) {
        throw new ExecutionException(t);
    }
}

public <V> Callable<V> associateWith(Callable<V> callable) {
    return new SubjectCallable<V>(this, callable);
}

public void execute(Runnable runnable) {
    Runnable associated = associateWith(runnable);
    associated.run();
}

public Runnable associateWith(Runnable runnable) {
    if (runnable instanceof Thread) {
        String msg = "This implementation does not support Thread arguments because of JDK ThreadLocal " +
                "inheritance mechanisms required by Shiro.  Instead, the method argument should be a non-Thread " +
                "Runnable and the return value from this method can then be given to an ExecutorService or " +
                "another Thread.";
        throw new UnsupportedOperationException(msg);
    }
    return new SubjectRunnable(this, runnable);
}

这里比较有迷惑性的是Shiro对Filter的处理并不是异步线程的方式,异步线程的方式是:new Thread(new Runnable() {public void run() {}}).start();,而Shiro只是用了Callable或者是Runnable的API(call()方法或run()方法)

AdviceFilter

通知型过滤器,其行为主要有前置通知、执行过滤器链、后置通知、异常处理

public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
        throws ServletException, IOException {

    Exception exception = null;

    try {        
        // 前置通知
        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);
    }
}

PathMatchingFilter

路径匹配型过滤器,只有路径匹配上了才会过滤

protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

    if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
        if (log.isTraceEnabled()) {
            log.trace("appliedPaths property is null or empty.  This Filter will passthrough immediately.");
        }
        return true;
    }

    for (String path : this.appliedPaths.keySet()) {
        // If the path does match, then pass on to the subclass implementation for specific checks
        //(first match 'wins'):
        if (pathsMatch(path, request)) {
            log.trace("Current requestURI matches pattern '{}'.  Determining filter chain execution...", path);
            Object config = this.appliedPaths.get(path);
            return isFilterChainContinued(request, response, path, config);
        }
    }

    //no path matched, allow the request to go through:
    return true;
}

路径匹配上后的过滤处理

private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
                                       String path, Object pathConfig) throws Exception {

    if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
        if (log.isTraceEnabled()) {
            log.trace("Filter '{}' is enabled for the current request under path '{}' with config [{}].  " +
                    "Delegating to subclass implementation for 'onPreHandle' check.",
                    new Object[]{getName(), path, pathConfig});
        }
        //The filter is enabled for this specific request, so delegate to subclass implementations
        // 执行前置处理
        return onPreHandle(request, response, pathConfig);
    }

    if (log.isTraceEnabled()) {
        log.trace("Filter '{}' is disabled for the current request under path '{}' with config [{}].  " +
                "The next element in the FilterChain will be called immediately.",
                new Object[]{getName(), path, pathConfig});
    }
    //This filter is disabled for this specific request,
    //return 'true' immediately to indicate that the filter will not process the request
    //and let the request/response to continue through the filter chain:
    return true;
}

// 默认通过,子类可以覆盖进行特殊处理
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    return true;
}

AccessControlFilter

其主要行为有过滤前置处理中的通过判断(isAccessAllowed)、拒绝处理(onAccessDenied)、拒绝处理中的重定向(saveRequestAndRedirectToLogin)

Filter的大体设计思路

 

AbstractFilter顶端Filter,只具备Filter的一些特定的行为(init、doFilter、destroy),具备FilterConfig,继承SelServletContextSupport,将使用FilterConfig获得作用域后注入到后者

NameableFilter,只负责Filter的Name

OncePerRequestFilter,负责Filter的doFilter的公共行为

AbstractShiroFilter,包装FilterChain为ProxiedFilterChain,后者具备了原始FilterChain和路径匹配的ShiroFilter集合,执行完ShiroFilter集合后执行原始FilterChain,开始执行其他类型的Filter如EncodingFilter

AdviceFilter通知型Filter,具备前置通知等,通过执行其他的Filter,不通过重定向到其他路径

 

posted @ 2018-07-24 19:57  BINGJJFLY  阅读(364)  评论(0编辑  收藏  举报