如何验证JWT令牌的自定义过期时长并手动指定签名算法?
JWT解码与自定义验证问题
我有一段用于解码和验证JWT令牌的Java代码如下:
public class JwtTokenDecoder implements JwtDecoder { private String secretKey = "123456789"; @SneakyThrows @Override public Jwt decode(String token) throws JwtException { JwtParser build = Jwts.parser().verifyWith(getSigningKey()).build(); io.jsonwebtoken.Jwt<?, ?> parse = build.parse(token); Map<String, Object> claims = JsonHelper.jsonMapper().convertValue(parse.getPayload(), new TypeReference<>() { }); claims.put("iat", Instant.ofEpochSecond((long) claims.get("iat"))); claims.put("exp", Instant.ofEpochSecond((long) claims.get("exp"))); return Jwt.withTokenValue(token) .headers((val) -> val.putAll(parse.getHeader())) .claims((val) -> val.putAll(claims)) .build(); } private SecretKey getSigningKey() { byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8); return Keys.hmacShaKeyFor(keyBytes); } }
请问如何基于自定义的最小可接受值(例如令牌不超过30天有效期)验证其是否过期,同时手动指定签名算法类型?
解决方案
1. 手动指定签名算法
在构建JwtParser时,通过requireSigningAlgorithm方法明确指定签名算法(如HS256),确保令牌使用预期的算法签名,避免算法混淆类的安全风险。
2. 自定义有效期验证
从JWT的iat(签发时间)字段计算令牌的最大允许有效期,对比当前系统时间,若超出自定义阈值则抛出JwtException;同时可保留默认的exp字段验证,形成双重保障。
修改后的完整代码如下:
import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtException; import lombok.SneakyThrows; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; public class JwtTokenDecoder implements JwtDecoder { private String secretKey = "123456789"; // 自定义令牌最大有效期:30天 private static final long MAX_VALID_DAYS = 30; @SneakyThrows @Override public Jwt decode(String token) throws JwtException { // 手动指定签名算法为HS256 JwtParser parser = Jwts.parser() .requireSigningAlgorithm(SignatureAlgorithm.HS256.getValue()) .verifyWith(getSigningKey()) .build(); io.jsonwebtoken.Jwt<?, ?> parsedJwt = parser.parse(token); Map<String, Object> claims = new ObjectMapper().convertValue(parsedJwt.getPayload(), new TypeReference<>() {}); // 转换时间字段为Instant类型 Instant issuedAt = Instant.ofEpochSecond((long) claims.get("iat")); Instant expiration = Instant.ofEpochSecond((long) claims.get("exp")); claims.put("iat", issuedAt); claims.put("exp", expiration); // 自定义有效期验证:签发后超过30天则失效 Instant maxValidTime = issuedAt.plus(MAX_VALID_DAYS, ChronoUnit.DAYS); if (Instant.now().isAfter(maxValidTime)) { throw new JwtException("令牌已超出自定义最大有效期(30天)"); } // 保留默认过期验证:确保令牌未超过自身设置的exp时间 if (Instant.now().isAfter(expiration)) { throw new ExpiredJwtException(parsedJwt.getHeader(), parsedJwt.getPayload(), "令牌已过期"); } return Jwt.withTokenValue(token) .headers(val -> val.putAll(parsedJwt.getHeader())) .claims(val -> val.putAll(claims)) .build(); } private SecretKey getSigningKey() { byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8); return Keys.hmacShaKeyFor(keyBytes); } }
关键说明
- 签名算法指定:
requireSigningAlgorithm方法强制令牌使用指定算法签名,不符合要求的令牌会直接抛出JwtException,提升安全性。 - 自定义有效期:通过
issuedAt.plus(MAX_VALID_DAYS, ChronoUnit.DAYS)计算令牌允许的最晚使用时间,确保令牌不会被长期滥用。 - 双重过期验证:同时验证
exp字段和自定义有效期,既遵守令牌自身的过期设置,也满足业务侧的有效期限制。
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

