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

Spring Boot JWT无状态会话下,如何解决请求被识别为匿名用户的401问题?

Spring Boot Security + JWT 无状态会话认证信息丢失问题排查与解决

问题背景

开发的Web应用采用Spring Boot(Security)结合JWT实现认证,用户登录流程正常,前端将JWT令牌存入Cookie,会话配置为SessionCreationPolicy.STATELESS策略。但后续携带令牌的请求返回401未授权,排查发现请求被识别为匿名用户会话。将会话策略改为IF_REQUIRED可临时解决,但会导致Spring接管会话,偏离JWT无状态设计,引发异常行为。已在JwtRequestFilter中执行setAuthentication操作,但后续流程中认证信息仍丢失。

提供的配置与代码

WebSecurityConfig 配置

http
    .headers()
        .xssProtection()
            .and()
        .contentSecurityPolicy("script-src 'self'")
            .and()
        .frameOptions().deny()
        .and()                               
    .authorizeRequests()
        .antMatchers(HttpMethod.GET, "/api/pub/v1/**").permitAll()
        .and()
    .authorizeRequests()
        .antMatchers(HttpMethod.OPTIONS, "/api/pub/v1/**").permitAll()
        .and()
    .authorizeRequests()
        .antMatchers(HttpMethod.POST, "/api/pub/v1/**").permitAll()                
        .anyRequest().authenticated()
        .and()
    .exceptionHandling()
        .authenticationEntryPoint(jwtAuthenticationEntryPoint)
        .and()
    .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

http
    .csrf().disable();

http
    .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

JwtRequestFilter 核心代码

if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
    Users user = rootService.core().users().getByEmail(email).get();
    
    List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
    grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
    
    if (jwtTokenUtil.validateToken(jwtToken, user)) {
        
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(user, null, grantedAuthorities);
        
        usernamePasswordAuthenticationToken.setDetails(
                new WebAuthenticationDetailsSource().buildDetails(request));
        
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        
        if (jwtTokenUtil.isLeadTimePassed(jwtToken)) {
            String newToken = jwtTokenUtil.generateToken(user);
            response.addHeader("Set-Authorization", newToken);
        }
    }
}

chain.doFilter(request, response);

解决方案

  • 校验JWT令牌提取逻辑:当前代码未展示从Cookie中提取JWT的过程,需确保jwtToken是从请求Cookie中正确获取(而非默认的Authorization Header),如果提取失败,email会为null,导致认证逻辑完全不执行。
  • 确认SecurityContext生命周期:无状态模式下SecurityContextHolder默认用ThreadLocal存储上下文,可在setAuthentication后添加日志,确认上下文已正确设置;同时在后续拦截器或控制器中打印上下文,追踪认证信息何时丢失。
  • 检查过滤器执行顺序:虽然addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)的顺序正确,但需确保没有其他过滤器(如Spring Security的匿名过滤器)在JWT过滤器之前执行,提前设置了匿名认证上下文。可通过http.getFilters()打印过滤器链顺序排查。
  • 调整认证令牌构造:UsernamePasswordAuthenticationToken的第一个参数若为user对象,需确保Users类正确实现UserDetails接口,否则Spring Security可能无法识别其为有效认证主体。若暂时无法修改实体类,可改为传入user.getUsername()作为主体,同时将user对象存入details字段中。
  • 显式关闭匿名认证:在无状态JWT场景下,可显式关闭匿名认证,避免匿名上下文覆盖JWT认证结果:
http.anonymous().disable();
  • 检查Cookie属性配置:确认前端设置的Cookie属性(如HttpOnly、Secure、SameSite)符合请求场景要求,若Cookie未被正确携带到后端,自然无法获取JWT令牌完成认证。

内容的提问来源于stack exchange,提问作者Barry Chapman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 21:57:53