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

Spring Boot无状态JWT认证疑问:查库、上下文存储等问题

Spring Boot JWT 实现的核心疑问

我是Spring Boot新手,之前用NodeJS(Javascript)开发。NodeJS里JWT令牌的生成和验证不用写太多代码,但Spring Boot里要写不少。研究了4天Spring Boot的JWT实现,反而越来越困惑:有些教程会在过滤器里给每个请求获取UserDetails。如果每个请求都要查数据库拿UserDetails,那JWT的意义何在?

示例代码如下:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    //Authorization

    String requestHeader = request.getHeader("Authorization");
    //Bearer 2352345235sdfrsfgsdfsdf
    logger.info(" Header :  {}", requestHeader);
    String username = null;
    String token = null;
    if (requestHeader != null && requestHeader.startsWith("Bearer")) {
        //looking good
        token = requestHeader.substring(7);
        try {

            username = this.jwtHelper.getUsernameFromToken(token);

        } catch (IllegalArgumentException e) {
            logger.info("Illegal Argument while fetching the username !!");
            e.printStackTrace();
        } catch (ExpiredJwtException e) {
            logger.info("Given jwt token is expired !!");
            e.printStackTrace();
        } catch (MalformedJwtException e) {
            logger.info("Some changed has done in token !! Invalid Token");
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();

        }


    } else {
        logger.info("Invalid Header Value !! ");
    }


    //
    if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {


        //fetch user detail from username but why??
        UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
        Boolean validateToken = this.jwtHelper.validateToken(token, userDetails);
        if (validateToken) {

            //set the authentication
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);


        } else {
            logger.info("Validation fails !!");
        }


    }

    filterChain.doFilter(request, response);

}

在最后的if条件里,要获取用户详情来验证令牌,那用JWT还有什么意义?另外我还疑惑为什么要把验证通过的用户信息存入应用的SecurityContext,就算部署在多Docker容器里,请求也不一定路由到持有这个上下文的实例。具体疑问如下:

  • 为何后续每个请求都要获取用户详情?JWT不是无状态的吗?这不会增加数据库查询负载吗?
  • 能否将用户名、邮箱、权限等用户信息存入JWT负载,验证时直接提取,无需查询数据库?
  • 为何要将用户信息存入SecurityContext?若应用部署在多Docker容器中,请求不一定路由到持有该上下文的实例。
  • 必须使用内置的UserDetails类进行验证吗?能否自定义用户详情逻辑?多数YouTube教程都使用UserDetails类。

解答

1. 为什么每个请求都要查用户详情?

这种写法是为了双重验证:一方面校验JWT本身的签名、有效期等合法性,另一方面通过数据库确认用户状态(比如账号是否被封禁)。但这确实违背了JWT无状态的设计初衷,会额外增加数据库负载。

如果你的业务场景不需要实时校验用户状态(比如允许令牌有效期内,用户即使被封禁仍能正常访问),完全可以去掉这个数据库查询,只验证JWT的签名和有效期即可。

2. 能不能把用户信息存入JWT负载?

当然可以。JWT的Payload部分就是用来存储自定义字段的,你可以把用户名、邮箱、权限列表等信息放进去。验证时直接从Payload里提取数据,无需查询数据库。

需要注意:Payload是Base64明文编码的,绝对不能存敏感信息(比如密码);另外Payload内容越多,JWT令牌越长,会增加请求开销;如果用户权限等信息在令牌有效期内发生变化,旧令牌仍会保留旧权限,除非你额外实现令牌黑名单等失效机制。

3. 为什么要存入SecurityContext?

Spring Security的整个权限体系都是基于SecurityContext运转的,后续的接口权限校验(比如@PreAuthorize注解)、获取当前登录用户信息,都是从这个上下文里读取数据。

至于多Docker容器的问题:SecurityContext是请求线程级别的,每个请求进来时在当前处理线程中创建,请求结束后销毁,和容器实例无关。不管请求路由到哪个容器,只要在处理请求的线程里设置了SecurityContext,后续逻辑就能正常获取到数据,不存在跨容器共享的问题。

4. 必须用UserDetails吗?能不能自定义?

不是必须的,但UserDetails是Spring Security提供的标准接口,用来统一封装用户核心信息(用户名、密码、权限等),和框架的其他组件(比如AuthenticationManager、UserDetailsService)兼容性最好,所以大部分教程都会使用它。

你完全可以自定义用户信息类,只要在创建Authentication对象时传入自定义主体即可。示例代码如下:

// 自定义用户信息类
public class CustomUser {
    private String username;
    private String email;
    private List<String> roles;
    // getter、setter方法
}

// 在过滤器中创建Authentication时传入自定义对象
CustomUser customUser = new CustomUser();
// 从JWT Payload中提取数据赋值给customUser
UsernamePasswordAuthenticationToken authentication = 
    new UsernamePasswordAuthenticationToken(customUser, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);

后续就能通过SecurityContextHolder拿到自定义的CustomUser对象了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:24:52