作用
我们获取当前登录用户信息是根据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
任务执行完成
初始化处
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; } }