You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 11:17:33