欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解spring security filter的工作原理

 更新時間:2019年07月25日 15:04:09   作者:流浪的神明  
這篇文章主要介紹了詳解spring security filter的工作原理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

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

Filter如何進入執(zhí)行邏輯的

初始配置:

 <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實現(xiàn)了Filter接口。

這個配置是一切的開始,配置完這個之后,在啟動項目的時候會執(zhí)行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");
    }
  }

在初始化方法中,會執(zhí)行初始化Filter的方法initFilterBean。這個方法的實現(xiàn)在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); //這個方法
        }
      }
    }
  }

在這個初始化方法中又調(diào)用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,這個名字是構(gòu)造方法中賦值的:

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 啟動時內(nèi)置的一個bean,這個bean實際是FilterChainProxy。

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

當一個請求進來的時候,會進入FilterChainProxy執(zhí)行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,然后執(zhí)行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;
    }

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

VirtualFilterChain是一個匿名內(nèi)部類:

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集合執(zhí)行的邏輯在VirtualFilterChain的doFilter方法中。

filter是如何執(zhí)行的

上面說了怎么才能進入filter的執(zhí)行邏輯,下面說一下filter到底怎么執(zhí)行,為什么一個

在VirtualFilterChain的doFilter方法可以執(zhí)行所有的filter。

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

public interface Filter {

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

public interface FilterChain {

  void doFilter(String username, int age);
}

定義兩個Filter實現(xiàn):

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("正在執(zhí)行: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("正在執(zhí)行:AgeFilter");
    filterChain.doFilter(username, age);
  }
}

定義一個FilterChain實現(xiàn):

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("過濾器鏈執(zhí)行結(jié)束");
    } else {

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

測試Filter實現(xiàn):

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
正在執(zhí)行:NameFilter
username: 張三1  age: 10
正在執(zhí)行:AgeFilter
過濾器鏈執(zhí)行結(jié)束

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

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

VirtualFilterChain的doFilter方法的執(zhí)行邏輯和這個測試實例中的執(zhí)行邏輯基本一致。

這樣就完成了整個過濾器鏈的執(zhí)行。

總結(jié)

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

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java批量導入Excel數(shù)據(jù)超詳細實例

    java批量導入Excel數(shù)據(jù)超詳細實例

    這篇文章主要給大家介紹了關(guān)于java批量導入Excel數(shù)據(jù)的相關(guān)資料,EXCEL導入就是文件導入,操作代碼是一樣的,文中給出了詳細的代碼示例,需要的朋友可以參考下
    2023-08-08
  • SpringBoot集成yitter-idgenerator(雪花漂移)分布式Id自增的實現(xiàn)

    SpringBoot集成yitter-idgenerator(雪花漂移)分布式Id自增的實現(xiàn)

    本文主要介紹了SpringBoot集成yitter-idgenerator(雪花漂移)分布式Id自增的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • java讀取excel表格的方法

    java讀取excel表格的方法

    這篇文章主要為大家詳細介紹了java讀取excel表格的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • Java設計模式之解釋器模式(Interpreter模式)介紹

    Java設計模式之解釋器模式(Interpreter模式)介紹

    這篇文章主要介紹了Java設計模式之解釋器模式(Interpreter模式)介紹,Interpreter定義:定義語言的文法,并且建立一個解釋器來解釋該語言中的句子,需要的朋友可以參考下
    2015-03-03
  • SpringMVC源碼解讀之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化

    SpringMVC源碼解讀之 HandlerMapping - AbstractDetectingUrlHandlerM

    這篇文章主要介紹了SpringMVC源碼解讀之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化的相關(guān)資料,需要的朋友可以參考下
    2016-02-02
  • 如何只返回實體類中的部分字段問題

    如何只返回實體類中的部分字段問題

    這篇文章主要介紹了如何只返回實體類中的部分字段問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java解決計算相鄰兩個數(shù)的最大差值的問題

    Java解決計算相鄰兩個數(shù)的最大差值的問題

    今天給大家?guī)硪坏浪惴}:給定一個數(shù)組,求如果排序之后,相鄰兩數(shù)的最大差值。要求時間復雜度O(N),且要求不能用非基于比較的排序??靵砀S小編一起學習一下如何解決這一問題吧
    2021-12-12
  • Springboot整合Thymeleaf引入公共的CSS和JS文件的方法及注意點

    Springboot整合Thymeleaf引入公共的CSS和JS文件的方法及注意點

    有時候很多css文件是公共的,我們必須要在每個html文件中引入它們,下面這篇文章主要給大家介紹了關(guān)于Springboot整合Thymeleaf引入公共的CSS和JS文件的方法及注意點,需要的朋友可以參考下
    2024-06-06
  • 淺談Springboot2.0防止XSS攻擊的幾種方式

    淺談Springboot2.0防止XSS攻擊的幾種方式

    本文主要介紹了Springboot2.0防止XSS攻擊的幾種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • java堆排序概念原理介紹

    java堆排序概念原理介紹

    在本篇文章里我們給大家分享了關(guān)于java堆排序的概念原理相關(guān)知識點內(nèi)容,有需要的朋友們可以學習下。
    2018-10-10

最新評論