关于java:JWT框架简单测评哪款是你的菜

温故而知新,能够为师矣。

JWT的实现框架

从上一篇 JWT就是这么简略 晓得JWT是一种规范,而不是具体的实现,那么在JAVA中实现了JWT的框架多不胜数(公司外部本人写的JWT框架)。

官网举荐是应用官网的Auth0,然而Auth0中性能远远满足不了各种需要。所以各路大神都献出本人写的JWT框架,目前失去官网认可的框架一共是6个 auth0jose4jnimbus-josejjwtfusionauthvertx

上手体验

上手体验前,朕水先写生成一个密钥、读取密钥的办法。

生成密钥代码:

public class CreatRsaKey {
//密钥长度 于原文长度对应 以及越长速度越慢 必须大于 512
private final static int KEY_SIZE = 2048;

public static void main(String[] args) throws Exception {
//一对密钥算法生成
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(“RSA”);
SecureRandom secureRandom = new SecureRandom();
//设置随机种子
secureRandom.setSeed(32);
keyPairGen.initialize(KEY_SIZE,secureRandom);
KeyPair keyPair = keyPairGen.generateKeyPair();
// 失去私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 失去公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 失去密钥字符串
byte[] publicKeyBytes = Base64.getEncoder().encode(publicKey.getEncoded());
byte[] privateKeyBytes = Base64.getEncoder().encode(privateKey.getEncoded());
//保留到文件中
Resource publicKeyResource = new FileSystemResource(“src/main/resources/rsa/publickey.rsa”);
FileOutputStream publicKeyOutputStream = new FileOutputStream(publicKeyResource.getFile());
publicKeyOutputStream.write(publicKeyBytes);
publicKeyOutputStream.close();
//保留到文件中
Resource privateKeyResource = new FileUrlResource(“src/main/resources/rsa/privatekey.rsa”);
FileOutputStream privateKeyOutputStream = new FileOutputStream(privateKeyResource.getFile());
privateKeyOutputStream.write(privateKeyBytes);
privateKeyOutputStream.close();

}
}

读取密钥代码:


