spring bootspring cloud 微服务

spring cloud OAuth2资源服务器的过滤器链路浅析

2018-01-29  本文已影响1248人  大浪滔滔

1. 导语

在Spring cloud体系中设计OAuth2应用时,当我们使用@EnableResourceServer注解把某个spring boot应用声明为一个OAuth2的资源服务器时,spring cloud将会创建一系列的web安全过滤器链。

接下来,我将带大家一起将分析spring cloud的源码,分析过滤器链路的工作原理。

2. spring cloud的web安全配置

spring boot启动时,在WebSecurityConfiguration中加载所有的web安全配置,并根据Order进行排序:

package org.springframework.security.config.annotation.web.configuration;

@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {

    // 注意:webSecurityConfigurers参数通过注解的方式注入
    @Autowired(required = false)
    public void setFilterChainProxySecurityConfigurer(
            ObjectPostProcessor<Object> objectPostProcessor,
            @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
            throws Exception {
        webSecurity = objectPostProcessor
                .postProcess(new WebSecurity(objectPostProcessor));
        if (debugEnabled != null) {
            webSecurity.debug(debugEnabled);
        }

        // 根据@Order顺序进行排序
        Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);

        Integer previousOrder = null;
        Object previousConfig = null;
        for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
            Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
            if (previousOrder != null && previousOrder.equals(order)) {
                throw new IllegalStateException(
                        "@Order on WebSecurityConfigurers must be unique. Order of "
                                + order + " was already used on " + previousConfig + ", so it cannot be used on "
                                + config + " too.");
            }
            previousOrder = order;
            previousConfig = config;
        }
        for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
            webSecurity.apply(webSecurityConfigurer);
        }
        this.webSecurityConfigurers = webSecurityConfigurers;
    }

}

注意:webSecurityConfigurers参数通过注解的方式注入:

@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers

即在spring容器里查找autowiredWebSecurityConfigurersIgnoreParents的实例并调用其getWebSecurityConfigurer()方法。

AutowiredWebSecurityConfigurersIgnoreParents类的作用是从spring容器中找到WebSecurityConfigure接口的所有实例:

final class AutowiredWebSecurityConfigurersIgnoreParents {

    private final ConfigurableListableBeanFactory beanFactory;

    public AutowiredWebSecurityConfigurersIgnoreParents(
            ConfigurableListableBeanFactory beanFactory) {
        Assert.notNull(beanFactory, "beanFactory cannot be null");
        this.beanFactory = beanFactory;
    }

    // 从spring容器中找到WebSecurityConfigurer接口的所有实例
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
        List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
        Map<String, WebSecurityConfigurer> beansOfType = beanFactory
                .getBeansOfType(WebSecurityConfigurer.class);
        for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
            webSecurityConfigurers.add(entry.getValue());
        }
        return webSecurityConfigurers;
    }
}

找到的实例包括:

顺序 (Order) 说明
IgnoredPathsWebSecurityConfigurerAdapter -2147483648:Ordered.HIGHEST_PRECEDENCE 白名单
ManagementWebSecurityConfigurerAdapter 2147483637:Ordered.LOWEST_PRECEDENCE - 10 spring自身的管理/监控端点过滤器链
ResourceServerConfiguration 2147483639:Ordered.LOWEST_PRECEDENCE - 8 资源服务器相关的过滤器链
ApplicationWebSecurityConfigurerAdapter 2147483642:Ordered.LOWEST_PRECEDENCE - 5 默认的web安全过滤器链

其中:
ResourceServerConfiguration的Order默认为3,但在OAuth2的资源服务器中,运行时将会被覆盖为其他值,具体值在ResourceServerProperties类的属性filterOrder中定义:

@ConfigurationProperties(prefix = "security.oauth2.resource")
public class ResourceServerProperties implements Validator, BeanFactoryAware {

    /**
     * The order of the filter chain used to authenticate tokens. Default puts it after
     * the actuator endpoints and before the default HTTP basic filter chain (catchall).
     */
    private int filterOrder = SecurityProperties.ACCESS_OVERRIDE_ORDER - 1;

3. 安全配置的初始化

接下来,在AbstractConfiguredSecurityBuilder类中对安全配置类进行初始化:

package org.springframework.security.config.annotation;

public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
        extends AbstractSecurityBuilder<O> {

    private void init() throws Exception {
        // configurers里面初始化了4个配置
        Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

        for (SecurityConfigurer<O, B> configurer : configurers) {
            configurer.init((B) this);
        }

        for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
            configurer.init((B) this);
        }
    }
}

