Spring Boot应用中如何不通过抛出异常判断JWT Token是否过期
Great question—relying on exceptions for routine validation checks definitely gets messy, especially when you need to do this in multiple places. Let's break down how you can avoid exception-based checks for JWT expiration and implement a cleaner, reusable solution.
Solution 1: Directly Check Expiration from JWT Claims
Most JWT libraries let you parse the token's claims without automatically throwing an exception for expiration (you can disable that validation temporarily). You can then extract the exp (expiration time) claim and compare it against the current time manually.
Example with JJWT (Spring's common JWT library)
First, disable automatic expiration validation during parsing, then check the expiration date explicitly:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import java.util.Date; // Assume you have your signing key configured byte[] signingKeyBytes = "your-signing-key-here".getBytes(); public boolean isTokenExpired(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(signingKeyBytes)) .ignoreExpiration() // Disable automatic expiration check .build() .parseClaimsJws(token) .getBody(); Date expirationDate = claims.getExpiration(); // Check if expiration date exists and is before current time return expirationDate != null && expirationDate.before(new Date()); }
Example with Nimbus JOSE + JWT
If you're using Nimbus, the approach is similar:
import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWTParser; import com.nimbusds.jwt.JWTClaimsSet; import java.util.Date; public boolean isTokenExpired(String token) throws ParseException { JWT jwt = JWTParser.parse(token); JWTClaimsSet claimsSet = jwt.getJWTClaimsSet(); Date expirationDate = claimsSet.getExpirationTime(); return expirationDate != null && expirationDate.before(new Date()); }
Solution 2: Create a Custom Token Wrapper Class
To make this even more reusable, wrap the JWT claims in a custom class that encapsulates the expiration check (and other common token operations). This way, you can call a simple .isExpired() method anywhere in your code.
import io.jsonwebtoken.Claims; import java.util.Date; import java.util.Map; public class CustomJwtToken { private final Claims claims; public CustomJwtToken(Claims claims) { this.claims = claims; } // Check if token is expired public boolean isExpired() { Date expiration = claims.getExpiration(); return expiration != null && expiration.before(new Date()); } // Helper method to get subject public String getSubject() { return claims.getSubject(); } // Helper method to get your custom 'registry' claim @SuppressWarnings("unchecked") public Map<String, String> getRegistry() { return (Map<String, String>) claims.get("registry"); } // Add other helper methods for claims you use regularly }
Then, when you parse a token, create an instance of this wrapper:
CustomJwtToken createCustomToken(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(signingKeyBytes)) .ignoreExpiration() .build() .parseClaimsJws(token) .getBody(); return new CustomJwtToken(claims); }
Now, anywhere you need to check expiration, just call:
CustomJwtToken token = createCustomToken(requestToken); if (token.isExpired()) { // Handle expired token logic } else { // Proceed with authentication, etc. }
Solution 3: Integrate with Spring Security Filter Chain
Since you're using Spring Boot, you can embed this logic into a custom authentication filter. This way, token expiration is checked once early in the request lifecycle, and you don't have to repeat the check in individual methods.
import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import java.io.IOException; import java.util.Collections; public class JwtAuthenticationFilter extends OncePerRequestFilter { private final byte[] signingKeyBytes; public JwtAuthenticationFilter(String signingKey) { this.signingKeyBytes = signingKey.getBytes(); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = extractTokenFromRequest(request); if (token != null) { try { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(signingKeyBytes)) .ignoreExpiration() .build() .parseClaimsJws(token) .getBody(); CustomJwtToken customToken = new CustomJwtToken(claims); if (customToken.isExpired()) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token has expired"); return; } // Set authentication in security context for downstream use UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( customToken.getSubject(), null, Collections.emptyList() ); SecurityContextHolder.getContext().setAuthentication(authToken); } catch (Exception e) { // Handle invalid token (invalid signature, malformed, etc.) response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token"); return; } } filterChain.doFilter(request, response); } private String extractTokenFromRequest(HttpServletRequest request) { // Extract token from Authorization header (Bearer token) or request parameter String bearerToken = request.getHeader("Authorization"); if (bearerToken != null && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return request.getParameter("token"); } }
Register this filter in your Spring Security configuration, and it will handle expiration checks automatically for all incoming requests.
内容的提问来源于stack exchange,提问作者wishyouwerehere




