我实现了 JWT 身份验证服务,该过程运行良好,并且我得到了 jwt 令牌作为每次身份验证的响应。问题是,当尝试访问另一个需要身份验证的端点时,我为 user1 获得的任何令牌都适用于其他用户,这意味着例如当尝试获取有关 user1 的配置文件的数据时,使用另一个用户的 jwt 身份验证令牌我可以在以下情况下获取 user1 的数据:他甚至没有进行身份验证。 我尝试在生成令牌时添加用户的声明,但问题仍然相同。 这是 JwtAuthenticationFilter 类:
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
)
throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
final String jwt_token;
final String userEmail;
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwt_token = authHeader.substring(7);
userEmail = jwtService.extractUserEmail(jwt_token); // extract userEmail from JWT token
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
if (jwtService.isTokenValid(jwt_token, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authToken);
}
else {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized");
return;
}
}
filterChain.doFilter(request, response);
}
}
这是 JwtService 类:
@Service
public class JwtService {
//constant secret key that must change
private static final String PRIVATE_KEY_PATH = "C:\\Users\\ihebt\\Documents\\security\\security\\src\\main\\resources/private.key";
/* public JwtService(CustomLoginSuccessHandler authenticationSuccessHandler, CustomLoginFailureHandler authenticationFailureHandler) {
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.authenticationFailureHandler = authenticationFailureHandler;
}*/
public String extractUserEmail(String jwtToken) {
return extractClaim(jwtToken, Claims::getSubject);
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("email", userDetails.getUsername());
return generateToken(claims, userDetails);
}
public String generateToken(
Map<String, Object> extraClaims,
UserDetails userDetails
) {
return Jwts
.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24))
.signWith(getSignInKey(), SignatureAlgorithm.HS256)
.compact();
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver){
final Claims claims = extractAllClaims(token);
return claimsResolver.apply((claims));
}
public boolean isTokenValid(String token, UserDetails userDetails) {
final String useremail = extractUserEmail(token);
return (useremail.equals(userDetails.getUsername())) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
private Claims extractAllClaims(String jwt_token) {
return Jwts
.parserBuilder()
.setSigningKey(getSignInKey())
.build()
.parseClaimsJws(jwt_token)
.getBody();
}
private Key getSignInKey() {
try {
byte[] privateKeyBytes = Files.readAllBytes(Paths.get(PRIVATE_KEY_PATH));
return Keys.hmacShaKeyFor(privateKeyBytes);
} catch (IOException e) {
throw new RuntimeException("Failed to read private key file", e);
}
}
}