configurers里面包含以下4个web安全配置类,即上面在spring容器中查找到的4个安全配置类实例:

[0] SpringBootWebSecurityConfiguration$IgnoredPathsWebSecurityConfigurerAdapter  (id=7391)  
[1] ManagementWebSecurityAutoConfiguration$ManagementWebSecurityConfigurerAdapter$$EnhancerBySpringCGLIB$$b7d4741a  (id=105)    
[2] ResourceServerConfiguration$$EnhancerBySpringCGLIB$$dae34c39  (id=7392) 
[3] SpringBootWebSecurityConfiguration$ApplicationWebSecurityConfigurerAdapter$$EnhancerBySpringCGLIB$$39896683  (id=7393)

ResourceServerConfiguration的配置

package org.springframework.security.oauth2.config.annotation.web.configuration;

@Configuration
public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered {
  
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer();
        ResourceServerTokenServices services = resolveTokenServices();
        if (services != null) {
            resources.tokenServices(services);
        }
        else {
            if (tokenStore != null) {
                resources.tokenStore(tokenStore);
            }
            else if (endpoints != null) {
                resources.tokenStore(endpoints.getEndpointsConfigurer().getTokenStore());
            }
        }
        if (eventPublisher != null) {
            resources.eventPublisher(eventPublisher);
        }
        for (ResourceServerConfigurer configurer : configurers) {
            configurer.configure(resources);
        }
        // @formatter:off
        http.authenticationProvider(new AnonymousAuthenticationProvider("default"))
        // N.B. exceptionHandling is duplicated in resources.configure() so that
        // it works
        .exceptionHandling()
                .accessDeniedHandler(resources.getAccessDeniedHandler()).and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .csrf().disable();
        // @formatter:on
        http.apply(resources);
        if (endpoints != null) {
            // Assume we are in an Authorization Server
            http.requestMatcher(new NotOAuthRequestMatcher(endpoints.oauth2EndpointHandlerMapping()));
        }
        for (ResourceServerConfigurer configurer : configurers) {
            // Delegates can add authorizeRequests() here
            configurer.configure(http);
        }
        if (configurers.isEmpty()) {
            // Add anyRequest() last as a fall back. Spring Security would
            // replace an existing anyRequest() matcher with this one, so to
            // avoid that we only add it if the user hasn't configured anything.
            http.authorizeRequests().anyRequest().authenticated();
        }
    }
}

4. 将安全配置类转化为安全过滤器链

将安全配置类转化为安全过滤器链的这个工作是由WebSecurity类来完成的,我们来看下相关代码:

package org.springframework.security.config.annotation.web.builders;
public final class WebSecurity extends
        AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
        SecurityBuilder<Filter>, ApplicationContextAware {
    @Override
    protected Filter performBuild() throws Exception {
        Assert.state(
                !securityFilterChainBuilders.isEmpty(),
                "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
                        + WebSecurity.class.getSimpleName()
                        + ".addSecurityFilterChainBuilder directly");
        int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
        List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
                chainSize);
        // 一、针对白名单url创建一条空的过滤器链(Chain A)
        for (RequestMatcher ignoredRequest : ignoredRequests) {
            securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
        }
        // 二、安全相关的过滤器链,可能为多条,这里是3条。
        for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
            securityFilterChains.add(securityFilterChainBuilder.build());
        }
        FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
        if (httpFirewall != null) {
            filterChainProxy.setFirewall(httpFirewall);
        }
        filterChainProxy.afterPropertiesSet();

        Filter result = filterChainProxy;
        if (debugEnabled) {
            logger.warn("\n\n"
                    + "********************************************************************\n"
                    + "**********        Security debugging is enabled.       *************\n"
                    + "**********    This may include sensitive information.  *************\n"
                    + "**********      Do not use in a production system!     *************\n"
                    + "********************************************************************\n\n");
            result = new DebugFilter(filterChainProxy);
        }
        postBuildAction.run();
        return result;
    }

在上面的代码中,spring cloud一共创建了4条过滤器链。

其中:

4.1 Chain A:配置了一个静态文件url的白名单

对应着前面的白名单安全配置类IgnoredPathsWebSecurityConfigurerAdapter

具体的配置在:

package org.springframework.boot.autoconfigure.security;
public class SpringBootWebSecurityConfiguration {
    private static List<String> DEFAULT_IGNORED = Arrays.asList("/css/**", "/js/**",
            "/images/**", "/webjars/**", "/**/favicon.ico");
    // ...

    private class DefaultIgnoredRequestCustomizer implements IgnoredRequestCustomizer {

