美文网首页spring security程序员
Spring Security配置学习笔记

Spring Security配置学习笔记

作者: 无知者云 | 来源:发表于2018-12-29 09:11 被阅读0次
  • AbstractSecurityWebApplicationInitializer用于向web容器中加入DelegatingFilterProxy并制定代理的bean的名字为SpringSecurity专用的springSecurityFilterChain
    private void insertSpringSecurityFilterChain(ServletContext servletContext) {
        String filterName = DEFAULT_FILTER_NAME;
        DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(
                filterName);
        String contextAttribute = getWebApplicationContextAttribute();
        if (contextAttribute != null) {
            springSecurityFilterChain.setContextAttribute(contextAttribute);
        }
        registerFilter(servletContext, true, filterName, springSecurityFilterChain);
    }

这种方式通常用于纯Spring MVC(非Spring Boot)项目中无web.xml配置Spring Security,类似于在web.xml中加入:

    <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>
  • 对于Spring Boot,我们没必要用AbstractSecurityWebApplicationInitializer了,因为SecurityFilterAutoConfiguration通过以下方式将DelegatingFilterProxy加入到了web容器:
    @Bean
    @ConditionalOnBean(name = DEFAULT_FILTER_NAME)
    public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
            SecurityProperties securityProperties) {
        DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
                DEFAULT_FILTER_NAME);
        registration.setOrder(securityProperties.getFilterOrder());
        registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
        return registration;
    }
  • Spring Security采用Servlet的Filter机制,其作用于任何Servlet之前,其中便包括Spring MVC的DispatcherServlet,因此在Spring Security眼中,DispatcherServlet只是一个普通的Servlet而已,和其他Servlet没有什么区别。因此,Spring Security和Spring MVC没有什么关系,可以一并使用,也可以单独将Spring Security用于其他环境,但是Spring Security是需要Spring的IoC容器支持的,因此在非Spring MVC的的Servelt容器中使用时,依然需要配置ContextLoaderListener或者使用AbstractContextLoaderInitializer。

  • Spring Security的配置中有两个重要的配置类,一个是SecuirtyBuilder,这是一个非常泛型化的类,用于构建任何对象,一个是SecurityConfigurator,用于对SecuirtyBuilder进行配置。WebSecurity和HttpSecurity都是SecuirtyBuilder的子类,WebSecurity用于构建出一个FilterChainProxy(一种Filter),而HttpSecurity用于构建出一个DefaultSecurityFilterChain,这些从二者的类定义中可以看出。SecurityConfigurator主要用于配置HttpSecurity,作用是向DefaultSecurityFilterChain注入各种Filter,比如CorsConfigurer会向HttpSecurity中加入CorsFilter。

  • @EnableWebSecurity通常将在WebSecurityConfigurerAdapter上,并与@Configuration一并使用,这样一方面可以启用Spring Security,另一方面WebSecurityConfigurerAdapter也可用于配置其他方面,并且WebSecurityConfigurerAdapter本身也是一个bean(加了@Configuration的类都是一个bean),进而可以被WebSecurityConfiguration自动注入完成配置工作。

  • @EnableWebSecurity最重要的作用是通过@Import使WebSecurityConfiguration生效。WebSecurityConfiguration中定义了多个Spring Security的Bean,其中最重要的有个名为springSecurityFilterChain的bean:

    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
        boolean hasConfigurers = webSecurityConfigurers != null
                && !webSecurityConfigurers.isEmpty();
        if (!hasConfigurers) {
            WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
                    .postProcess(new WebSecurityConfigurerAdapter() {
                    });
            webSecurity.apply(adapter);
        }
        return webSecurity.build();
    }

