Spring Security Filter详解
来源:互联网 发布:淘宝怎么不能批量发货 编辑:程序博客网 时间:2024/06/05 18:52
0. 概述
经过基于注解的Spring Security原理解析分析,Spring Security本身所做的事情就是在Spring容器中注册了一系列的Filter,这些Filters在检测到满足条件的URL请求时,会执行其定义的处理过程; Security本身默认提供了一些Filter来完成其各种功能; 本文主要分析以下问题:
- 默认Filter的作用及配置
- 默认Filter的配置及生效示例分析
由于水平受限,如有分析不正确处敬指出。
1. 默认Filter分析
1.1 概述
Security默认的Filter入口在HttpSecurity对象中;关于该对象的加载过程,请参考基于注解的Spring Security原理解析;
在HttpSecurity对象中,实际提供的是各默认Filter的配置类,通过配置类来控制对应Filter的各个属性配置;在配置完成将Filter加载到HttpSecurity中的FilterChain中去。
在HttpSecurity中提供了以下默认Filter及其配置类:
默认的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清单如下:
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();
该配置类所包含的配置项清单及其作用见下表:
2.1.3 Filter配置过程分析
上方已经提到过,Configurer是用来配置Filter的各个属性的;实际上最终添加到HttpSecurity对象的Filters属性中去的还是各个Filter;那Configurer与Filter分别是如何发挥作用的?
在此我们将以FormLoginConfigurer为例来分析其生效过程。
先来看下配置加载过程:
其中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。
参考资料:
- http://www.mossle.com/docs/auth/html/ch101-filters.html
- Spring Security Filter详解
- 9. Spring Security Filter
- Spring Security Filter
- spring security filter源码分析
- spring security (五)Filter
- spring security添加自定义filter
- spring security 自定义filter参考资料
- Spring Security学习记录(二) -- Spring Security的Filter
- spring security详解教程
- Spring Security详解
- spring security详解
- Spring Security详解
- spring security详解
- spring security详解
- spring-security入门详解
- Spring Security的filter配置及说明
- Spring Security(09)——Filter
- Spring Security(09)——Filter
- 马云:网上90%的评论都没脑子 现在有网上病夫
- eclipse使用ModelGoon插件自动生成java类的UML图
- windows安装svn服务端
- Oracle9i 安装步骤
- 这个 Bug 怎么解?
- Spring Security Filter详解
- 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO
- RecyclerView 的 divier (可以控制是否显示边界)
- Android缓存机制&一个缓存框架推荐
- 如何从 Windows 虚拟机分离数据磁盘
- springmvc上传文件
- 算法分析的正确姿势
- 《计算机科学导论(第2版)》读书笔记(0)目录(关键词:计算机科学)
- centos系统中yum安装与卸载软件常见命令