        // 这里定义了一个魔法
        private List<String> getIgnored(SecurityProperties security) {
            List<String> ignored = new ArrayList<String>(security.getIgnored());
            if (ignored.isEmpty()) {
                ignored.addAll(DEFAULT_IGNORED);
            }
            else if (ignored.contains("none")) {
                ignored.remove("none");
            }
            return ignored;
        }
    }
}

我们可以看到SpringBootWebSecurityConfiguration定义了一个默认的uri白名单,里面定义了一些静态文件的uri:

因此,对匹配这些uri的http请求,将会跳过后续所有的过滤器链。
那么,接下来,我们可能会有疑问,如果我想对这些uri进行定制化怎么办?
答案就在getIgnored()方法,它定义了一个魔法,因此允许我们对白名单进行定制化。有两种方法:
1、在application.yml中配置security.ignored属性

security: 
  ignored: none

如果security.ignored=none,则忽略上面的白名单。
当然,你也可以做个性化的配置,如:

security: 
  ignored: /css/**,/js/**,/images/**,/webjars/**,/**/favicon.ico,/static/**

2、或者通过代码实现

@Component
public class CustomSecurityProperties extends SecurityProperties {
    public CustomSecurityProperties() {
        // the default list is empty
        List<String> ignoredPaths = getIgnored();
        ignoredPaths.add("none");
    }
}

4.2 安全相关的过滤器链,这里创建了3条

对应前面的其他3个过滤器安全配置类:

  • ManagementWebSecurityConfigurerAdapter,spring自身的管理/监控端点过滤器链
  • ResourceServerConfiguration,资源服务器相关的过滤器链
  • ApplicationWebSecurityConfigurerAdapter,默认的web安全过滤器链

1. ManagementWebSecurityConfigurerAdapter

ManagementWebSecurityConfigurerAdapter是spring自身的管理/监控端点过滤器链。包括以下端点:

OrRequestMatcher 
    [requestMatchers=
        [
            Ant [pattern='/pause'], 
            Ant [pattern='/pause/**'], 
            Ant [pattern='/pause.*'], 
            Ant [pattern='/pause/'], 
            Ant [pattern='/env'], 
            Ant [pattern='/env/**'], 
            Ant [pattern='/env.*'], 
            Ant [pattern='/env/'], 
            Ant [pattern='/features'], 
            Ant [pattern='/features/**'], 
            Ant [pattern='/features.*'], 
            Ant [pattern='/features/'], 
            Ant [pattern='/loggers'], 
            Ant [pattern='/loggers/**'], 
            Ant [pattern='/loggers.*'], 
            Ant [pattern='/loggers/'], 
            Ant [pattern='/archaius'], 
            Ant [pattern='/archaius/**'], 
            Ant [pattern='/archaius.*'], 
            Ant [pattern='/archaius/'], 
            Ant [pattern='/autoconfig'], 
            Ant [pattern='/autoconfig/**'], 
            Ant [pattern='/autoconfig.*'], 
            Ant [pattern='/autoconfig/'], 
            Ant [pattern='/mappings'], 
            Ant [pattern='/mappings/**'], 
            Ant [pattern='/mappings.*'], 
            Ant [pattern='/mappings/'], 
            Ant [pattern='/refresh'], 
            Ant [pattern='/refresh/**'], 
            Ant [pattern='/refresh.*'], 
            Ant [pattern='/refresh/'], 
            Ant [pattern='/restart'], 
            Ant [pattern='/restart/**'], 
            Ant [pattern='/restart.*'], 
            Ant [pattern='/restart/'], 
            Ant [pattern='/info'], 
            Ant [pattern='/info/**'], 
            Ant [pattern='/info.*'], 
            Ant [pattern='/info/'], 
            Ant [pattern='/resume'], 
            Ant [pattern='/resume/**'], 
            Ant [pattern='/resume.*'], 
            Ant [pattern='/resume/'], 
            Ant [pattern='/metrics'], 
            Ant [pattern='/metrics/**'], 
            Ant [pattern='/metrics.*'], 
            Ant [pattern='/metrics/'], 
            Ant [pattern='/beans'], 
            Ant [pattern='/beans/**'], 
            Ant [pattern='/beans.*'], 
            Ant [pattern='/beans/'], 
            Ant [pattern='/trace'], 
            Ant [pattern='/trace/**'], 
            Ant [pattern='/trace.*'], 
            Ant [pattern='/trace/'], 
            Ant [pattern='/auditevents'], 
            Ant [pattern='/auditevents/**'], 
            Ant [pattern='/auditevents.*'], 
            Ant [pattern='/auditevents/'], 
            Ant [pattern='/configprops'], 
            Ant [pattern='/configprops/**'], 
            Ant [pattern='/configprops.*'], 
            Ant [pattern='/configprops/'], 
            Ant [pattern='/service-registry'], 
            Ant [pattern='/service-registry/**'], 
            Ant [pattern='/service-registry.*'], 
            Ant [pattern='/service-registry/'], 
            Ant [pattern='/dump'], 
            Ant [pattern='/dump/**'], 
            Ant [pattern='/dump.*'], 
            Ant [pattern='/dump/'], 
            Ant [pattern='/health'], 
            Ant [pattern='/health/**'], 
            Ant [pattern='/health.*'], 
            Ant [pattern='/health/'], 
            Ant [pattern='/heapdump'], 
            Ant [pattern='/heapdump/**'], 
            Ant [pattern='/heapdump.*'], 
            Ant [pattern='/heapdump/']
        ]
    ]

2. ResourceServerConfiguration

ResourceServerConfiguration对应着我们自定义的资源服务器的配置类,我们可以在里面配置我们自定义的过滤器。例如:

/**
 * 自定义资源服务器配置
 * 
 * @author 大浪滔滔
 *
 */