这个名为springSecurityFilterChain便是Spring Security在启动时通过DelegatingFilterProxy所要找到的bean。

  • 这个名为springSecurityFilterChain的bean的真实类型为FilterChainProxy,这个FilterChainProxy也是Spring Security的总入口,FilterChainProxy维护了多个SecurityFilterChain的List,SecurityFilterChain又维护了一个Filter的列表,真实其作用的其实是SecurityFilterChain的Filter列表(比如其中包含有UsernamePasswordAuthenticationFilter和LogoutFilter等)。当请求来时会依次调用各个SecurityFilterChain的matches()方法来确定由哪个SecurityFilterChain处理,当找到第一个之后,后续的SecurityFilterChain便没有机会了,因此放在List前面的SecurityFilterChain会优先起作用,在实际配置时,越精确的路径匹配应该放在越前面。在Spring中可以通过@Order来确定List中自动注入bean的顺序,也即通过在WebSecurityConfigurerAdapter上加上@Order,那么在WebSecurityConfiguration中,webSecurityConfigurers列表中的多个WebSecurityConfigurerAdapter便已经按照顺序排列了。默认情况SecurityFilterChain可以处理所有请求,在配置该SecurityFilterChain时,可以使用requestMatcher进行过滤。
    关于Spring Security的架构讲解请参考这里这里

  • 在配置时,有两个主要配置类,一个是WebSecurity,一个是HttpSecurity,整个Spring Security中只有一个WebSecurity,并在WebSecurityConfiguration中完成初始化,而HttpSecruity可以有多个,一个WebSecurityConfigurerAdapter对应创建一个HttpSecurity,进而对应一个SecurityFilterChain。

  • WebSecurityConfiguration创建WebSecurity:

    @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);
        }

        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;
    }
  • WebSecurityConfigurerAdapter创建HttpSecurity:
    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);
        authenticationBuilder.authenticationEventPublisher(eventPublisher);
        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<>()).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;
    }
  • WebSeurity主要用于配置需要忽略的请求:
   @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers("/bi/email-login/**");
    }
  • HttpSecurity主要用于配置各种其作用的Filter,因此在配置Spring Security时,我们主要配置的是HttpSecrity,比如:
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()
                .authorizeRequests()
                .anyRequest()
                .authenticated();
    }
  • WebSecurity用于配置FilterChainProxy,整个Spring Security中只有一个FilterChainProxy,也即只有一个WebSeurity配置,WebSecurity创建于@EnableWebSecurity的入口配置WebSecurityConfiguration中。

  • 如果不想Spring Security应用于某个路径,可以通过WebSecurity的ignoring()进行配置,实际上WebSecurity会根据该ignoring()配置创建一个SecurityFilterChain,只是该SecurityFilterChain没有任何Filter而已。

  • HttpSecurity用于一对一配置SecurityFilterChain,一个FilterChainProxy可以包含多个SecurityFilterChain,因此Spring Security在配置时可以有多个HttpSecurity。HttpSecurity创建于WebSecurityConfigurerAdapter中,也即一个WebSecurityConfigurerAdapter对应配置一个HttpSecurity,进而对应配置一个SecurityFilterChain。

  • WebSecurityConfiguration在初始化的时候会通过@Autowire自动注入所实现了WebSecurityConfigurerAdapter接口bean,所有这些WebSecurityConfigurerAdapter可以配置相同的一个WebSecurity,并且可以配置自己的HttpSecurity。

  • Spring Boot通过SecurityAutoConfiguration配置了一个空的WebSecurityConfigurerAdapter,然后通过UserDetailsServiceAutoConfiguration配置了一个in memory的用户,用户名user,随机密码,并由此创建一个InMemoryUserDetailsManager类型的Bean。InMemoryUserDetailsManager实现了UserDetailsService,该InMemoryUserDetailsManager会被Spring Security的AuthenticationConfiguration通过globalAuthConfigurers找到,即InitializeUserDetailsManagerConfigurer/InitializeUserDetailsBeanManagerConfigurer会在IoC容器中查找UserDetailsService类型bean,并将其用于构建全局的AuthenticationManager。由于InitializeUserDetailsBeanManagerConfigurer实现了GlobalAuthenticationConfigurerAdapter,因此在AuthenticationConfiguration中会被自动找到:

    @Autowired(required = false)
    public void setGlobalAuthenticationConfigurers(
            List<GlobalAuthenticationConfigurerAdapter> configurers) throws Exception {
        Collections.sort(configurers, AnnotationAwareOrderComparator.INSTANCE);
        this.globalAuthConfigurers = configurers;
    }
  • Spring Security通过FilterComparator对SecurityFilterChain中的多个Filter进行排序,可以看到Filter的默认顺序都是写死的。
FilterComparator() {
        Step order = new Step(INITIAL_ORDER, ORDER_STEP);
        put(ChannelProcessingFilter.class, order.next());
        put(ConcurrentSessionFilter.class, order.next());
        put(WebAsyncManagerIntegrationFilter.class, order.next());
        put(SecurityContextPersistenceFilter.class, order.next());
        put(HeaderWriterFilter.class, order.next());
        put(CorsFilter.class, order.next());
        put(CsrfFilter.class, order.next());
        put(LogoutFilter.class, order.next());
        filterToOrder.put(
            "org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
                order.next());
        put(X509AuthenticationFilter.class, order.next());
        put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
        filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
                order.next());
        filterToOrder.put(
            "org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
                order.next());
        put(UsernamePasswordAuthenticationFilter.class, order.next());
        put(ConcurrentSessionFilter.class, order.next());
        filterToOrder.put(
                "org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
        put(DefaultLoginPageGeneratingFilter.class, order.next());
        put(DefaultLogoutPageGeneratingFilter.class, order.next());
        put(ConcurrentSessionFilter.class, order.next());
        put(DigestAuthenticationFilter.class, order.next());
        filterToOrder.put(
                "org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter", order.next());
        put(BasicAuthenticationFilter.class, order.next());
        put(RequestCacheAwareFilter.class, order.next());
        put(SecurityContextHolderAwareRequestFilter.class, order.next());
        put(JaasApiIntegrationFilter.class, order.next());
        put(RememberMeAuthenticationFilter.class, order.next());
        put(AnonymousAuthenticationFilter.class, order.next());
        filterToOrder.put(
            "org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
                order.next());
        put(SessionManagementFilter.class, order.next());
        put(ExceptionTranslationFilter.class, order.next());
        put(FilterSecurityInterceptor.class, order.next());
        put(SwitchUserFilter.class, order.next());
    }
  • Spring Security的FilterChainProxy在默认情况下的order为OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100,这是一个很靠前的排序了。可以设置该Filter的顺序号,比如:
security.filter-order=5

对于其他自定义的Bean,可以通过FilterRegistrationBean.setOrder()进行排序,或者通过@Order

@Order(Ordered.LOWEST_PRECEDENCE -1)
@Component
public class ABCFilter implements Filter {
  ------
} 

相关文章

网友评论

    本文标题:Spring Security配置学习笔记

    本文链接:https://www.haomeiwen.com/subject/frcflqtx.html