public class PairKey {
//公钥门路 src/main/resources/ec/publickey.ec
private static final String PUBLIC_URI = “src/main/resources/rsa/publickey.rsa”;
//私钥门路 src/main/resources/ec/privatekey.ec
private static final String PRIVATE_URI = “src/main/resources/rsa/privatekey.rsa”;
//加密类型 能够换为EC
private static final String ALGORITHM_TYPE = “RSA”;
//寄存RSA密钥对
public static KeyPair keyPair; //留神这里嗷,这里是非对称加密的容器

static {
try{
//初始化数据
FileInputStream publicInputStream =
new FileInputStream(PUBLIC_URI);
//读取公钥
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(
new BASE64Decoder().decodeBuffer(publicInputStream));
publicInputStream.close();
//读取私钥
FileInputStream privateInputStream =
new FileInputStream(PRIVATE_URI);
PKCS8EncodedKeySpec bobPriKeySpec = new PKCS8EncodedKeySpec(
new BASE64Decoder().decodeBuffer(privateInputStream));
privateInputStream.close();
// 密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_TYPE);
// 取公钥对象
PublicKey publicKey = keyFactory.generatePublic(bobPubKeySpec);
// 取私钥对象
PrivateKey privateKey = keyFactory.generatePrivate(bobPriKeySpec);
// 放入容器
keyPair = new KeyPair(publicKey,privateKey);
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
}
}

auth0

auth0是最为简略的依据密钥(或HMAC)生成Algorithm对象,间接应用Algorithm对象加密解密。

集体认为单例生成Algorithm对象再适宜不过了。

public static void main(String[] args) throws Exception {
//RSA
Algorithm algorithm = Algorithm
.RSA256((RSAPublicKey) PairKey.keyPair.getPublic(), (RSAPrivateKey) PairKey.keyPair.getPrivate());
//Algorithm algorithm = Algorithm.HMAC256(“zhenshuizhenshui”);
//EC
//Algorithm algorithm = Algorithm
// .ECDSA256((ECPublicKey) keyPair.getPublic(), (ECPrivateKey) keyPair.getPrivate());
//生产token
String token = JWT.create()
//签发人
.withIssuer(“zhenshui”)
//自定义信息
.withClaim(“username”,”zhenshuizhenshui”)
.withClaim(“isAuth”,”0″)
.sign(algorithm);
System.out.println(token);
System.out.println(“——–校验token—————“);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(“zhenshui”)
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
Map<String, Claim> claims = jwt.getClaims();
//获取签发的字段
Claim username = claims.get(“username”);
System.out.println(username.asString());
}

jjwt

jjwt能够说是把’极简‘一词给表达出来,token的生成全副一手包办。没有多余的操作余地。

public static void main(String[] args) {
//jjwt反对RSA-PSS算法。然而朕水不会生成PSS算法的的密钥对
String token = Jwts.builder()
.claim(“username”,”zhenshui”)
.claim(“isAuth”,”0″)
.signWith(SignatureAlgorithm.RS256, PairKey.keyPair.getPrivate())
//.signWith(SignatureAlgorithm.HS256,”secret”)
.compact();
//私钥加密,公钥解密
Jwt parse = Jwts.parser()
//设置公钥
.setSigningKey(PairKey.keyPair.getPublic())
//.signWith(SignatureAlgorithm.HS256,”secret”)
.parse(token);
//失去Json数据,所有数据
Object body = parse.getBody();
System.out.println(body.toString());
}

funsionauth

能够说,这个框架是令朕水最悲观的框架。繁冗的操作,只能接管密钥字符串,密钥须要开始和完结标识,速度也比不上后面两个、设置头部信息须要应用函数式编程等等问题。。

public static void main(String[] args) {
//这是一个SMAC256的例子
String  secret =”secret”;
//设置密钥和算法SHA256
Signer signer = HMACSigner.newSHA256Signer(secret);
JWT jwt = new JWT()
//设置载荷根本信息
.setIssuer(“zhenshui”)
.addClaim(“username”,”朕水真水”);
//对JWT进行编码
String token = JWT.getEncoder().encode(jwt, signer,(header)->{
header.set(“key”,”header”); //设置头部信息
});
System.out.println(token);
//校验token应用的校验密钥和算法
Verifier verifier = HMACVerifier.newVerifier(secret);
//进行解码(base64)
JWT decodedJwt = JWT.getDecoder().decode(token, verifier);
//失去所有载荷参数
Map<String, Object> allClaims = decodedJwt.getAllClaims();
System.out.println(allClaims.toString());
//失去自定义参数
Map<String, Object> otherClaims = decodedJwt.getOtherClaims();
System.out.println(otherClaims);
//读取某个参数名的值
String key = decodedJwt.getString(“username”);
System.out.println(key);
}

public static void main(String[] args) throws IOException {
JWT jwt = new JWT()
.setIssuer(“zhenshui”)
.addClaim(“username”,”朕水真水”);
String str = Base64.encodeBase64String(PairKey.keyPair.getPrivate().getEncoded());
//没测试RSA胜利。因为PEMDecoder的decode办法强制要求RSA的公钥私钥必须有 开始的标识和完结的标识
// —–BEGIN RSA PRIVATE KEY—–
//—–END RSA PRIVATE KEY—–
RSASigner rsaSigner = RSASigner.newSHA256Signer(str);
//对JWT进行编码
String encodedJwt = JWT.getEncoder().encode(jwt, rsaSigner,(header)->{
header.set(“key”,”header”);
});

//校验token应用的校验密钥和算法
Verifier verifier = RSAVerifier.newVerifier((RSAPublicKey) PairKey.keyPair.getPublic());
//进行解码(base64)
JWT decodedJwt = JWT.getDecoder().decode(encodedJwt, verifier);
//失去所有载荷参数
Map<String, Object> allClaims = decodedJwt.getAllClaims();
System.out.println(allClaims.toString());
}

下面两个是funsionauth的HMAC256(对称加密算法)和RSA(非对称加密算法)两种算法的例子。如果说应用HMAC256只是简单一点的话。那应用对称加密之类的算法就是给朕水当头一棒。

先说说funsionauth的对称加密的API,它外面设置私钥只能设置字符串类型。好吧,朕水忍住了,转字符串进去。拿字符串去干嘛?一看代码不得了,拿朕水给的字符串去转化为RSAPrivateKey…好吧,这是作者的设计,朕水再忍。运行一下代码,报错了。认真看了看源码,必须要有BEGIN PRIVATE KEY….

可能是朕水不能了解作者的思维,朕水告辞了。后续有机会再钻研吧,这里朕水只做简略上手的测试。整体来说,这个框架被束缚的比拟厉害。

贴一下funsionauth的源码

jose4j

能够说jose4j很是最规矩的一个框架。在载荷放什么数据全看用户设置的Map中,符合规范的字段(sub、iss、jti、aud等)就调用对应的校验器进行校验,提供一个JsonUtil将Map转化为JSON格局的字符串。

public static void main(String[] args) throws JoseException {
//自带生成RSA算法密钥工具
//RsaJsonWebKey jwk = RsaJwkGenerator.generateJwk(1025);
//加密解密应用的都是JsonWebSignature对象
Map<String, String> payload = new HashMap<>();
payload.put(“username”,”zhenshui”);
payload.put(“sub”,”开party!!”);
//设置载荷
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(JsonUtil.toJson(payload));
//设置密钥和加密形式
jws.setKey(PairKey.keyPair.getPrivate());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
//jws.setKey(new HmacKey(secret.getBytes()));
//jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
String token = jws.getCompactSerialization();
System.out.println(token);
//JsonWebSignature校验
JsonWebSignature jwsvif = new JsonWebSignature();
//设置token
jwsvif.setCompactSerialization(token);
//设置公钥
jwsvif.setKey(PairKey.keyPair.getPublic());
//校验
boolean v = jwsvif.verifySignature();
//失去载荷
if (v) {
System.out.println(jwsvif.getPayload());
}
}

jose4j中jwe的实现简略应用。生成与校验根本与JWS应用形式统一。改为JsonWebEncryption对象生成

public static void main(String[] args) throws JoseException {
//应用的都是JsonWebEncryption
//加密
JsonWebEncryption jwe = new JsonWebEncryption();
Map<String, String> payload = new HashMap<>();
payload.put(“username”,”zhenshui”);
//设置载荷
jwe.setPayload(JsonUtil.toJson(payload));
//加密算法
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP_256);
//音讯摘要算法
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
//Key key = new AesKey(secret.getBytes()); 应用对称加密算法时的Key
jwe.setKey(PairKey.keyPair.getPublic());
//序列化token
String token = jwe.getCompactSerialization();
System.out.println( token);
//从新设置一个 对象
jwe = new JsonWebEncryption();
//设置两层算法
jwe.setAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT,
KeyManagementAlgorithmIdentifiers.RSA_OAEP_256));
jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT,
ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256));
//设置密钥
jwe.setKey(PairKey.keyPair.getPrivate());
//设置token
jwe.setCompactSerialization(token);
System.out.println( jwe.getPayload());
//失去的token为5段//在申请头设置两个算法
}

