欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring-security(二十)核心Filter-SecurityContextPersistenceFilter 博客分类: spring security springsecurity 

程序员文章站 2024-03-13 18:49:21
...
一、SecurityContextPersistenceFilter功能和属性
1.在前面介绍spring filter顺序时,我们简单介绍过SecurityContextPersistenceFilter,他主要有两个作用。
  • 请求开始时从对应的SecurityContextRepository获取securityContext存入SecurityContextHolder中
  • 请求结束时清除SecurityContextHolder中的securityContext,将本次请求执行后新的SecurityContext存入到对应的SecurityContextRepository中

其中在请求结束后清除SecurityContextHolder中的SecurityContext的操作是必须的,因为默认情况下SecurityContextHolder会把SecurityContext存储到ThreadLocal中,而这个thread刚好是存在于servlet容器的线程池中的,如果不清除,当后续请求又从线程池中分到这个线程时,程序就会拿到错误的认证信息。
2.SecurityContext存储类SecurityContextRepository
从Spring Security 3.0开始,存储和加载securityContext都是通过SecurityContextRepository来完成的
public interface SecurityContextRepository {
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);

void saveContext(SecurityContext context, HttpServletRequest request,
                 HttpServletResponse response);
boolean containsContext(HttpServletRequest request);
}

其中参数HttpRequestResponseHolder仅仅是一个request和response的封装类,从而允许这个接口的具体实现类对request和response进行包装,返回的对象会传入到后续的filter中。
默认情况下,spring boot用的具体实现类是HttpSessionSecurityContextRepository,把SecurityContext存入到HttpSession的属性中,这个类最主要的配置属性是allowSessionCreation默认是true,这样当需要为认证用户存储security context而对应的session还不存在时,这个类就会为当前用户创建session对象。
在spring security中这个接口还有另一个实现-NullSecurityContextRepository,这个类不会存储当前的SecurityContext,即便用户已经认证成功。
二、在spring boot环境下,采用Java config机制这个filter是如何追加到servlet中对我们的请求进行拦截的呢。
在前面的章节(spring-security(十六)Filter配置原理)中,我们知道spring 安全相关的Filter是在WebSecurity的build方法中调用HttpSecurity的build来将追加到HttpSecurity中filter列表排好序后构建成SecurityFilterChain,再把所有的SecurityFilterChain追加到FilterChainProxy中,最后通过DelegatingFilterProxy注册到ServletContext中的,下面我们主要来看下这个类是如何追加到HttpSecuriy的filter列表中的。
1. 从我们的配置入口WebSecurityConfigurerAdapter类开始,在这个类的getHttp()方法中,采用默认配置时会调用http.securityContext()方法
public SecurityContextConfigurer<HttpSecurity> securityContext() throws Exception {
    return getOrApply(new SecurityContextConfigurer<HttpSecurity>());
}

在这个方法中创建了一个实现了SecurityConfigurer接口的配置类SecurityContextConfigurer,通过调用getOrApply方法最终追加到HttpSecurity的configurers属性中,通过这个配置类我们也可以设置SecurityContextPersistenceFilter中的SecurityContextRepository属性。
2. WebSecurity在构建HttpSecurity时,会调用HttpSecurity的build方法,这个方法会先执行HttpSecurity的configure()方法,就是依次调用configurers属性中各个SecurityConfigurer的configure方法
private void configure() throws Exception {
  Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
  for (SecurityConfigurer<O, B> configurer : configurers) {
     configurer.configure((B) this);
  }
}

下面来看下SecurityContextConfigurer的configure方法
public void configure(H http) throws Exception {
  SecurityContextRepository securityContextRepository = http
     .getSharedObject(SecurityContextRepository.class);
  if(securityContextRepository == null) {
	 securityContextRepository = new HttpSessionSecurityContextRepository();
	}
  SecurityContextPersistenceFilter securityContextFilter = new 
  SecurityContextPersistenceFilter(securityContextRepository);
  SessionManagementConfigurer<?> sessionManagement = http
	 .getConfigurer(SessionManagementConfigurer.class);
  SessionCreationPolicy sessionCreationPolicy = sessionManagement == null ? null: sessionManagement.getSessionCreationPolicy();
	if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
		securityContextFilter.setForceEagerSessionCreation(true);
	}
	securityContextFilter = postProcess(securityContextFilter);
	http.addFilter(securityContextFilter);
}

很明显,在这个方法里面会创建一个SecurityContextPersistenceFilter,设置对应的SecurityContextRepository属性,并追加到了HttpSecurity的filter列表中,这样就可以和其他的过滤器一样对我们的请求进行过滤了。

可以看到这个过滤器功能并不复杂,实际上spring security通过大量的过滤器将功能进行明确的拆分,即方便了扩展,也大大提升了代码的可读性。
相关标签: spring security