关于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() 出错。希望能帮忙分析并解决此问题,感谢!