nimbusds-jose

这个框架比照jose4j来说并没有那么晦涩,给朕水的感觉就是应用起来比jose4j简单,然而能显著的感触JWT的设计理念。

public static void main(String[] args) throws MalformedURLException, ParseException, JOSEException, BadJOSEException {
//JWSSigner jwsSigner = new MACSigner(secret); //SMAC256算法
JWSSigner jwsSigner = new RSASSASigner(PairKey.keyPair.getPrivate());
JWSHeader jwsHeader = new JWSHeader
.Builder(JWSAlgorithm.RS256)
.type(JOSEObjectType.JWT).build();
//参数
JWTClaimsSet jwtClaimsSet =new JWTClaimsSet.Builder()
.issuer(“admin”)
.subject(“sub”)
.claim(“username”,”zhenshui”)
.build();
Payload payload = new Payload(jwtClaimsSet.toJSONObject());
JWSObject jwsObject = new JWSObject(jwsHeader, payload);
jwsObject.sign(jwsSigner);
//base64
String token = jwsObject.serialize();
System.out.println(token);
//解密
JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) PairKey.keyPair.getPublic());
//不校验获取头部和载荷
JWSObject parseJWS = JWSObject.parse(token);
//校验
boolean verify = parseJWS.verify(verifier);
//获取载荷
if (verify) {
String decryptPayload = parseJWS.getPayload().toString();
System.out.println(decryptPayload);
}
}

