spring security filter的工作原理

這篇文章介紹filter的工作原理。配置方式爲xml。

Filter如何進入執行邏輯的

初始配置:

 <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

DelegatingFilterProxy這個類繼承了GenericFilterBean,GenericFilterBean實現了Filter接口。

這個配置是一切的開始,配置完這個之後,在啓動項目的時候會執行Filterd的初始化方法:

@Override
    public final void init(FilterConfig filterConfig) throws ServletException {
        Assert.notNull(filterConfig, "FilterConfig must not be null");
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
        }

        this.filterConfig = filterConfig;

        // Set bean properties from init parameters.
        PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
                Environment env = this.environment;
                if (env == null) {
                    env = new StandardServletEnvironment();
                }
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                String msg = "Failed to set bean properties on filter '" +
                    filterConfig.getFilterName() + "': " + ex.getMessage();
                logger.error(msg, ex);
                throw new NestedServletException(msg, ex);
            }
        }

        // Let subclasses do whatever initialization they like.
        initFilterBean(); // 這個方法

        if (logger.isDebugEnabled()) {
            logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
        }
    }

在初始化方法中,會執行初始化Filter的方法initFilterBean。這個方法的實現在DelegatingFilterProxy中:

protected void initFilterBean() throws ServletException {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                // If no target bean name specified, use filter name.
                if (this.targetBeanName == null) {
                    this.targetBeanName = getFilterName();
                }
                // Fetch Spring root application context and initialize the delegate early,
                // if possible. If the root application context will be started after this
                // filter proxy, we'll have to resort to lazy initialization.
                WebApplicationContext wac = findWebApplicationContext();
                if (wac != null) {
                    this.delegate = initDelegate(wac); //這個方法
                }
            }
        }
    }

在這個初始化方法中又調用initDelegate方法進行初始化:

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        String targetBeanName = getTargetBeanName();
        Assert.state(targetBeanName != null, "No target bean name set");
        Filter delegate = wac.getBean(targetBeanName, Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }

在這個方法中,先獲取targetBeanName,這個名字是構造方法中賦值的:

public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {
        Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
        this.setTargetBeanName(targetBeanName);
        this.webApplicationContext = wac;
        if (wac != null) {
            this.setEnvironment(wac.getEnvironment());
        }
    }

這個名字就是web.xml中配置的名字springSecurityFilterChain:

 <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>

springSecurityFilterChain是固定不能改的,如果改了啓動時就會報錯,這是spring 啓動時內置的一個bean,這個bean實際是FilterChainProxy。

這樣一個Filter就初始化話好了,過濾器chain也初始化好了。

當一個請求進來的時候,會進入FilterChainProxy執行doFilter方法:

public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
        if (clearContext) {
            try {
                request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
                doFilterInternal(request, response, chain);
            }
            finally {
                SecurityContextHolder.clearContext();
                request.removeAttribute(FILTER_APPLIED);
            }
        }
        else {
            doFilterInternal(request, response, chain);
        }
    }

先獲取所有的Filter,然後執行doFilterInternal方法:

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

        FirewalledRequest fwRequest = firewall
                .getFirewalledRequest((HttpServletRequest) request);
        HttpServletResponse fwResponse = firewall
                .getFirewalledResponse((HttpServletResponse) response);

        List<Filter> filters = getFilters(fwRequest);

        if (filters == null || filters.size() == 0) {
            if (logger.isDebugEnabled()) {
                logger.debug(UrlUtils.buildRequestUrl(fwRequest)
                        + (filters == null ? " has no matching filters"
                                : " has an empty filter list"));
            }

            fwRequest.reset();

            chain.doFilter(fwRequest, fwResponse);

            return;
        }

        // 最終執行下面的這些代碼
        VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
        vfc.doFilter(fwRequest, fwResponse);
    }

VirtualFilterChain是一個匿名內部類:

private static class VirtualFilterChain implements FilterChain {
        private final FilterChain originalChain;
        private final List<Filter> additionalFilters;
        private final FirewalledRequest firewalledRequest;
        private final int size;
        private int currentPosition = 0;

