Spring Security Filter详解

0. 概述

经过基于注解的Spring Security原理解析分析,Spring Security本身所做的事情就是在Spring容器中注册了一系列的Filter,这些Filters在检测到满足条件的URL请求时,会执行其定义的处理过程; Security本身默认提供了一些Filter来完成其各种功能; 本文主要分析以下问题:

  1. 默认Filter的作用及配置
  2. 默认Filter的配置及生效示例分析

由于水平受限,如有分析不正确处敬指出。

1. 默认Filter分析

1.1 概述

Security默认的Filter入口在HttpSecurity对象中;关于该对象的加载过程,请参考基于注解的Spring Security原理解析;

在HttpSecurity对象中,实际提供的是各默认Filter的配置类,通过配置类来控制对应Filter的各个属性配置;在配置完成将Filter加载到HttpSecurity中的FilterChain中去。

在HttpSecurity中提供了以下默认Filter及其配置类: 

ConfigurerFilter功能说明
OpenIDLoginConfigurerOpenIDAuthenticationFilter处理OpenID授权请求
HeaderWriterFilterHeadersConfigurer在返回报文头中添加Security相关信息
CorsConfigurerCorsFilter提供跨域访问配置支持的Filter
SessionManagementConfigurerSessionManagementFilter会话管理Filter
PortMapperConfigurer用于在Http及Https请求之间重定向时的端口判定
JeeConfigurerJ2eePreAuthenticatedProcessingFilter添加J2EE预授权处理机制支持
X509ConfigurerX509AuthenticationFilter添加X509预授权处理机制支持
RememberMeConfigurerRememberMeAuthenticationFilter记住用户名及密码功能支持
ExpressionUrlAuthorizationConfigurerFilterSecurityInterceptorSecurity的主要Filter,通过调用权限管理器等进行Http访问的权限判断
RequestCacheConfigurerRequestCacheAwareFilter缓存请求并在必要的时候使用缓存的请求
ExceptionHandlingConfigurerExceptionTranslationFilter处理AccessDeniedException及AuthenticationException异常
SecurityContextConfigurerSecurityContextPersistenceFilterSecurityContext对象持久化Filter,用于在请求开始阶段初始化并持久化该对象,在后续的Filter中可以使用该对象来获取信息
ServletApiConfigurerSecurityContextHolderAwareRequestFilter在原始请求基础上包装一些方法供后续调用
CsrfConfigurerCsrfFilter跨站请求伪造保护Filter;
LogoutConfigurerLogoutFilter退出登录请求处理Filter
AnonymousConfigurerAnonymousAuthenticationFilter匿名请求控制Filter
FormLoginConfigurerUsernamePasswordAuthenticationFilter表单登录请求处理Filter
OAuth2LoginConfigurerOAuth2AuthorizationRequestRedirectFilterOAuth2请求权限控制处理Filter,为其它网站提供本网站Oauth2方式登录,即其它网站通过本网站的账户密码进行登录授权
ChannelSecurityConfigurerChannelProcessingFilter通道选择Filter,确保请求是通过正确的通道过来的,如Http或者Https
HttpBasicConfigurerBasicAuthenticationFilterSecurity基础登录授权Filter,将其结果保存在SecurityContextHolder中

默认的Filter并不是在HttpSecurity对象初始化的时候就全部加载,而是根据用户定制情况进行加载,具体加载情况见后文;

1.2 默认Filter默认配置

在WebSecurityConfigurerAdapter类中,存在默认的configure方法,它会提供一些默认的权限控制配置,默认方法实现哪下:

    protected void configure(HttpSecurity http) throws Exception {
        logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin().and()
            .httpBasic();
    }

同时,在初始化HttpSecurity的方法init中,也会提供一些默认的配置:

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

通过分析这两个配置处,Security默认加载的Filter清单如下: 

ConfigurerFilter
CsrfConfigurerCsrfFilter
WebAsyncManagerIntegrationFilter
ExceptionHandlingConfigurerExceptionTranslationFilter
HeadersConfigurerHeaderWriterFilter
SessionManagementConfigurerSessionManagementFilter
SecurityContextConfigurerSecurityContextPersistenceFilter
RequestCacheConfigurerRequestCacheAwareFilter
AnonymousConfigurerAnonymousAuthenticationFilter
ServletApiConfigurerSecurityContextHolderAwareRequestFilter
DefaultLoginPageConfigurerDefaultLoginPageGeneratingFilter
LogoutConfigurerLogoutFilter
FormLoginConfigurerUsernamePasswordAuthenticationFilter
HttpBasicConfigurerBasicAuthenticationFilter

1.3 默认Filter用户自定义加载

当开发的时候使用@EnableWebSecurity注解加载Security的配置类时,如果该类继承了WebSecurityConfigurerAdapter类,则可以覆盖其configure方法来配置权限控制参数:

