`

spring-security(十六)Filter配置原理

阅读更多
前言:
  spring security最常见的应用场景还是基于http请求和servlet API的web应用,接下来的几个章节我们将重点探讨下spring security是如何在web应用中实现认证和访问控制的。通过前面章节的讲述,我们已经了解到,在web应用中spring是通过各种各样的filter来做认证和鉴权,本小节就主要讨论下spring中过滤器的几个主要组件。
1.DelegatingFilterProxy
为了使Filter起作用,我们必须明确的在web.xml中声明,或者通过servlet3.0+中追加的方法ServletContext.addFilter方法追加到servlet中,否则这个filter是会被servlet容器忽略的。在spring security中,我们的filter也是一个spring bean,也需要用到依赖注入功能来装配,而我们知道,正常情况下servlet context的创建是在application context之前的,如果我们直接把我们的filter注册到servlet中,filter中依赖注入的对象将获取不到,为了解决这个问题,spring引入了DelegatingFilterProxy类,他提供了一servlet context和application context之间的一种链接。
在web.xml中使用DelegatingFilterProxy,通常采用下面的配置代码段
<filter>
 <filter-name>myFilter</filter-name> 
 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>myFilter</filter-name> 
 <url-pattern>/*</url-pattern>
</filter-mapping>


相应的java config代码段如下:
Filter filter = new DelegatingFilterProxy("myFilter");
servletContext.addFilter("myFilter", filter)
                        .addMappingForUrlPatterns(
                         EnumSet.of(DispatcherType.FORWARD, 
                                              DispatcherType.INCLUDE,
                                              DispatcherType.REQUEST), false, "/*");

可以看到,我们实际上配置的Filter是DelegatingFilterProxy,并不是我们实际实现了Filter接口的类,当有请求满足DelegatingFilterProxy的UriPatterns时,DelegatingFilterProxy类会首先判断实际的Filter类是否已经配置过,如果没有将根据配置的bean名称从spring context中获取对应的bean,然后将处理委托给我们真实的Filter类。这样我们真实的Filter类就可以利用spring bean生命周期方法以及灵活的依赖注入配置,需要注意的是我们的Filter类注入到spring context中的bean名称必须和传入到DelegatingFilterProxy中的名字一致。DelegatingFilterProxy中处理请求的代码段如下:
@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// Lazily initialize the delegate if necessary.
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			synchronized (this.delegateMonitor) {
				if (this.delegate == null) {
				WebApplicationContext wac = findWebApplicationContext();
				if (wac == null) {
				throw new IllegalStateException("No WebApplicationContext found: " +
					"no ContextLoaderListener or DispatcherServlet registered?");
					}
					this.delegate = initDelegate(wac);
				}
				delegateToUse = this.delegate;
			}
		}

		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}

......

protected void invokeDelegate(
			Filter delegate, ServletRequest request, ServletResponse response,FilterChain filterChain)
			throws ServletException, IOException {

		delegate.doFilter(request, response, filterChain);
	}


2.FilterChainProxy
在spring security我们通过DelegatingFilterProxy注册到servlet context中并不是一个个单独的filter,而是FilterChainProxy,一个FilterChainProxy就是一系列过滤器链的代理,里面包含很多个的过滤器链(过滤器链在spring中的统一接口是SecurityFilterChain,每一个SecurityFilterChain中都包含一个排好序的过滤器列表),当FilterChainProxy接受到DelegatingFilterProxy委托过来的请求时,就从这些过滤器链中查找到第一个满足当前请求的过滤器链,并获取这个过滤器链中的过滤器列表,然后依次执行获取到的过滤器列表。虽然理论上我们是能够通过DelegatingFilterProxy将我们需要的filter一个个都配置到web.xml或者通过servletContext.addFilter方法追加到servlet中,但是随着我们filter的追加这种方式会变的难以维护,并且这样一来filter的执行顺序将严格依赖我们追加的顺序,缺少灵活性。下面是一个例子
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <constructor-arg>
<list>
<sec:filter-chain pattern="/restful/**" filters="
  securityContextPersistenceFilterWithASCFalse,
  basicAuthenticationFilter,
  exceptionTranslationFilter,
  filterSecurityInterceptor" />
<sec:filter-chain pattern="/**" filters="
 securityContextPersistenceFilterWithASCTrue,
 formLoginFilter, 
 exceptionTranslationFilter, 
 filterSecurityInterceptor" />
 </list>
</constructor-arg>
</bean>



在FilterChainProxy中的多个过滤器链中,我们是用第一个匹配当前请求的过滤器链对请求进行filter操作,所以越具体的URI应配置的越靠前


3.下面我们来看看在spring security中是如何把我们需要的filter注册到servlet中的
3.1首先是WebSecurity类的performBuild方法,
这个方法会把我们追加到ignoredRequests(通过ignoring()方法配置)和配置好的securityFilterChainBuilders(实际就是HttpSecurity)分别配置成SecurityFilterChain,代码片段如下:
@Override
	protected Filter performBuild() throws Exception {
		...
		int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
		List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
				chainSize);
		for (RequestMatcher ignoredRequest : ignoredRequests) {
			securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
		}
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
			securityFilterChains.add(securityFilterChainBuilder.build());
		}
		FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
...

3.2配置好的FilterChainProxy返回后会在WebSecurityConfiguration中被注册到spring context,bean名称为springSecurityFilterChain
/**
	 * Creates the Spring Security Filter Chain
	 * @return
	 * @throws Exception
	 */
	@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();
	}

如果我们有继承WebSecurityConfigurerAdapter的配置类,就会用我们自己的。
AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME的具体值就是springSecurityFilterChain
public abstract class AbstractSecurityWebApplicationInitializer
		implements WebApplicationInitializer {

...
public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
...

3.3 通过DelegatingFilterProxy把名称为springSecurityFilterChain的bean注册到servlet中,在spring boot工程中这个是通过SecurityFilterAutoConfiguration这个自动注册类来实现的
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;

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

可以看到,当我们的context中存在名称为springSecurityFilterChain的bean后,spring boot将自动为我们注册一个DelegatingFilterProxyRegistrationBean,并将springSecurityFilterChain传递进去,DelegatingFilterProxyRegistrationBean这个bean的继承关系为
DelegatingFilterProxyRegistrationBean ->AbstractFilterRegistrationBean->RegistrationBean->ServletContextInitializer,所以在servlet启动时会出发onStartup方法,在这个方法内部,具体实现在AbstractFilterRegistrationBean类中
@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		Filter filter = getFilter();
		Assert.notNull(filter, "Filter must not be null");
		String name = getOrDeduceName(filter);
		if (!isEnabled()) {
			this.logger.info("Filter " + name + " was not registered (disabled)");
			return;
		}
		FilterRegistration.Dynamic added = servletContext.addFilter(name, filter);
		if (added == null) {
			this.logger.info("Filter " + name + " was not registered "
					+ "(possibly already registered?)");
			return;
		}
		configure(added);
	}

可以看到,这里会获取一个filter并注册到servletContext中,具体的getFilter方法在DelegatingFilterProxyRegistrationBean中
@Override
	public Filter getFilter() {
		return new DelegatingFilterProxy(this.targetBeanName,
				getWebApplicationContext()) {

			@Override
			protected void initFilterBean() throws ServletException {
				// Don't initialize filter bean on init()
			}

		};
	}

很明显,是用了一个DelegatingFilterProxy将我们传入的bean名称进行了包装。至此spring security中用到的filter就被以DelegatingFilterProxy的形式注册到了servlet。

具体我们想用的filter如 UsernamePasswordAuthenticationFilter、SecurityContextPersistenceFilter等是如何追加到HttpSecurity中,从而在3.1步骤中被加入到SecurityFilterChain中的,我们在具体讨论每一个filter时再继续讲解

4.绕过filter
在实际应用开发中,对于某些资源的访问,我们可能不想使用spring security进行保护,例如一些静态的js、html等,下面分别给出用xml和java config两种配置形式实现这个功能的例子
xml配置中,我们可以将对应URI的filter设置成none,如下
<http pattern="/static/**" security="none"/>
<http pattern="/resources/**" security="none"/>

对应的java config
@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/resources/**", "/static/**");
	}

通过3.1小节,我们知道通过web.ignoring()配置的过滤规则,会被配置成DefaultSecurityFilterChain追加到过滤器链的列表的最前面,并且在配置DefaultSecurityFilterChain时只传入了RequestMatcher,没有传入任何filter,DefaultSecurityFilterChain构造函数如下,
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
		this(requestMatcher, Arrays.asList(filters));
	}

表示对于满足条件的uri不适用任何过滤器来处理,和xml中security="none"效果一样。
另外需要注意的一点是,采用这种配置后,任何满足条件的URI将不被spring security的过滤器处理,此时SecurityContext中将不包含任何用户信息。
分享到:
评论

相关推荐

    spring-security-oauth2与spring-security-web 3.1.2 源码

    Spring Security是Java领域中广泛应用的安全框架,用于保护Web应用程序免受各种安全威胁。OAuth2则是一种授权协议,常用于提供安全的第三方应用访问资源的权限。在这个源码分析中,我们将深入探讨`spring-security-...

    spring-security-3.1.4

    Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,用于Java应用程序。...虽然现在已经有更新的版本,但对于学习和理解Spring Security的基本概念和工作原理,这个版本仍然是一个很好的起点。

    spring-security-2.0.6. API和 jar包

    通过学习这个版本,我们可以了解Spring Security的基本架构和工作原理,为理解和使用更高级的版本奠定基础。同时,这个版本的API文档和JAR包对于修复旧系统中的安全问题或研究历史实现也具有参考价值。

    spring-security文档和jar包

    对于初学者来说,这是了解Spring Security工作原理和开始项目开发的好资料。 《spring-security3.0.rar》是一个包含Spring Security 3.0版本库的压缩文件,用户可以将其解压到项目的lib目录下,以便在项目中引用。...

    spring-security-3.1.3.RELEASE.jar

    在实际应用中,Spring Security还提供了过滤器链(Filter Chain)的概念,这是实现Web安全的关键。每个过滤器都有特定的职责,例如,`HttpSessionAuthenticationStrategy`处理会话相关的认证,而`...

    spring-security-3.0.5.RELEASE 官方下载

    要深入了解Spring Security 3.0.5.RELEASE,你可以查看官方文档,了解每个组件的工作原理和配置选项,同时研究压缩包中的源代码和示例,这将有助于你在实际项目中更有效地应用这一强大的安全框架。

    spring-security-3.1.0.RELEASE.zip

    Spring Security 是一个强大的和高度...虽然这个版本相对较旧,但它的基本原理和架构在后续版本中仍然适用,学习它有助于理解Spring Security的核心思想。在实际项目中,通常会使用最新版本以获得更好的安全性和功能。

    spring-security-3.1.0.RC3

    本篇文章将深入探讨Spring Security的核心特性、工作原理以及在实际项目中的应用。 一、Spring Security概述 Spring Security是Spring生态系统的组成部分,主要负责应用程序的安全性,包括身份验证、授权和访问...

    spring-security-core-2.0.6.RELEASE

    本文将深入探讨Spring Security的核心模块,并以2.0.6.RELEASE版本为基准,解析其主要组件和工作原理。 一、概述 Spring Security 2.0.6.RELEASE是该框架的一个稳定版本,它提供了强大的安全特性,包括基于角色的...

    spring-security-3.2.9和spring-framework-3.2.4的jar包和源码

    1. **过滤器链**:Spring Security通过一系列预定义的Filter来拦截HTTP请求,执行身份验证和授权检查。 2. **配置**:通常通过XML或Java配置来定制安全行为,例如设置认证提供者、定义访问规则等。 3. **认证**:...

    spring-security 案例

    Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,用于Java应用程序。它为Web应用程序和企业级...通过分析这些文件,你可以更好地理解Spring Security的工作原理,并学习如何在实际项目中应用它。

    spring-Security简单例子

    下面将详细阐述Spring Security的核心概念、配置步骤以及实现原理。 1. **核心概念** - **Authentication(认证)**: 认证是验证用户身份的过程,Spring Security提供了多种认证方式,如用户名/密码、API密钥等。 ...

    spring-security demo

    Spring Security 是一个强大的且高度可定制的身份验证和访问控制框架,用于保护Java应用程序。...通过分析和运行这个示例,你可以更深入地理解Spring Security的架构和工作原理,为实际项目开发打下坚实的基础。

    spring-security-demo.zip

    这个示例对于学习和理解Spring Security的工作原理以及如何在实际开发中应用它非常有帮助。通过分析源代码和运行示例,你可以更好地掌握Spring Security的精髓,并将其应用于自己的项目中,提升Web应用的安全性。

    spring-security.zip

    8. **Filter Chain**:Spring Security通过一系列过滤器处理请求,每个过滤器执行特定的安全任务,如认证、授权等。 9. **国际化的错误消息**:Spring Security支持多语言错误消息,方便不同地区的用户理解。 10. ...

    Spring-Security-3中文官方文档.pdf

    通过这份文档,读者可以深入理解Spring Security的工作原理,掌握如何构建安全的Spring应用程序,并了解如何根据具体需求定制和扩展框架。这份高清且带有书签的文档,使得学习过程更加高效,无疑是对Spring Security...

    spring-security3.0.5源码

    在这个源码包`spring-security-3.0.5.RELEASE`中,我们可以深入理解Spring Security的核心机制及其工作原理。 首先,Spring Security的核心组件包括以下几个部分: 1. **Authentication(认证)**:负责用户身份的...

    spring-security-web-3 source code

    《深入剖析Spring Security Web 3源码...总结,通过对`spring-security-web-3`源码的分析,开发者不仅可以掌握Spring Security的内在工作原理,还能提升解决实际安全问题的能力,为构建更安全的Web应用提供坚实的基础。

    Spring security oauth源码

    通过分析这些源码,我们可以深入理解OAuth的工作原理,以及Spring Security OAuth如何支持这些功能。同时,Sparklr2和Tonr2示例能帮助我们直观地看到OAuth流程的每个步骤,包括授权、令牌交换和资源访问。 在实际...

    spring-security demo实例

    通过这个示例,开发者可以理解Spring Security的基本工作原理,并能将其应用到自己的项目中,提供强大的安全保护。同时,这个例子也是一个很好的学习资源,帮助开发者掌握如何配置、定制和扩展Spring Security以满足...

Global site tag (gtag.js) - Google Analytics