zoukankan      html  css  js  c++  java
  • 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,不通过重定向到其他路径

  • 相关阅读:
    vs运行代码版本不一致删除缓存
    spring framework 各版本源码下载地址
    类型初始值设定项引发异常
    datagridview 点击列标题排序
    软媒魔方u盘装系统
    sql中exists,not exists的用法
    java web第一个Servlet程序
    《ASP.NET 本质论》HttpApplication的处理管道 ,HttpMoudle,HttpHandler
    如何正确设置 Informix GLS 及 CSDK 语言环境
    Linux网络流量控制工具—Netem
  • 原文地址:https://www.cnblogs.com/BINGJJFLY/p/9362224.html
Copyright © 2011-2022 走看看