1、作用

  • • 是为了接口返回异步对象,然后执行异步任务也能通过SecurityContextHolder获取SecurityContext
  • • 比如说返回值是WebAsyncTask的时候

2、WebAsyncManagerIntegrationFilter

  • • 源码很短就是在WebAsyncManager中注册了SecurityContextCallableProcessingInterceptor
public final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter {

   private static final Object CALLABLE_INTERCEPTOR_KEY = new Object();

   @Override
   protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
         throws ServletException, IOException {
      //获得Web异步管理器
      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager
            .getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
      if (securityProcessingInterceptor == null) {
         //重点就是注册了一个这个拦截器
         asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY,
               new SecurityContextCallableProcessingInterceptor());
      }
      filterChain.doFilter(request, response);
   }

}
  • • WebAsyncManagerIntegrationFilter没有对应的配置类,是在获取HttpSecurity的时候,默认注册的拦截器

3、AsyncTaskMethodReturnValueHandler

  • • 当接口返回值是WebAsyncTask的时候,SpringMVC会适配AsyncTaskMethodReturnValueHandler作为返回值处理器
  • • 可以看出就是调用了WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(webAsyncTask, mavContainer);来处理
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

   if (returnValue == null) {
      mavContainer.setRequestHandled(true);
      return;
   }

   WebAsyncTask<?> webAsyncTask = (WebAsyncTask<?>) returnValue;
   if (this.beanFactory != null) {
      webAsyncTask.setBeanFactory(this.beanFactory);
   }
   WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(webAsyncTask, mavContainer);
}
  • • 而startCallableProcessing方法中就会提取在WebAsyncManager中注册的拦截器,组成拦截器链然后在异步任务的前后执行对应的回调方法
  • • 而在WebAsyncManagerIntegrationFilter中注册的SecurityContextCallableProcessingInterceptor就会在这里面执行

4、SecurityContextCallableProcessingInterceptor

public final class SecurityContextCallableProcessingInterceptor extends CallableProcessingInterceptorAdapter {

   private volatile SecurityContext securityContext;

   public SecurityContextCallableProcessingInterceptor() {
   }

   public SecurityContextCallableProcessingInterceptor(SecurityContext securityContext) {
      Assert.notNull(securityContext, "securityContext cannot be null");
      setSecurityContext(securityContext);
   }

   /**
    * 在执行异步任务之前执行,也就是还是用户线程的时候执行,是为了将安全上下文保存起来
    * @param request
    * @param task
    * @param <T>
    */
   @Override
   public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) {
      if (this.securityContext == null) {
         setSecurityContext(SecurityContextHolder.getContext());
      }
   }

   /**
    * 在已经执行异步任务(submit)但是还没有执行Callable.call()方法,是为了将安全上下文保存到线程级别的安全上下文策略中
    * @param request
    * @param task
    * @param <T>
    */
   @Override
   public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
      SecurityContextHolder.setContext(this.securityContext);
   }

   /**
    * 异步任务已经执行完毕,是为了清空安全上下文
    * @param request
    * @param task
    * @param concurrentResult
    * @param <T>
    */
   @Override
   public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
      SecurityContextHolder.clearContext();
   }

   private void setSecurityContext(SecurityContext securityContext) {
      this.securityContext = securityContext;
   }

}
  • • 其原理很简单,当还在Tomcat线程的时候,将SecurityContext保存起来,然后进入异步线程后又取出来就行了
  • • 而SecurityContext的存储策略有四种

[SpringSecurity5.2.2源码分析七]:WebAsyncManagerIntegrationFilter_SpringSecurity

image.png

  • • 默认是ThreadLocalSecurityContextHolderStrategy,也就是将SecurityContext保存在Thread的threadLocals中