        private VirtualFilterChain(FirewalledRequest firewalledRequest,
                FilterChain chain, List<Filter> additionalFilters) {
            this.originalChain = chain;
            this.additionalFilters = additionalFilters;
            this.size = additionalFilters.size();
            this.firewalledRequest = firewalledRequest;
        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException {
            if (currentPosition == size) {
                if (logger.isDebugEnabled()) {
                    logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
                            + " reached end of additional filter chain; proceeding with original chain");
                }

                // Deactivate path stripping as we exit the security filter chain
                this.firewalledRequest.reset();

                originalChain.doFilter(request, response);
            }
            else {
                currentPosition++;

                Filter nextFilter = additionalFilters.get(currentPosition - 1);

                if (logger.isDebugEnabled()) {
                    logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
                            + " at position " + currentPosition + " of " + size
                            + " in additional filter chain; firing Filter: '"
                            + nextFilter.getClass().getSimpleName() + "'");
                }

                nextFilter.doFilter(request, response, this);
            }
        }
    }

filter集合執行的邏輯在VirtualFilterChain的doFilter方法中。

filter是如何執行的

上面說了怎麼才能進入filter的執行邏輯,下面說一下filter到底怎麼執行,爲什麼一個
在VirtualFilterChain的doFilter方法可以執行所有的filter。

下面寫一個例子,模擬filter的執行邏輯。
定義FilterChain接口、Filter接口:

public interface Filter {

    void doFilter(String username, int age, FilterChain filterChain);
}
public interface FilterChain {

    void doFilter(String username, int age);
}

定義兩個Filter實現:

public class NameFilter implements Filter {

    @Override
    public void doFilter(String username, int age, FilterChain filterChain) {

        username = username + 1;
        System.out.println("username: " + username + "   age: " + age);
        System.out.println("正在執行:NameFilter");
        filterChain.doFilter(username, age);
    }
}
public class AgeFilter implements Filter {

    @Override
    public void doFilter(String username, int age, FilterChain filterChain) {

        age += 10;
        System.out.println("username: " + username + "   age: " + age);
        System.out.println("正在執行:AgeFilter");
        filterChain.doFilter(username, age);
    }
}

定義一個FilterChain實現:

public class FilterChainProxy implements FilterChain {


    private int position = 0;
    private int size = 0;
    private List<Filter> filterList = new ArrayList<>();

    public void addFilter(Filter filter) {

        filterList.add(filter);
        size++;
    }

    @Override
    public void doFilter(String username, int age) {

        if (size == position) {
            System.out.println("過濾器鏈執行結束");
        } else {

            Filter filter = filterList.get(position);
            position++;
            filter.doFilter(username, age, this);
        }
    }
}

測試Filter實現:

public class FilterTest {

    public static void main(String[] args) {

        FilterChainProxy proxy = new FilterChainProxy();
        proxy.addFilter(new NameFilter());
        proxy.addFilter(new AgeFilter());

        proxy.doFilter("張三", 0);
    }
}
=======
username: 張三1   age: 0
正在執行:NameFilter
username: 張三1   age: 10
正在執行:AgeFilter
過濾器鏈執行結束

在這個執行邏輯中,最重要的是【this】,this就是初始化的好的FilterChain實例,在這個測試實例中,this就是FilterChainProxy。

執行FilterChainProxy的doFilter方法的時候,傳入了初始參數username和age,進入這個方法後,根據position取出相應的Filter,初次進入position是0,執行Filter的doFilter方法,注意,此時Filter的doFilter方法額外傳入了一個this參數,這個參數就是初始化的好的FilterChain實例,在Filter中的doFilter的方法中最後又會執行FilterChain的doFilter方法,相當於第二次調用FilterChain實例的doFilter方法,此時posotion是1,然後再執行Filter的doFilter方法,直到所有的Filter執行完,整個執行過程結束。

VirtualFilterChain的doFilter方法的執行邏輯和這個測試實例中的執行邏輯基本一致。
這樣就完成了整個過濾器鏈的執行。

總結

以前用Filter的時候就非常疑惑過濾器怎麼執行的,直到今天才算解決了這個疑惑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章