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

Spring Boot实现JWT单次认证后会话内免重复提交的问题

解决方案:基于Session实现JWT一次性认证免重复提交需求

先解决你遇到的NullPointerException问题,再一步步实现会话内免JWT访问的目标。

一、修复空指针异常(NPE)

你的JwtTokenVerifier过滤器中,当请求没有携带Authorization头时,token会被赋值为null,后续调用token.isEmpty()直接触发空指针。先修正这个判断逻辑,同时加入会话认证信息的检查:

public class JwtTokenVerifier extends OncePerRequestFilter {
 @Override
 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    // 优先检查当前会话是否已有认证信息,有则直接放行
    SecurityContext currentContext = SecurityContextHolder.getContext();
    if (currentContext != null && currentContext.getAuthentication() != null && currentContext.getAuthentication().isAuthenticated()) {
        filterChain.doFilter(request, response);
        return;
    }

    String token = request.getHeader("Authorization");
    // 修正判断逻辑:先处理null,避免空指针
    if (token == null || token.isEmpty() || !token.startsWith("Bearer ")) {
        filterChain.doFilter(request, response);
        return;
    }

    try {
        token = token.replace("Bearer ", "");
        Algorithm alg = Algorithm.HMAC512("fStrongPassWordfStrongPassWordfStrongPassWord");
        JWTVerifier verifier = JWT.require(alg).withIssuer("auth0").build();
        DecodedJWT jwt = verifier.verify(token);
        String username = jwt.getSubject();
        Claim c = jwt.getClaim("auths");
        String[] auths = c.asArray(String.class);
        Set<GrantedAuthority> ga = new HashSet<>();
        for (String auth : auths) {
            ga.add(new SimpleGrantedAuthority(auth));
        }

        Authentication authn = new UsernamePasswordAuthenticationToken(username, null, ga);
        SecurityContext sc = SecurityContextHolder.createEmptyContext();
        sc.setAuthentication(authn);
        
        // 将认证上下文存入Session,后续请求自动读取
        HttpSession session = request.getSession(true);
        session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);
        
        // 更新当前线程的认证上下文
        SecurityContextHolder.setContext(sc);
    } catch (JWTVerificationException exception) {
        throw new ServletException(String.format("invalid token supplied:\n%s", token));
    }
    filterChain.doFilter(request, response);
 }
}

关键修改点:

  • 新增会话认证信息检查:如果用户已通过认证,直接放行,无需重复解析JWT
  • 调整token判断顺序:先判断token == null,避免调用isEmpty()触发空指针
  • 使用SecurityContextHolder.createEmptyContext()创建上下文,避免线程安全问题

二、调整Security配置,启用Session

原来的sessionCreationPolicy(SessionCreationPolicy.STATELESS)会禁用Session,我们需要改成启用Session的模式:

public class SecurityConfig extends WebSecurityConfigurerAdapter {
 PasswordEncoder passEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
 @Override
 protected void configure(HttpSecurity http) throws Exception {
 http
 .csrf().disable()
 // 启用Session,使用IF_REQUIRED模式(需要时自动创建)
 .sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
 .and()
 .addFilter(new jwtUsrPassAuthFilter(authenticationManager()))
 .addFilterAfter(new JwtTokenVerifier(), jwtUsrPassAuthFilter.class)
 .authorizeRequests()
 .antMatchers("/", "/login**", "/webjars/**","/css/**","/js/**","/tst1").permitAll()
 .anyRequest()
 .authenticated() ;
 }
}

Spring Security会自动管理Session:第一次认证成功后,将认证信息存入Session;后续请求只要浏览器自动携带Session Cookie,就会自动恢复认证上下文,无需再提交JWT。

三、生产环境优化建议

  1. 增强Session安全性:开启Cookie的httpOnly和secure属性,防止XSS攻击:
http.sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    .and()
    .csrf().disable()
    .headers()
        .httpOnly()
        .and()
        .secure();
  1. 调整Session超时时间:默认Session超时为30分钟(Tomcat默认),可根据业务需求修改:
http.sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    .sessionFixation().migrateSession()
    .invalidSessionUrl("/login")
    .maximumSessions(1) // 限制同一用户只能同时登录一个会话
    .expiredUrl("/login");
  1. 完善登出逻辑:登出时销毁Session和认证上下文:
http.logout()
    .logoutUrl("/logout")
    .addLogoutHandler((request, response, authentication) -> {
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        SecurityContextHolder.clearContext();
    })
    .logoutSuccessUrl("/login");

调整完成后,用户首次携带JWT认证成功后,后续请求只要浏览器自动携带Session Cookie,即可直接访问受保护资源,无需重复提交JWT。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 14:32:50