温故而知新,可以为师矣。

JWT的实现框架

从上一篇 JWT就是这么简单 知道JWT是一种标准,而不是具体的实现,那么在JAVA中实现了JWT的框架多不胜数(公司内部自己写的JWT框架)。

官方推荐是使用官方的Auth0,但是Auth0中功能远远满足不了各种需求。所以各路大神都献出自己写的JWT框架,目前得到官方认可的框架一共是6个
auth0jose4jnimbus-josejjwtfusionauthvertx

上手体验

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

生成密钥代码:

public class CreatRsaKey {//密钥长度 于原文长度对应 以及越长速度越慢 必须大于 512private 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.ecprivate static final String PUBLIC_URI = "src/main/resources/rsa/publickey.rsa";//私钥路径  src/main/resources/ec/privatekey.ecprivate static final String PRIVATE_URI = "src/main/resources/rsa/privatekey.rsa";//加密类型 可以换为ECprivate 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 {//RSAAlgorithm 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());//生产tokenString 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 instanceDecodedJWT 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";//设置密钥和算法SHA256Signer 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();//设置tokenjwsvif.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()); 使用对称加密算法时的Keyjwe.setKey(PairKey.keyPair.getPublic());//序列化tokenString 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());//设置tokenjwe.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);//base64String 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不校验subnbfiatjti

如果只使用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 //数字、浮点类型

JWT框架简单测评,哪款是你的菜相关推荐

  1. php e框架是啥,几款主流PHP框架的优缺点评比

    PHP语言还是比较常用到的一门计算机高级语言.我们将会在这篇文章中向大家主要介绍关于PHP框架相关优缺点评比,作为一个参考风险给朋友们. 主要参考的PHP框架包括:CodeIgniter.CakePH ...

  2. el-admin框架简单解析

    el-admin框架简单解析 el-admin 简单了解 使用框架的四大步 前端文件架构 el-admin前端部分解析 前端Vue目录结构 Layout 布局 mixins 混入模式 router 路 ...

  3. Vue.js - Vue下使用perfect-scrollbar(在特定框架里使用一款并非为该框架定制的库/插件)

    其实,如何在特定框架里使用一款并非为该框架定制的库/插件,只有两点: 1. 熟悉这个框架 2. 熟悉这个库/插件的工作原理 说完废话,进入正题. perfect-scrollbar是一款轻量级的滚动插 ...

  4. python 报表开发工具_测评8款热门的报表开发工具 开源

    阅读提示: 文章中与FineReport软件使用的相关内容,基于软件的V7.0旧版本编写,不代表软件最新的使用方式. FineReport最新版免费试用:https://www.finereport. ...

  5. 【CI3.1】CI框架简单使用方法

    CI框架简单使用方法 1.回忆MVC1.1.M:模型,提供数据,保存数据1.2.V:视图,只负责显示,表单form1.3.C:控制器,协调模型和视图1.4.action:动作,是控制器中的方法,用于被 ...

  6. SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题

    SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题 参考文章: (1)SpringBoot2.0 整合 JWT 框架,解决Token跨域验证问题 (2)https://www. ...

  7. MongoDB聚合运算之group和aggregate聚集框架简单聚合(10)

    聚合运算之group 语法: db.collection.group( { key:{key1:1,key2:1}, cond:{}, reduce: function(curr,result) { ...

  8. Django - Django框架 简单介绍

    Django框架 简单介绍 本文地址: http://blog.csdn.net/caroline_wendy/article/details/29172271 1. 介绍 Django是一个开放源码 ...

  9. Mybatis框架简单使用

    Mybatis框架简单使用 环境搭建 新建一个JavaWeb项目,在web\WEB-INF\创建lib文件,并且在其下添加Mybatis的核心包以及依赖包,以及Mysql驱动包,junit4测试包等. ...

最新文章

  1. 更强、更稳、更高效:解读 etcd 技术升级的三驾马车
  2. web自动化测试之百度经验-HTTP层面的Web Service自动化测试
  3. C语言二叉树的lowest common ancestor最低公共祖先(附完整源码)
  4. 电信信息日志使用mapreduce统计的两种方式
  5. ruijie交换机lacp动态_Server2016和华为s5720交换机链路聚合增加带宽
  6. STM32F1笔记(一)GPIO输出
  7. python 线程池回收_python实现线程池
  8. Hive篇--搭建Hive集群
  9. git 中遇到的错误及解决方法
  10. log4j教程 11、日志记录到文件
  11. TF2.0—tf.keras.losses.BinaryCrossentropy
  12. 修改spring Boot启动时的默认图案Banner
  13. 麦克风阵列的同步性、一致性、峰值幅度、RMS、DC偏移
  14. 创建Django项目及配置
  15. VISIO 连接线转角居然默认不是直角,每次要改格式
  16. 企业个人所得税网上申报系统_增值税、企业所得税、个人所得税申报难点梳理...
  17. day03_顺丰快递分拣小程序
  18. java 1.5 jdk_jdk1.5安装及配置
  19. unity使用videoplayer播放视频黑屏问题解决方案
  20. 03_Snaker流程demo

热门文章

  1. iTunes备份到电脑F盘
  2. intellij idea 管理module和project,以及git管理项目
  3. python 福利_发现一个舔狗福利!这个Python爬虫神器太爽了,自动下载妹子图片!...
  4. Chrome浏览器谷歌浏览器保存的用户名密码突然都没了
  5. python获取人民币汇率数据
  6. 证监会:将重点关注公司上市不满三年卖壳行为
  7. 我爱你宠物网——创业笔记(三)
  8. swf文件提取音频文件方法
  9. python 做绘图工具
  10. 一本修炼秘籍,带你打穿文件上传的21层妖塔(1)