@EnableWebSecurity(debug = true)
public class SercurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private TestAuthenticationProvider authenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/*", "/home", "/manager/dbManager.html", "/js/*", "/db/*", "/js/**/*").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/")
                .permitAll()
                .and()
                .sessionManagement().invalidSessionUrl("/timeout");

        http.headers().frameOptions().sameOrigin().httpStrictTransportSecurity().disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
    }

    @Bean
    public UserDetailsService getUserDetailservice() {
        return new TestUserDetailService();
    }
}

此时即可配置登录链接、退出登录链接、各个URL的访问权限配置等。

1.4 Filter执行顺序分析

Security在初始化的时候会初始化一系列的Filter,这些Filter之间实际上是有先后关系的,其先后关系是如何控制的?
通过前面的分析,Security的Filters是加载到HttpSecurity对象的Filters属性中去的,在HttpSecurity对象Build的时候生成FilterChain对象,此时会将所有添加的Filters添加到FilterChain对象中并返回。其PerformBuild方法实现如下: 

    @Override
    protected DefaultSecurityFilterChain performBuild() throws Exception {
        Collections.sort(filters, comparator);
        return new DefaultSecurityFilterChain(requestMatcher, filters);
    }

可以看到在PerformBuild方法中,第一步是对所有的Fiilters进行排序;其排序规则使用HttpSecurity的属性comparator;我们看下comparator的初始化:

private FilterComparator comparator = new FilterComparator();

再分析下该Comparator的实现:

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

很明显执行顺序是在这个地方控制的。

2. 默认Filter配置分析举例

2.1 FormLoginConfigurer

2.1.1 概述

表单登录Filter配置类;用于基于表单的权限控制配置;所有配置项均存在默认值,因此所有参数均可使用默认值; 需要特别说明的是,如果没有指定loginPage配置项,Security将会提供一个默认的登录页面。 该配置项配置的实际上是UsernamePasswordAuthenticationFilter这个Filter。

2.1.2 配置详解

如要配置FormLoginConfigurer,在继承自WebSecurityConfigurerAdapter的类的Configure方法中进行:

http.formLogin()
    .loginPage("/login")
    .permitAll();

该配置类所包含的配置项清单及其作用见下表:

配置项配置项说明
loginPage登录页面,如果用户未指定,Security将提供默认的登录页面; 默认登录页面请求链接:/login
usernameParameter用户名属性的名称;默认是username
passwordParameter密码属性名称,默认是password
failureForwardUrl授权失败时的跳转链接
failureUrl登录失败时的跳转链接,默认是/login?error
failureHandler登录失败后的处理器
successForwardUrl授权成功时的跳转链接
successHandler登录成功时的处理器
defaultSuccessUrl指定如果用户登录前未访问需要授权访问的页面,登录成功后的跳转链接
loginProcessingUrl登录请求处理链接
authenticationDetailsSource保存登录请求地址及其SessionID的对象

2.1.3 Filter配置过程分析

上方已经提到过,Configurer是用来配置Filter的各个属性的;实际上最终添加到HttpSecurity对象的Filters属性中去的还是各个Filter;那Configurer与Filter分别是如何发挥作用的?
在此我们将以FormLoginConfigurer为例来分析其生效过程。

先来看下配置加载过程:
FilterConfigurer加载过程

其中HttpSecurity继承自AbstractConfiguredSecurityBuilder类,其配置入口是其方法formLogin;实际最终调用的是其父类的add方法,将FormLoginConfigurer对象添加进去。
add方法源码如下:

private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
        Assert.notNull(configurer, "configurer cannot be null");

        Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
                .getClass();
        synchronized (configurers) {
            if (buildState.isConfigured()) {
                throw new IllegalStateException("Cannot apply " + configurer
                        + " to already built object");
            }
            List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
                    .get(clazz) : null;
            if (configs == null) {
                configs = new ArrayList<SecurityConfigurer<O, B>>(1);
            }
            configs.add(configurer);
            this.configurers.put(clazz, configs);
            if (buildState.isInitializing()) {
                this.configurersAddedInInitializing.add(configurer);
            }
        }
    }

该段代码实际所做的事情就是: 将FormLoginConfigurer对象添加到HttpSecurity的configurers属性中去;

基于注解的Spring Security原理解析一文中已经说明,HttpSecurity最终会被加载到WebSecurity的securityFilterChainBuilders属性中去; 添加进去后,在WebSecurity对象的build方法中,会调用HttpSecurity的build方法生成FilterChain对象。
HttpSecurity对象的其Build方法实现如下(AbstractConfiguredSecurityBuilder父类中实现):

@Override
    protected final O doBuild() throws Exception {
        synchronized (configurers) {
            buildState = BuildState.INITIALIZING;

            beforeInit();
            init();

            buildState = BuildState.CONFIGURING;

            beforeConfigure();
            configure();

            buildState = BuildState.BUILDING;

            O result = performBuild();

            buildState = BuildState.BUILT;

            return result;
        }
    }