@Configuration
@EnableConfigurationProperties(SecuritySettings.class)
public class DefaultResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        // 自定义的过滤器
        http.addFilterBefore(threadLocalFilter(), WebAsyncManagerIntegrationFilter.class);
        http.addFilterAfter(accessFilter(), ThreadLocalFilter.class);
        http.addFilterAfter(serviceAuthFilter, ThreadLocalFilter.class);
      
        // ...
    }
}

在这个过滤器链中包含以下过滤器:

序号 过滤器
[0] ThreadLocalFilter (id=382),安全上下文过滤器。
[1] WebAsyncManagerIntegrationFilter (id=383)
[2] AccessFilter (id=384),日志过滤器。
[3] ServiceAuthFilter (id=385),微服务token认证过滤器。
[4] SecurityContextPersistenceFilter (id=387)
[5] HeaderWriterFilter (id=388)
[6] LogoutFilter (id=389)
[7] OAuth2AuthenticationProcessingFilter (id=390)
[8] RequestCacheAwareFilter (id=391)
[9] SecurityContextHolderAwareRequestFilter (id=392)
[10] AnonymousAuthenticationFilter (id=393)
[11] SessionManagementFilter (id=394)
[12] ExceptionTranslationFilter (id=395)
[13] FilterSecurityInterceptor (id=396)

其中,ThreadLocalFilter、AccessFilter和ServiceAuthFilter是我们自定义的过滤器。

其他过滤器是spring自动生成的,见以下代码:

package org.springframework.security.config.annotation.web.configuration;

@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
        WebSecurityConfigurer<WebSecurity> {
  
    protected final HttpSecurity getHttp() throws Exception {
        if (http != null) {
            return http;
        }

        DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
                .postProcess(new DefaultAuthenticationEventPublisher());
        localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

        AuthenticationManager authenticationManager = authenticationManager();
        authenticationBuilder.parentAuthenticationManager(authenticationManager);
        Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();

        http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
                sharedObjects);
        if (!disableDefaults) {
            // @formatter:off
            http
                .csrf().and() // 
                .addFilter(new WebAsyncManagerIntegrationFilter())
                .exceptionHandling().and()
                .headers().and()
                .sessionManagement().and()
                .securityContext().and()
                .requestCache().and()
                .anonymous().and()
                .servletApi().and()
                .apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
                .logout();
            // @formatter:on
            ClassLoader classLoader = this.context.getClassLoader();
            List<AbstractHttpConfigurer> defaultHttpConfigurers =
                    SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

            for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
                http.apply(configurer);
            }
        }
        configure(http);
        return http;
    }
}

3. ApplicationWebSecurityConfigurerAdapter

ApplicationWebSecurityConfigurerAdapter是spring默认的web安全过滤器链,它对uri的匹配规则是

OrRequestMatcher [requestMatchers=[Ant [pattern='/**']]]

即匹配所有的http请求。

它包括以下过滤器:

序号 过滤器
[0] WebAsyncManagerIntegrationFilter (id=370)
[1] SecurityContextPersistenceFilter (id=371)
[2] HeaderWriterFilter (id=372)
[3] LogoutFilter (id=373)
[4] BasicAuthenticationFilter (id=374)
[5] RequestCacheAwareFilter (id=375)
[6] SecurityContextHolderAwareRequestFilter (id=376)
[7] AnonymousAuthenticationFilter (id=377)
[8] SessionManagementFilter (id=378)
[9] ExceptionTranslationFilter (id=379)
[10] FilterSecurityInterceptor (id=380)

