You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot集成JWT认证时服务器环境出现"Full authentication is required to access this resource"错误求助

Spring Boot集成JWT认证时服务器环境出现"Full authentication is required to access this resource"错误求助

嘿,我仔细梳理了你的代码和日志,找到了几个核心问题,咱们一步步来解决:

先从日志里抓关键线索

  1. JWT验证是通过的(JWT token is valid.),说明签名和密钥匹配没问题
  2. 但从JWT解析出的用户名居然是nullpointerstr nullpointer ),这明显是异常值
  3. 认证后的用户角色是ROLE_SUBSCRIBED,但你的/daily控制器要求的是ROLE_BASIC权限(@PreAuthorize("hasRole('BASIC')")
  4. 最后报错显示请求的是/error,这是因为Spring Security拒绝访问后默认跳转到错误端点,而这个端点也被你的认证逻辑拦截了,才抛出了看似"未认证"的错误——实际是权限不足导致的访问被拒

问题根源拆解

  1. JWT解析出异常用户名:用jwt.io解码日志里的JWT会发现,payload的sub字段就是nullpointer,说明生成JWT时userPrincipal.getUsername()返回了这个值,大概率是服务器环境下用户信息存储/获取逻辑出了问题,或者用户表中存在异常用户名的用户
  2. 权限不匹配:控制器要求BASIC角色,但当前认证用户只有SUBSCRIBED角色,直接触发访问拒绝
  3. 错误端点被拦截:Spring Security拒绝访问后转发到/error,这个路径没被排除在认证外,所以触发了AuthEntryPointJwt的错误日志

解决步骤

1. 修复JWT用户名异常问题

  • 先确认服务器上的bezkoder.app.jwtSecret配置和本地完全一致,避免密钥不一致导致解析异常
  • 排查JWT生成逻辑:检查JwtUtils.generateJwtToken方法中,userPrincipal.getUsername()为什么会返回nullpointer?是不是服务器环境下用户登录时的信息存储有问题?
  • getUserNameFromJwtToken添加异常捕获,避免解析失败导致后续逻辑混乱:
public String getUserNameFromJwtToken(String token) {
    log.info("getUserNameFromJwtToken {} ", token);
    try {
        String str = Jwts.parserBuilder().setSigningKey(key()).build()
                .parseClaimsJws(token).getBody().getSubject();
        log.info("str {} ", str);
        return str;
    } catch (Exception e) {
        log.error("Failed to parse username from JWT: {}", e.getMessage());
        return null;
    }
}

2. 解决权限不匹配问题

你有两个选择:

  • 调整控制器的权限要求,匹配当前用户的角色:
@PreAuthorize("hasRole('SUBSCRIBED')")
  • 或者给目标用户添加ROLE_BASIC权限,确保角色和控制器要求一致

3. 排除错误端点的认证拦截

在你的Spring Security配置类里添加对/error的忽略,避免拒绝访问后触发二次错误:

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/error");
}

4. 优化AuthTokenFilter的逻辑

在过滤器里添加用户名非空判断,避免加载不存在的用户:

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

  try {
    log.info(String.valueOf(request));
    String jwt = parseJwt(request);
    log.info("jwt: " + jwt);

    if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
      String username = jwtUtils.getUserNameFromJwtToken(jwt);
      log.info("username: " + username);

      // 添加用户名非空判断
      if (username != null) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        log.info("userDetails: " + userDetails);

        UsernamePasswordAuthenticationToken authentication =
            new UsernamePasswordAuthenticationToken(
                userDetails,
                null,
                userDetails.getAuthorities());

        log.info("authentication: " + authentication);
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        log.info("authentication: " + authentication);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        log.info("authentication: " + authentication);
      }
    }
  } catch (Exception e) {
    log.error("Cannot set user authentication: {}", e);
  }
  filterChain.doFilter(request, response);
}

另外,你的控制器里的@RequestHeader(value = "Authorization") String authHeader参数可以去掉,因为AuthTokenFilter已经处理了这个请求头,不需要再手动接收。

备注:内容来源于stack exchange,提问作者Nunyet Calçada

火山引擎 最新活动