Java后端JWT验证的实现
在Java后端中使用JWT令牌验证需要引入三个包
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>
|
jjwt-api用于在编译时提供API接口,包含接口、方法等,jjwt-impl在运行时提供默认实现,里面的方法可以被重写,jjwt-jackson用于提供JSON处理器,也可以使用其他JSON库
基本API
Claims接口
Claims是JWT的核心载荷,继承于Map<String, Object>泛型集合,其内部有一些默认字段
String ISSUER = "iss"; String SUBJECT = "sub"; String AUDIENCE = "aud"; String EXPIRATION = "exp"; String NOT_BEFORE = "nbf"; String ISSUED_AT = "iat"; String ID = "jti";
|
以上字段在接口内都规定了getter、setter方法
CompressionCodec接口
该接口用于规定JWT的压缩与解压方法,以减少 JWT 的总体大小
byte[] compress(byte[] var1) throws CompressionException;
byte[] decompress(byte[] var1) throws CompressionException;
|
header是JWT的头部部分,继承于Map<String, Object>泛型集合,可声明JWT的一些元数据、描述
String JWT_TYPE = "JWT"; String TYPE = "typ"; String CONTENT_TYPE = "cty"; String COMPRESSION_ALGORITHM = "zip";
|
以上字段在接口内都规定了getter、setter方法
JwtBuilder接口
该接口规定了构建JWT令牌的核心方法
public interface JwtBuilder extends ClaimsMutator<JwtBuilder> { JwtBuilder setHeader(Header var1); JwtBuilder setHeader(Map<String, Object> var1); JwtBuilder setHeaderParams(Map<String, Object> var1);
JwtBuilder setHeaderParam(String var1, Object var2); JwtBuilder setPayload(String var1); JwtBuilder setClaims(Claims var1); JwtBuilder setClaims(Map<String, ?> var1); JwtBuilder addClaims(Map<String, Object> var1); JwtBuilder setIssuer(String var1); JwtBuilder setSubject(String var1); JwtBuilder setAudience(String var1); JwtBuilder setExpiration(Date var1); JwtBuilder setNotBefore(Date var1); JwtBuilder setIssuedAt(Date var1); JwtBuilder setId(String var1);
JwtBuilder claim(String var1, Object var2); JwtBuilder signWith(Key var1) throws InvalidKeyException; JwtBuilder signWith(Key var1, SignatureAlgorithm var2) throws InvalidKeyException; JwtBuilder compressWith(CompressionCodec var1); JwtBuilder base64UrlEncodeWith(Encoder<byte[], String> var1); JwtBuilder serializeToJsonWith(Serializer<Map<String, ?>> var1); String compact(); }
|
claim()方法在默认实现类中的逻辑。若claims字段不存在则新添字段,否则检测value是否为空,为空则删除传入的字段,否则放入字段
public JwtBuilder claim(String name, Object value) { Assert.hasText(name, "Claim property name cannot be null or empty."); if (this.claims == null) { if (value != null) { this.ensureClaims().put(name, value); } } else if (value == null) { this.claims.remove(name); } else { this.claims.put(name, value); }
return this; }
|
JwtParserBuilder接口
该接口规定了解析JWT令牌的核心方法,先前的JwtParser众多方法已被弃用,JwtParserBuilder是其替代方案,该方案强制验证 JWT Claims 中的特定值,不匹配则解析失败
public interface JwtParserBuilder { JwtParserBuilder requireId(String var1); JwtParserBuilder requireSubject(String var1); JwtParserBuilder requireAudience(String var1); JwtParserBuilder requireIssuer(String var1);
JwtParserBuilder requireIssuedAt(Date var1);
JwtParserBuilder requireExpiration(Date var1);
JwtParserBuilder requireNotBefore(Date var1);
JwtParserBuilder require(String var1, Object var2); JwtParserBuilder setClock(Clock var1); JwtParserBuilder setAllowedClockSkewSeconds(long var1) throws IllegalArgumentException; JwtParserBuilder setSigningKey(byte[] var1);
JwtParserBuilder setSigningKey(String var1);
JwtParserBuilder setSigningKey(Key var1); JwtParserBuilder setSigningKeyResolver(SigningKeyResolver var1); JwtParserBuilder setCompressionCodecResolver(CompressionCodecResolver var1);
JwtParserBuilder base64UrlDecodeWith(Decoder<String, byte[]> var1);
JwtParserBuilder deserializeJsonWith(Deserializer<Map<String, ?>> var1); JwtParser build(); }
|
JwtParser接口
现在JwtParser一般只用于接收JwtParserBuilder规定的解析规则进行解析操作,不直接参与验证过程
public interface JwtParser { char SEPARATOR_CHAR = '.'; boolean isSigned(String var1); Jwt parse(String var1) throws ExpiredJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; <T> T parse(String var1, JwtHandler<T> var2) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; Jwt<Header, String> parsePlaintextJwt(String var1) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; Jwt<Header, Claims> parseClaimsJwt(String var1) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; Jws<String> parsePlaintextJws(String var1) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; Jws<Claims> parseClaimsJws(String var1) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException; }
|
具体实现
构造JWT,先使用jjwt-impl自带的Jwts类获取claims
Claims claims = Jwts.claims();
|
放入字段,使用密钥进行签名
public String makeUserToken() { Claims claims = Jwts.claims().setSubject("CommonUser"); claims.put("iat", new Date()); claims.put("exp", new Date(System.currentTimeMillis() + EXPIRATION_TIME)); return Jwts.builder().setClaims(claims).signWith(Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8))).compact(); }
|
解析JWT,需要先定义一个解析器
Jws<Claims> claimsJws = Jwts.parserBuilder() .setSigningKey(SECRET.getBytes(StandardCharsets.UTF_8)) .build() .parseClaimsJws(Token);
|
获取字段名
String role = claimsJws.getBody().getSubject(); String other = claimsJws.getBody().get(String claimName, Class<T> requiredType);
|
最后是我写的一个JWT验证后台,用于一个题目的JWT验证,虽十分简陋但也够用
@Component public class JWTHandler { private static final String SECRET = "{SECRET_KEY_WHICH_YOU_NEED_TO_FIND_IT_OUT}"; private static final long EXPIRATION_TIME = 86_400_000L;
public Boolean parseToken(String Token) { try {
Jws<Claims> claimsJws = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8))) .build() .parseClaimsJws(Token); String role = claimsJws.getBody().getSubject(); return role.equals("admin");
} catch (Exception e) { return false; } }
public String makeUserToken() { Date now = new Date(); Date expiry = new Date(now.getTime() + EXPIRATION_TIME); Claims claims = Jwts.claims().setSubject("CommonUser"); claims.put("iat", now); claims.put("exp", expiry); return Jwts.builder().setClaims(claims).signWith(Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8))).compact(); }
}
|