5. 过滤器链的选择

在前面的分析中,spring创建了4条过滤器链,那么,是否每条链都会被执行呢?

答案是否定的,这4条过滤器链每条都有uri的匹配规则,spring将会根据http请求的uri按顺序去匹配,第1条符合条件的过滤器链将会被选中并执行。

相关的代码如下:

package org.springframework.security.web;

public class FilterChainProxy extends GenericFilterBean {
  
    /**
     * Returns the first filter chain matching the supplied URL.
     *
     * @param request the request to match
     * @return an ordered array of Filters defining the filter chain
     */
    private List<Filter> getFilters(HttpServletRequest request) {
        for (SecurityFilterChain chain : filterChains) {
            if (chain.matches(request)) {
                return chain.getFilters();
            }
        }

        return null;
    }

6. 过滤器的执行顺序

HttpSecurity类定义了一个安全过滤器排序器FilterComparator

package org.springframework.security.config.annotation.web.builders;

public final class HttpSecurity extends
        AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
        implements SecurityBuilder<DefaultSecurityFilterChain>,
        HttpSecurityBuilder<HttpSecurity> {
          
    // 过滤器排序器
    private FilterComparator comparator = new FilterComparator();
          
}          

FilterComparator类定义一些spring自带过滤器的默认顺序:

package org.springframework.security.config.annotation.web.builders;

final class FilterComparator implements Comparator<Filter>, Serializable {
    private static final int STEP = 100;
    private Map<String, Integer> filterToOrder = new HashMap<String, Integer>();

    FilterComparator() {
        int order = 100;
        put(ChannelProcessingFilter.class, order);
        order += STEP;
        put(ConcurrentSessionFilter.class, order);
        order += STEP;
        put(WebAsyncManagerIntegrationFilter.class, order);
        order += STEP;
        put(SecurityContextPersistenceFilter.class, order);
        order += STEP;
        put(HeaderWriterFilter.class, order);
        order += STEP;
        put(CorsFilter.class, order);
        order += STEP;
        put(CsrfFilter.class, order);
        order += STEP;
        put(LogoutFilter.class, order);
        order += STEP;
        put(X509AuthenticationFilter.class, order);
        order += STEP;
        put(AbstractPreAuthenticatedProcessingFilter.class, order);
        order += STEP;
        filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
                order);
        order += STEP;
        put(UsernamePasswordAuthenticationFilter.class, order);
        order += STEP;
        put(ConcurrentSessionFilter.class, order);
        order += STEP;
        filterToOrder.put(
                "org.springframework.security.openid.OpenIDAuthenticationFilter", order);
        order += STEP;
        put(DefaultLoginPageGeneratingFilter.class, order);
        order += STEP;
        put(ConcurrentSessionFilter.class, order);
        order += STEP;
        put(DigestAuthenticationFilter.class, order);
        order += STEP;
        put(BasicAuthenticationFilter.class, order);
        order += STEP;
        put(RequestCacheAwareFilter.class, order);
        order += STEP;
        put(SecurityContextHolderAwareRequestFilter.class, order);
        order += STEP;
        put(JaasApiIntegrationFilter.class, order);
        order += STEP;
        put(RememberMeAuthenticationFilter.class, order);
        order += STEP;
        put(AnonymousAuthenticationFilter.class, order);
        order += STEP;
        put(SessionManagementFilter.class, order);
        order += STEP;
        put(ExceptionTranslationFilter.class, order);
        order += STEP;
        put(FilterSecurityInterceptor.class, order);
        order += STEP;
        put(SwitchUserFilter.class, order);
    }
}
过滤器名称 过滤器类 顺序 Order值 说明
MetricsFilter @Order(Ordered.HIGHEST_PRECEDENCE) -2147483648
OrderedCharacterEncodingFilter -2147483648
OrderedHiddenHttpMethodFilter -10000
OrderedHttpPutFormContentFilter -9900
OrderedRequestContextFilter -105
ThreadLocalFilter -104 自定义过滤器
ServiceAuthFilter -103 自定义过滤器
AccessFilter -102 自定义过滤器
springSecurityFilterChain DelegatingFilterProxyRegistrationBean -100
webRequestLoggingFilter WebRequestTraceFilter 2147483637
上一篇下一篇

猜你喜欢

热点阅读