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