SecurityFilterChain重复过滤致认证丢失问题求助
问题:SecurityFilterChain执行两次过滤且第二次丢失认证信息
开启异步功能前系统正常,当前问题表现为:
- SecurityFilterChain对同一请求执行两次过滤(预期仅一次)
- 第二次过滤时认证信息丢失,触发AuthenticationEntryPoint
相关代码片段
异步线程池配置
@Bean("AsyncTask") @Primary public Executor threadPoolTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(100); executor.setMaxPoolSize(100); executor.setQueueCapacity(500); executor.setThreadNamePrefix("template-thread-"); executor.initialize(); return new DelegatingSecurityContextAsyncTaskExecutor(executor); }
Controller方法
@GetMapping("/get_test") public CompletableFuture<String> getTest() throws InterruptedException { System.out.println("Entered in controller"); long i = Thread.currentThread().getId(); System.out.println("Thread id: " + i); SecurityContext preContext = SecurityContextHolder.getContext(); System.out.println("Before Async - Authenticated user: " + (preContext.getAuthentication() != null ? preContext.getAuthentication().getName() : "null")); return testService.asyncMethod(); }
Service异步方法
@Async("AsyncTask") public CompletableFuture<String> asyncMethod() throws InterruptedException { System.out.println("Entered in service"); System.out.println("Thread id: " + Thread.currentThread().getId()); Authentication auth = SecurityContextHolder.getContext().getAuthentication(); System.out.println("In async method - Authenticated user: " + (auth != null ? auth.getName() : "none")); return CompletableFuture.completedFuture("Hello World"); }
Security配置类
@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig{ private final JWTAuthenticationFilter jwtAuthenticationFilter; private final PermissionMapping permissionMapping; private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; private final CustomAccessDeniedHandler customAccessDeniedHandler; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{ httpSecurity .csrf(AbstractHttpConfigurer::disable) .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .authorizeHttpRequests(auth -> auth .anyRequest().access((authenticationSupplier, context) -> { System.out.println("Entered in security filter"); Authentication authentication = authenticationSupplier.get(); System.out.println("Thread id: " + Thread.currentThread().getId()); System.out.println("Security - Authenticated user: " + authentication.getName()); String currentUri = context.getRequest().getRequestURI(); System.out.println("Security - Current URI: " + currentUri); List<String> requiredPermissions = permissionMapping.getPermissions(currentUri); if (requiredPermissions != null) { boolean hasPermission = authentication.getAuthorities().stream() .anyMatch(grantedAuthority -> requiredPermissions.contains(grantedAuthority.getAuthority())); return new AuthorizationDecision(hasPermission); } return new AuthorizationDecision(true); }) ) .exceptionHandling(exceptionHandling -> exceptionHandling .authenticationEntryPoint(customAuthenticationEntryPoint) // Handle unauthenticated access .accessDeniedHandler(customAccessDeniedHandler) // Handle unauthorized access ); return httpSecurity.build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
JWT认证过滤器
@Component @RequiredArgsConstructor public class JWTAuthenticationFilter extends OncePerRequestFilter { private final JWTUtils jwtUtils; private final CustomUserService customUserService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { System.out.println("Entered in JWT Filter"); System.out.println("Thread id: " + Thread.currentThread().getId()); Cookie jwtCookie = WebUtils.getCookie(request, "jwt"); String jwt = jwtCookie != null ? jwtCookie.getValue() : null; if(jwt == null){ request.setAttribute("jwtStatus", "invalid"); // This attribute will be used in another filter filterChain.doFilter(request, response); return; } try { String username = jwtUtils.getUsernameFromJWT(jwt); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = customUserService.loadUserByUsername(username); if (jwtUtils.isTokenValid(jwt, userDetails.getUsername())) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); request.setAttribute("jwtStatus", "valid"); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } else { jwtCookie.setMaxAge(0); request.setAttribute("jwtStatus", "invalid"); response.addCookie(jwtCookie); } } else { jwtCookie.setMaxAge(0); request.setAttribute("jwtStatus", "invalid"); response.addCookie(jwtCookie); } } catch (Exception e){ jwtCookie.setMaxAge(0); request.setAttribute("jwtStatus", "invalid"); response.addCookie(jwtCookie); } filterChain.doFilter(request, response); } }
控制台调试输出
Entered in JWT Filter Thread id: 42 Entered in security filter Thread id: 42 Security - Authenticated user: dev Security - Current URI: /get_test Entered in controller Thread id: 42 Before Async - Authenticated user: dev Entered in service Thread id: 66 In async method - Authenticated user: dev Entered in security filter Thread id: 42 Security - Authenticated user: anonymousUser Security - Current URI: /get_test
已尝试方案
修改SecurityContextHolder的策略为MODE_INHERITABLETHREADLOCAL,但问题仍未解决。
内容的提问来源于stack exchange,提问作者Claudiu Claudiu
相关产品推荐
相关产品推荐

