spring-security源码-Filter之WebAsyncManagerIntegrationFilter(十)

作用

我们获取当前登录用户信息是根据SecurityContextHolder.getContext()获取的,SecurityContextHolder.getContext()本质是ThreadLocal实现

Spring MVC WebAsyncTask是异步另外一个线程 所以用于保证我们在Task 线程也能通过SecurityContextHolder.getContext()获取

WebAsyncTask简单例子

GetMapping("/completion")
public WebAsyncTask<String> asyncTaskCompletion() {
    // 打印处理线程名
    out.println(format("请求处理线程:%s", currentThread().getName()));

    // 模拟开启一个异步任务,超时时间为10s
    WebAsyncTask<String> asyncTask = new WebAsyncTask<>(10 * 1000L, () -> {
        out.println(format("异步工作线程:%s", currentThread().getName()));
        // 任务处理时间5s,不超时
        sleep(5 * 1000L);
        return asyncService.generateUUID();
    });

    // 任务执行完成时调用该方法
    asyncTask.onCompletion(() -> out.println("任务执行完成"));
    out.println("继续处理其他事情");
    return asyncTask;
}

输出

请求处理线程:http-nio-8080-exec-2
继续处理其他事情
异步工作线程:MvcAsync1
任务执行完成

初始化处

参考《spring-security源码-初始化(九)》

private void applyDefaultConfiguration(HttpSecurity http) throws Exception {
        //http本质也是build 这里都是配置默认的config configure add CsrfConfigurer
        http.csrf();
        //默认增加一个WebAsyncManagerIntegrationFilter
        http.addFilter(new WebAsyncManagerIntegrationFilter());
        //configures add ExceptionHandlingConfigurer
        http.exceptionHandling();
        //configures add HeadersConfigurer
        http.headers();
        //configures add SessionManagementConfigurer
        http.sessionManagement();
        //configure add SecurityContextConfigurer
        http.securityContext();
        //configure add RequestCacheConfigurer
        http.requestCache();
        ///configure add AnonymousConfigurer
        http.anonymous();
        ///configure add ServletApiConfigurer
        http.servletApi();
        //configure DefaultLoginPageConfigurer
        http.apply(new DefaultLoginPageConfigurer<>());
        //configure LogoutConfigurer
        http.logout();
    }

WebAsyncManagerIntegrationFilter

public final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter {
    private static final Object CALLABLE_INTERCEPTOR_KEY = new Object();

    public WebAsyncManagerIntegrationFilter() {
    }

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取WebAsync 异步管理器
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        //如果没有初始化SecurityContextCallableProcessingInterceptor
        SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor)asyncManager.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
        if (securityProcessingInterceptor == null) {
            //则手动创建Spring Security的SecurityContextCallableProcessingInterceptor
            //<1>核心原理应该是在开启异步的时候通过拦截器 设置当前Context
            asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, new SecurityContextCallableProcessingInterceptor());
        }

        filterChain.doFilter(request, response);
    }
}

<1>

public final class SecurityContextCallableProcessingInterceptor extends CallableProcessingInterceptorAdapter {
    private volatile SecurityContext securityContext;

    public SecurityContextCallableProcessingInterceptor() {
    }

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

    public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) {
        if (this.securityContext == null) {
            // before 开启Task之前设置SecurityContext 
            this.setSecurityContext(SecurityContextHolder.getContext());
        }

    }

    public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
        //负责Task线程写入
        SecurityContextHolder.setContext(this.securityContext);
    }

    public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
        //负责Task线程清空
        SecurityContextHolder.clearContext();
    }

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

 

posted @ 2021-11-09 14:15  意犹未尽  阅读(315)  评论(0编辑  收藏  举报