java中jwt令牌通常是拿来做身份校验,这里介绍jjwt和hutool的jwt共两种的使用方法
一、io.jsonwebtoken下的jwt
1.引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!----- jdk>8加上下面的,8不用加 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
2.jwt的分发与解密
这里我们创建一个类,后续可以改成util类
2.1使用固定密钥的加密(对称加密)
固定密钥主要是指在jwt的第三个部分,签名的部分,使用一串固定字符串进行签名并加密,解密的时候用的是同样的密钥。这样简单但是不是很安全,一旦密钥泄露,别人就可以伪造签名。
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
public static void main(String[] args) {
//创建
//填入信息
Map<String,Object> map= new HashMap<>();
map.put("phone",10086);
//创建
String token = createJWT("hello", 6000, map);
System.out.println(token);
//解析
Claims claims = parseJWT("hello", token);
System.out.println(claims);
}
/**
* 生成jwt
* 使用Hs256算法, 私匙使用固定秘钥
* @param secretKey jwt秘钥
* @param ttlMillis jwt过期时间(毫秒)
* @param claims 设置的信息
* @return
*/
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
// 指定签名的时候使用的签名算法,header上写的算法
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// jwt过期时间设置
long expMillis = System.currentTimeMillis() + ttlMillis;
Date exp = new Date(expMillis);
// 设置jwt的body
JwtBuilder builder = Jwts.builder()
// jwt的载荷,存了什么信息
.setClaims(claims)
// 设置签名使用的签名算法和签名使用的秘钥
.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
// 设置过期时间
.setExpiration(exp);
return builder.compact();
}
/**
* Token解密
*
* @param secretKey 秘钥
* @param token 加密后的token
* @return
*/
public static Claims parseJWT(String secretKey, String token) {
// 得到DefaultJwtParser
Claims claims = Jwts.parser()
// 设置签名的秘钥
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
// 设置需要解析的jwt
.parseClaimsJws(token).getBody();
return claims;
}
}
运行后控制台如下:
后续可以把main方法删了,做工具类
2.2使用公私钥加密(非对称加密)
使用密钥对加密其实只是增加了前置操作(获取密钥),并且稍稍修改了签名的地方(加密用私钥,解密用公钥,比较安全)
这里我简单的写一段代码,演示一下
public void test2() {
//-------------------获取密钥对---------------------------------
String filePath = "your.jks"; // 替换为你的文件路径
Resource resource = (Resource) new ClassPathResource(filePath);
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(
resource, //文件路径
"yourPassword".toCharArray()); //密码数组
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(
"yourAlias", //Alias
"yourPassword".toCharArray()); //密码数组
//-------------------加密---------------------------------
//过期时间
Date date = new Date(System.currentTimeMillis() + 600000);
//注意,加密的时候用的是私钥
PrivateKey privateKey = keyPair.getPrivate();
//载荷,这里存一个user=10086
Map<String,Object> claims =new HashMap<>();
claims.put("user",10086);
JwtBuilder builder = Jwts.builder()
// 设置算法和私钥进行签名
.setClaims(claims)
.signWith(SignatureAlgorithm.RS256, privateKey) //加密用私钥!
.setExpiration(date);
String token2 = builder.compact();
System.out.println(token2);
//-------------------解密---------------------------------
Claims c = Jwts.parser()
// 设置签名的秘钥 注意,解密用公钥!
.setSigningKey(keyPair.getPublic())
// 设置需要解析的jwt
.parseClaimsJws(token2).getBody();
System.out.println(c);
}
结果如下:
二、 cn.hutool下hutool-all的jwt
1.引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
2.jwt的分发与解密
2.1使用固定密钥的加密
感觉对称加密上,糊涂包并没有jjwt好用,主要是糊涂包的Signer签名器不能直接接受"自定义密钥".getBytes()这种形式的密钥,必须是符合编码格式的,比较麻烦
class JwtExample {
private static final JWTSigner jwtSigner;
static {
//假设密钥是helloWorld 后面改成工具类的时候,取掉main方法,把这块static里面的放到构造函数里就好
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("helloWorld");
SecretKeySpec keySpec = new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
//设置签名器
jwtSigner=JWTSignerUtil.createSigner("hs256",keySpec);
}
//main方法测试一下
public static void main(String[] args) {
Map<String,Object> map = new HashMap<>();
map.put("phone",100860); //加个手机号为100860
String token = createJwt(map);
System.out.println(token);
JWTPayload jwtPayload = parseJwt(token);
System.out.println(jwtPayload.toString());
}
//创建jwt令牌
public static String createJwt(Map<String,Object> claims){
Date date = new Date(System.currentTimeMillis() + 600000);
String token =JWT.create()
.setPayload("phone",claims.get("phone")) // 注意,糊涂包给一条一条加数据
.setExpiresAt(date) // 一个键值对就一个setPayload()
.setSigner(jwtSigner) //签名器
.sign();
return token;
}
//解析jwt令牌
public static JWTPayload parseJwt(String token){
JWT jwt;
jwt = JWT.of(token).setSigner(jwtSigner);
return jwt.getPayload();
}
}
2.2使用公私钥加密
非对称加密上,只要把密钥对传给签名器就可以了,签名器会自己根据是创建还是解析,来自动使用公钥或者私钥,这就不用我们自己去获取对应的密钥再进行使用,相对于jjwt方便了一些
public void test(){
//------------------获取密钥对-----------------------------
String filePath = "yourJks.jks"; // 替换为你的文件路径
Resource resource = (Resource) new ClassPathResource(filePath);
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(
resource,
"yourPassword".toCharArray());
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(
"yourAlias",
"yourPassword".toCharArray());
//设置签名器,请自己在类里定义一个成员变量
this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);
//--------------------加密--------------------------------------
Date date = new Date(System.currentTimeMillis() + 600000);
String token1 =JWT.create()
.setPayload("phone", 1008899) //数据,还是一条一条的插
.setExpiresAt(date) //过期日期
.setSigner(jwtSigner) //签名器
.sign();
System.out.println(token1);
//--------------------解密-------------------------------------------
JWT jwt;
jwt = JWT.of(token1).setSigner(jwtSigner); //这里直接把签名器写进去
System.out.println(jwt.getPayload().toString());
}
结果如下:
三、注意事项
注意,非对称加密和对称加密的加密算法并不一样,对称加密是hs256而非对称是rs256,当然你可以改成别的,但是要注意跟你的密钥选择要匹配
四、总结
个人观点哈!如果单论方便来说,在使用对称加密的时候,jjwt更方便一些,并不用对密钥进行各种处理,相反糊涂包的需要对密钥进行编码,并且给符合密钥格式.而在非对称加密的时候,jjwt需要对于加解密取得不同的密钥进行业务处理,而糊涂包的只要把密钥对放进签名器,会自动处理,这样糊涂包就又方便了一些.但是只能说差的不是很多!修改一下也不会很麻烦