在nimbus-jose中的JWE实现根本与JWS无差。改为JWEObject对象生成

public static void main(String[] args) throws ParseException, JOSEException {
//载荷字段
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.issuer(“admin”)
.subject(“sub”)
.claim(“username”,”zhenshui”)
.build();
//双重加密
JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128CBC_HS256);
//载荷能够设置字符串
//Payload payload = new Payload(“Hello world!”);
Payload payload = new Payload(claimsSet.toJSONObject());
JWEObject jwt = new JWEObject(header, payload);
//加密
RSAEncrypter encrypter = new RSAEncrypter((RSAPublicKey) PairKey.keyPair.getPublic());
//加密
jwt.encrypt(encrypter);
String token = jwt.serialize();
System.out.println(token);
//设置RSA解密对象私钥
RSADecrypter decrypter = new RSADecrypter(PairKey.keyPair.getPrivate());
//解密//公钥加密私钥解密
EncryptedJWT parseJWT = EncryptedJWT.parse(token);
//解密
parseJWT.decrypt(decrypter);
//获取载荷的值
JWTClaimsSet jwtClaimsSet = parseJWT.getJWTClaimsSet();
System.out.println(jwtClaimsSet.toString());
//失去的token为5段//在申请头设置两个算法
}

JWT框架简评

朕水对5个框架进行简略的上手的应用感触给各位参考,后果如下:

上手难度:

auth0 > jjwt > jose4j > fusionauth > nimbus-jose > vertx

vertx排在最初只是朕水集体的应用感触(实际上朕水试用了一天都没有生成Token胜利)。

性能完整性:

只有jose4j和nimbus-jose有JWE的相干API,其余4个没有。

只有jose4j和nimbus-jose才有密钥(非对称加密)的生成器, auth0、jjwt、fusionauth要本人生成私钥和公钥。

只有jose4j和nimbus-jose不强制载荷设置为键值对,能够设置为字符串。auth0、jjwt和funsionauth强制应用键值对。

只有auth0和jjwt没有密钥治理服务的应用API

(朕水狐疑jose4j和nimbus-jose是一伙的!!!性能都十分的类似)

从上手难度、性能完整性来看,举荐应用:**nimbus-jose** > **jose4j**。

举荐起因:nimbus-jose和jose4j性能是最全的。他们两个的性能相差不大,区别点:jose4j的载荷参数要用户设置,nimbus-jose能够应用JWTClaimsSet结构进去,nimbus-jose生成和解析Token速度快,不过nimbus-jose不校验`sub` 、`nbf`、`iat` 、`jti` 。

如果只应用JWS性能举荐应用:**auth0** > **jjwt**。

举荐起因:官网举荐、上手简略、文档全,轻量,生成校验token快。jjwt与auth0相似,然而jjwt则尽量不依赖内部类库,应用本人外部的写的办法。

funsionauth要哭了,毫无位置(funsionauth:喵?喵?喵?)

目前,auth0和jjwt都没有密钥治理服务的API。所以当朕水只想应用JWS的token生成和近程检索密钥的时候,朕水就只有3个抉择funsionauth、nimbus-jose、jose4j。在这三个中,只有funsionauth性能没有冗余。

下面的上手感触的简评并不谨严,理论应用以我的项目需要为准(用不到JWE性能,JWT端点的API等性能就没必要应用nimbus-jose、jose4j),如果有误可斧正。

PS:

浅谈一下对于JSON。JWT是对JSON数据依据某种加密失去的字符串。为什么朕水要把 “能够设置为字符串”归为性能完整性。次要是因为**字符串也是JSON数据格式**!!

朕水察觉很多同学都有一个误区,就是只有上面这种格局才是JSON格局:

{“key”:”value”}

实际上JSON能够有很多种,只是罕用的格局是下面的那种模式。其余的模式例如:

“” //字符串类型

[“1″,”2″,”3”] //数组类型

74.74 //浮点类型

后记

微信关注”朕水真水”,一起学习更多后端技术。

看到发送给我的音讯我尽量查看。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理