可以看到在performBuild前会调用configure方法; 跟踪HttpSecurity的该方法,其实现如下: 

    private void configure() throws Exception {
        Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

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

可以看到实际调用的是每个FilterConfigurer的configure方法,此处也就是FormLoginConfigurer的configure方法(实现在其父类中):

@Override
    public void configure(B http) throws Exception {
        PortMapper portMapper = http.getSharedObject(PortMapper.class);
        if (portMapper != null) {
            authenticationEntryPoint.setPortMapper(portMapper);
        }

        authFilter.setAuthenticationManager(http
                .getSharedObject(AuthenticationManager.class));
        authFilter.setAuthenticationSuccessHandler(successHandler);
        authFilter.setAuthenticationFailureHandler(failureHandler);
        if (authenticationDetailsSource != null) {
            authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
        }
        SessionAuthenticationStrategy sessionAuthenticationStrategy = http
                .getSharedObject(SessionAuthenticationStrategy.class);
        if (sessionAuthenticationStrategy != null) {
            authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
        }
        RememberMeServices rememberMeServices = http
                .getSharedObject(RememberMeServices.class);
        if (rememberMeServices != null) {
            authFilter.setRememberMeServices(rememberMeServices);
        }
        F filter = postProcess(authFilter);
        http.addFilter(filter);
    }

该方法是重点了,可以看到执行过程实际就是根据Configurer对象中的属性更新authFilter对象的属性,最后调用HttpSecurity的addFilter方法将生成的Filter添加到了HttpSecurity对象中去。authFilter对象实现类型为: DefaultLoginPageGeneratingFilter。

在Configure方法完成后,HttpSecurity的build方法最终调用的是其performBuild方法,该方法实现如下: 

    @Override
    protected DefaultSecurityFilterChain performBuild() throws Exception {
        Collections.sort(filters, comparator);
        return new DefaultSecurityFilterChain(requestMatcher, filters);
    }

实际上就是组装了一个包含已配置的Filter的一个FilterChain对象。此时
至此,Filter的配置加载过程已经分析完成。

2.1.4 Filter执行过程分析

经过2.1.3中分析,FormLoginConfigurer最终是向HttpSecurity中添加了DefaultLoginPageGeneratingFilter这样一个Filter。其doFilter方法如下:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        boolean loginError = isErrorPage(request);
        boolean logoutSuccess = isLogoutSuccess(request);
        if (isLoginUrlRequest(request) || loginError || logoutSuccess) {
            String loginPageHtml = generateLoginPageHtml(request, loginError,
                    logoutSuccess);
            response.setContentType("text/html;charset=UTF-8");
            response.setContentLength(loginPageHtml.length());
            response.getWriter().write(loginPageHtml);

            return;
        }

        chain.doFilter(request, response);
    }

实际也就是将判断是否是登录、登录失败、退出登录请求,如果是的话则跳转到登录页面;此时将不会再执行后续的Filters了。如果不是上述请求,则继续执行FilterChain中的其它Filters。

参考资料:

  1. http://www.mossle.com/docs/auth/html/ch101-filters.html
  • 13
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Security是一个功能强大且灵活的身份验证和访问控制框架,用于保护基于Java的应用程序。它提供了一套全面的安全解决方案,可用于保护Web应用程序、RESTful API、方法级别的安全等。 Spring Security的核心原则是基于拦截器链(Filter Chain)的安全性,它通过一系列的过滤器(Filters)来处理不同的安全问题。这些过滤器可以在认证(Authentication)和授权(Authorization)过程中执行各种任务。 在Spring Security中,认证是指验证用户的身份,通常通过用户名和密码进行验证。授权是指根据用户的身份和角色来确定其是否有权访问特定资源或执行特定操作。 Spring Security提供了许多功能和扩展点,可以轻松地自定义和扩展以满足特定的需求。以下是一些Spring Security的主要功能: 1. 身份验证(Authentication):Spring Security支持多种身份验证方式,如基于数据库、LDAP、OAuth等。它还提供了记住我(Remember Me)和匿名访问等功能。 2. 授权(Authorization):Spring Security支持基于角色和权限的授权机制。可以配置细粒度的访问控制规则,以确保只有具有合适权限的用户可以访问受保护的资源。 3. 安全性注解(Security Annotations):Spring Security提供了一套注解,可以在方法级别上标记安全性要求。这些注解可以用于限制对特定方法的访问,并进行细粒度的授权控制。 4. CSRF保护(CSRF Protection):Spring Security提供了一种防止跨站请求伪造(CSRF)攻击的机制。它通过生成和验证CSRF令牌来确保只有合法的请求才能被处理。 5. Session管理(Session Management):Spring Security提供了对会话管理的支持,包括会话过期、并发控制和无效会话处理等功能。 6. 安全事件与日志(Security Events and Logging):Spring Security可以生成安全相关的事件,并提供了灵活的日志配置选项,以便记录和监视应用程序的安全状态。 以上只是Spring Security的一些主要功能,它还有很多其他特性和扩展点可用于满足各种安全需求。要详细了解Spring Security的使用和配置,可以参考官方文档或其他相关资源。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值