JJWT的是在JVM上创建和验证JSON Web Token(JWTs)的库。
JJWT是基于JWT、JWS、JWE、JWK和JWA RFC规范的Java实现。
这个库是由Okta的Les Hazlewood创建的,现在由一个贡献者社区维护。
JWT是一种在两方之间传输信息的方法。
在JWT的主体中编码的信息被称为claims。JWT的扩展形式是JSON格式,因此每个claim都是JSON对象中的一个键。
JWTs可以加密签名(使它成为JWS)或加密(使它成为JWE)。
这为JWTs增强了可验证性。例如,接收者可以确定JWT没有通过验证签名来篡改。
所生成JWT的结果是有三个部分的字符串,每个部分由"."分隔。
例如:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY
每一节都是base 64编码的。
第一部分是header,必须指定用于签署JWT的算法。
eyJhbGciOiJIUzI1NiJ9
第二部分是body。本节包含了JWT编码的所有声明。
eyJzdWIiOiJKb2UifQ
最后一部分是signature。它通过在头文件中指定的算法通过header和body的组合来计算。
ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY
如果你通过一个基本的64解码器传递前两个部分,你将得到:
header
{
"alg": "HS256"
}
body
{
"sub": "Joe"
}
在这种情况下,我们得到的信息是使用sha - 256算法的HMAC来签署JWT。而且,body有一个claim sub与value Joe。
Registered Claims 包含一些标准的claims,sub就是其中的一个(subject)。
要计算签名,你必须知道签名的sercrect。
Maven:
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.9.0version>
dependency>
Gradle:
dependencies {
compile 'io.jsonwebtoken:jjwt:0.9.0'
}
Note:JJWt依赖了 Jackson 2.x。
假如你依赖了jackson的老版本,你必须更新项目中的版本到新版本,否则会冲突。
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.8.9version>
dependency>
先看一个简单的例子:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.MacProvider;
import java.security.Key;
// We need a signing key, so we'll create one just for this example. Usually
// the key would be read from your application configuration instead.
Key key = MacProvider.generateKey();
String compactJws = Jwts.builder()
.setSubject("Joe")
.signWith(SignatureAlgorithm.HS512, key)
.compact();
在上边的例子中下,我们构建了一个JWT,该JWT将将注册的claim的sub(subject)设置为Joe,使用sha - 512算法在HMAC上注册JWT。最后,我们将它转换成字符串形式。
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJKb2UifQ.yiV1GWDrQyCeoOswYTf_xvlgsnaVVYJM0mU6rkmRBf2T1MBl3Xh2kZii0Q9BdX5-G0j25Qv2WF4lA6jPl5GKuA
下面来验证一下jwt:
assert Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws).getBody().getSubject().equals("Joe");
这里有两个时间。之前的密钥被用来验证JWT的签名。如果未能验证JWT,则抛出一个签名异常。假设JWT已被验证,我们将解析claim并断言该sub被设置为Joe。
但如果签名验证失败了怎么办?可以捕获签名异常并做出相应的反应:
try {
Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws);
//OK, we can trust this JWT
} catch (SignatureException e) {
//don't trust the JWT!
}
- HS256: HMAC using SHA-256
- HS384: HMAC using SHA-384
- HS512: HMAC using SHA-512
- RS256: RSASSA-PKCS-v1_5 using SHA-256
- RS384: RSASSA-PKCS-v1_5 using SHA-384
- RS512: RSASSA-PKCS-v1_5 using SHA-512
- PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
- PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
- PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
- ES256: ECDSA using P-256 and SHA-256
- ES384: ECDSA using P-384 and SHA-384
- ES512: ECDSA using P-521 and SHA-512
String compactJws = Jwts.builder()
.setSubject("Joe")
.compressWith(CompressionCodecs.DEFLATE)
.signWith(SignatureAlgorithm.HS512, key)
.compact();
如果检查Jws的header部分,它就会对这个进行解码:
{
"alg": "HS512",
"zip": "DEF"
}
JJWT自动检测到压缩是通过检查头来使用的,并且在解析时将自动解压。对于解压缩,不需要额外的编码。
try {
Jws claims = Jwts.parser()
.requireSubject("Joe")
.require("hasMotorcycle", true)
.setSigningKey(key)
.parseClaimsJws(compactJws);
} catch (MissingClaimException e) {
// we get here if the required claim is not present
} catch (IncorrectClaimException e) {
// we get here if the required claim has the wrong value
}
所有的claim是可选的,并且是大小写敏感的。
"iss" (issuer)是签发该证书的负责人。
"sub" (Subject)是主体。
"aud" (Audience) Claim是指jwt的接受者,假如aud没有发现,则解析jwt时会抛出异常
"exp" (Expiration Time)指的是过期时间,假如超过过期时间,则会抛出异常
"nbf" (Not Before) Claim指的是开始日期,claim要求当前日期/时间必须在以后或等于
在“nbf”声明中列出的日期/时间
"iat" (Issued At) Claim是指jwt的发行时间
"jti" (JWT ID) Claim为JWT提供了惟一的标识符,如果应用程序
使用多个发行者,必须在值之间避免冲突,
由不同的发行商制作。