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