Shiro Filter的設計概念

weixin_30588675發表於2020-04-05

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,不通過重定向到其他路徑

 

轉載於:https://www.cnblogs.com/BINGJJFLY/p/9362224.html

相關文章