程序员小红 2025-01-14 07:14 采纳率: 33.3%
浏览 61

Spring Boot + Spring Security + JWT 中 Claims.get() 返回 null 致空指针

关于JWT令牌解析报错 “Cannot invoke "Object.toString()" because the return value of "io.jsonwebtoken.Claims.get(Object)" is null” 的问题

我在开发一个基于Spring Boot的项目,项目结构中有  filter  包和  util  包 ,分别存放  TokenFilter  和  TokenService  类。功能是通过JWT令牌进行用户和学生登录验证。但运行时出现报错 “Cannot invoke "Object.toString()" because the return value of "io.jsonwebtoken.Claims.get(Object)" is null”。
[此处附上夸克网盘链接,内有完整代码:https://pan.quark.cn/s/a0ef1d1cf701]

package com.example.studyspot.filter;

import com.alibaba.fastjson2.JSON;
import com.example.studyspot.security.SysStudentDetails;
import com.example.studyspot.security.SysUserDetails;
import com.example.studyspot.util.TokenService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;

@Slf4j
@Component
public class TokenFilter extends OncePerRequestFilter {
    @Autowired
    private TokenService tokenService;

    public TokenFilter(TokenService tokenService) {
        this.tokenService = tokenService;
    }

    private final List<String> WHITE_LIST = List.of(
            "/swagger-ui.html",
            "/swagger-ui/**",
            "/doc.html/**",
            "/v3/api-docs/**",
            "/*/api-docs",
            "/webjars/**",
            "/user/register",
            "/user/login",
            "/student/register",
            "/student/login"
    );

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.info("TokenFilter doFilterInternal start");

        if (isWhitelisted(request)) {
            filterChain.doFilter(request, response);
            return;
        }
        SysUserDetails userDetails = tokenService.getUserLogin(request);
        SysStudentDetails studentDetails = tokenService.getStudentLogin(request);

        if (userDetails == null && studentDetails == null) {
            sendUnauthorizedResponse(response, "Token expired or invalid.");
            return;
        }

        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            UsernamePasswordAuthenticationToken authenticationToken;
            if (userDetails != null) {
                authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            } else {
                authenticationToken = new UsernamePasswordAuthenticationToken(studentDetails, null, studentDetails.getAuthorities());
            }
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }

        filterChain.doFilter(request, response);
        log.info("TokenFilter doFilterInternal end");
    }

    private boolean isWhitelisted(HttpServletRequest request) {
        return WHITE_LIST.stream().anyMatch(path -> matchesPath(request.getServletPath(), path));
    }

    private boolean matchesPath(String servletPath, String whiteListPath) {
        String regex = whiteListPath.replace("*", "[^/]*");
        return Pattern.compile(regex).matcher(servletPath).matches();
    }

    private void sendUnauthorizedResponse(HttpServletResponse response, String message) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json");
        response.getWriter().write(JSON.toJSONString(message));
    }
}



package com.example.studyspot.filter;

import com.alibaba.fastjson2.JSON;
import com.example.studyspot.security.SysStudentDetails;
import com.example.studyspot.security.SysUserDetails;
import com.example.studyspot.util.TokenService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;

@Slf4j
@Component
public class TokenFilter extends OncePerRequestFilter {
    @Autowired
    private TokenService tokenService;

    public TokenFilter(TokenService tokenService) {
        this.tokenService = tokenService;
    }

    private final List<String> WHITE_LIST = List.of(
            "/swagger-ui.html",
            "/swagger-ui/**",
            "/doc.html/**",
            "/v3/api-docs/**",
            "/*/api-docs",
            "/webjars/**",
            "/user/register",
            "/user/login",
            "/student/register",
            "/student/login"
    );

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.info("TokenFilter doFilterInternal start");

        if (isWhitelisted(request)) {
            filterChain.doFilter(request, response);
            return;
        }
        SysUserDetails userDetails = tokenService.getUserLogin(request);
        SysStudentDetails studentDetails = tokenService.getStudentLogin(request);

        if (userDetails == null && studentDetails == null) {
            sendUnauthorizedResponse(response, "Token expired or invalid.");
            return;
        }

        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            UsernamePasswordAuthenticationToken authenticationToken;
            if (userDetails != null) {
                authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            } else {
                authenticationToken = new UsernamePasswordAuthenticationToken(studentDetails, null, studentDetails.getAuthorities());
            }
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }

        filterChain.doFilter(request, response);
        log.info("TokenFilter doFilterInternal end");
    }

    private boolean isWhitelisted(HttpServletRequest request) {
        return WHITE_LIST.stream().anyMatch(path -> matchesPath(request.getServletPath(), path));
    }

    private boolean matchesPath(String servletPath, String whiteListPath) {
        String regex = whiteListPath.replace("*", "[^/]*");
        return Pattern.compile(regex).matcher(servletPath).matches();
    }

    private void sendUnauthorizedResponse(HttpServletResponse response, String message) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json");
        response.getWriter().write(JSON.toJSONString(message));
    }
}



我推测是在解析JWT令牌的声明(Claims)时获取到了  null  值,导致后续调用  toString()  出错。希望能帮忙分析并解决此问题,感谢!

  • 写回答

2条回答 默认 最新

  • 道友老李 JWE233286一种基于机器视觉的水表指针读数识别及修正的方法 专利发明者 2025-01-14 07:15
    关注
    让【道友老李】来帮你解答,本回答参考gpt编写,并整理提供,如果还有疑问可以点击头像关注私信或评论。
    如果答案让您满意,请采纳、关注,非常感谢!
    这个报错是因为在从JWT令牌中解析出的Claims对象中获取某个键对应的值时,该键对应的值为null。可能是令牌内容有误或者解析出的Claims对象不包含该键。以下是可能的解决方案:
    1. 检查JWT令牌内容是否正确:确保JWT令牌中包含了需要的键值对,以及值不为null。
    2. 检查TokenService中解析JWT令牌的逻辑:确保TokenService在解析JWT令牌后,正确获取了Claims对象并能够正确从Claims对象中获取需要的值。
    3. 对从Claims对象中获取的值进行null判断:在获取Claims对象中某个键对应的值之前,可以先进行null判断,避免出现空指针异常。
    4. 如果以上方法无法解决问题,可以尝试捕获空指针异常,并根据具体情况处理异常。 以下是可能的代码调整示例:
    // 在TokenService中解析JWT令牌时,对获取的Claims对象中某个键对应的值进行null判断
    String userId = claims.get("userId") != null ? claims.get("userId").toString() : null;
    // 在TokenFilter中解决问题
    Object value = claims.get("key");
    if(value == null) {
        // 处理异常情况
    } else {
        // 继续执行逻辑
    }
    

    通过上述方法,可以尝试解决"Cannot invoke "Object.toString()" because the return value of "io.jsonwebtoken.Claims.get(Object)" is null"报错问题。

    评论

报告相同问题?

问题事件

  • 修改了问题 1月14日
  • 创建了问题 1月14日