JWT
JWT介绍
JWT(JSON Web Token)是一种用于在网络应用之间传递信息的开放标准(RFC 7519)。它通常用于对用户进行身份验证和授权。JWT是一个紧凑的、自包含的方式来表示信息,它以JSON格式编码,并使用数字签名或加密进行安全验证。
一个JWT通常包含三部分:头部(Header)、载荷(Payload)和签名(Signature)。
- 头部(Header) 包含了关于令牌的元数据,例如令牌的类型和所用的签名算法。
- 载荷(Payload) 包含了实际的数据,通常包括一些声明(例如,用户ID或过期时间)。
- 签名(Signature) 用于验证令牌的完整性。签名是使用头部中指定的签名算法和密钥对头部和荷载进行签名。
JWT的组成
JWT的格式:Header.Payload.Signature
Header部分
json
{
"alg": "HS256",
"typ": "JWT"
}header部门是一个json对象,其中:
- alg:表示签名的算法,默认是 HMAC SHA256(写成 HS256),它是一种特殊的SHA256算法,增加接受一个加密的密钥。
- typ:表示这个令牌的属性,jwt令牌统一写成JWT。
Payload部分
json
{
"iss": "iss user",
"exp": "1516239022",
"sub": "1234567890",
"aud": "aud user",
"nbf": "1516239022",
"iat": "1516239022",
"jti": "jwt id"
"name": "John Doe",
"admin": true
}Payload部分也是一个json对象,官方定义了一些基础字段,包括:
- iss(issuer):表示签发人/发行人
- exp(expiration time):表示失效时间,以时间戳表示,从Unix时间戳的起始时间点开始计算的,即 1970年1月1日 00:00:00 UTC。
- sub(subject):主题、用户
- aud(audience):受众
- nfb(not before):生效日期
- iat(issued at):签发日期
- jti(JWT ID):编号
除了这些基础属性,还可以在这个部分定义私有字段,比如:
json
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}Signature部分
Signature部分是对前两部分的的签名,防止被篡改。一般是按照以下公式进行签名
java
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)- 使用Base64Url算法分别对header和payload部分进行编码,使用
.连接两部分 - 然后使用密钥secret和HMACSHA256散列算法,对上面得到的base64url串进行签名,其中的secret是密钥需要保管好
JWT、JWS、JWE
这三者的关系,相当于JWT是接口,JWS、JWE是JWT的实现。
- JWT是一种json令牌的规范。
- JWS是指对JWT增加签名后的JWT,增加了签名部分。
- JWE是指对jwt的payload部分进行了加密的jwt。
JWT的一些常用库
- nimbus-jose-jwt
- jose4j
- java-jwt
- jjwt
Nimbus JOSE + JWT的使用
官方地址:https://connect2id.com/products/nimbus-jose-jwt
使用HMAC保护JWS对象
创建步骤
- 创建JWSSigner签名器
- 创建JWSObejct对象
- 使用签名器对JWSObject对象签名
- 对JWSObject对象进行序列化serialize输出
验证步骤
- 创建JWSVerifier验证器
- 使用JWSObject对jws进行parse解析
- 使用验证器对JWSObejct进行verify验证
java
@Test
public void jwsWithHMAC() throws JOSEException, ParseException {
SecureRandom secureRandom = new SecureRandom();
byte[] shareSecret = new byte[32];
secureRandom.nextBytes(shareSecret);
//JWS签名过程
//创建签名器
JWSSigner signer = new MACSigner(shareSecret);
JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);
Payload payload = new Payload("hello world");
//闯将jws对象
JWSObject pendingSignJWSObejct = new JWSObject(jwsHeader, payload);
//对未签名的jws对象进行签名
pendingSignJWSObejct.sign(signer);
//对已签名的jws对象进行序列化输出
String jws = pendingSignJWSObejct.serialize();
System.out.println("得到的token是:" + jws);
//JWS验证过程
//从字符串中解析出JWS对象
JWSObject pendingVerifyJWSObject = JWSObject.parse(jws);
//创建jws验证器
JWSVerifier jwsVerifier = new MACVerifier(shareSecret);
//验证jws对象
boolean verify = pendingVerifyJWSObject.verify(jwsVerifier);
if (!verify) {
System.out.println("验证失败");
} else {
System.out.println("验证成功");
}
}使用HMAC保护JWT对象
java
@Test
public void jwtWithHMAC() throws JOSEException, ParseException {
SecureRandom secureRandom = new SecureRandom();
byte[] secret = new byte[32];
secureRandom.nextBytes(secret);
//生成jwt并签名
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder()
.issuer("issuer")
.expirationTime(new Date(new Date().getTime() + 7 * 24 * 60))
.subject("chenzhuowen")
.audience("root")
.notBeforeTime(new Date())
.issueTime(new Date())
.jwtID(UUID.randomUUID().toString())
.build();
JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);
SignedJWT pendSignSignedJWT = new SignedJWT(jwsHeader, jwtClaimsSet);
//创建签名器
MACSigner macSigner = new MACSigner(secret);
//对jwt进行签名
pendSignSignedJWT.sign(macSigner);
//将jwt序列化输出
String jwt = pendSignSignedJWT.serialize();
System.out.println("生成的jwt是:" + jwt);
//解析jwt
SignedJWT pendVerifySignedJWT = SignedJWT.parse(jwt);
MACVerifier macVerifier = new MACVerifier(secret);
boolean verify = pendSignSignedJWT.verify(macVerifier);
if (!verify) {
System.out.println("验证失败");
} else {
System.out.println("验证成功");
}
System.out.println(pendVerifySignedJWT.getJWTClaimsSet().toString());
}