手写jwt验证,实现java和node无缝切换

前言

前端时间和我朋友写了一个简易用户管理后台,功能其实很简单,涉及到的技术栈有:vue+elementUI,java+spring MVC以及node+egg,数据库用的mysql,简单方便。

一开始是我是只负责前端,但是前端开发的的速度太快,老是没事,加上他小子并没有接触过实战的项目,又怕他出乱子,所以考虑我也写一个后端,

开始考虑的是用python+django,但是还是在半途放弃了,因为总感觉Django不过灵活,使用起来非常别扭,也可能是用scrapy写爬虫写多了,难以理解django的框架设计,总是会把他想象成一个scrapy架构,也导致代码写的很乱,下面上一张scrapy架构图。

所以最后考虑的用node,Express和Koa框架学习过node应该都知道,我以前也用过express(写过一个小demo),但是给我的感觉这到像是一个只能有5年以上node开发经验才能玩的转的,因为express是非常的精简的,安装它后,会发现它几乎不会为你提供任何编码规范,也没有约束你的框架应该怎么设计,以至于新手下载完成后完全不知道自己应该干嘛,甚至不知道直接的文件应该写在哪,没有框架本身的约定,标准的MVC模型有着千奇百怪的写法,所以我觉得没有一定的架构思想和经验是很难驾驭的。

koa我并没有直接的使用过,只是听说是express的原班人马打造的,中间件是基于洋葱圈模型实现的。下面上一张图洋葱圈模型图。

而是后来直接就接触的egg,egg标语是“为企业级框架和应用而生”,通道

废话说的有点多了,重点是通一套前端代码,开发了两套后端代码,功能是完全一致的,后端都实现了相应的jwt验证功能,-->json web token,密钥都是我们约定好的固定值,但是最后发现一样的密钥,相同的数据,使用的jwt包不同产生的结果也不同,这也就导致两端之间无法相互切换,每次切换必须从登陆重新开始,这不太符合逻辑,而且重要的是后面的安排有需要这样的功能,无法做到无缝切换就直接导致实现不了下面的功能。

提纲内容

  • jwt实现原理
  • jwt未能解决的疑惑
  • base64和base64url

jwt实现原理

其实作为一个前端开发人员,jwt实现原理我是没必要懂得的,但是如果你不限于此,这算是个必不可少的内容了吧。

先上一张图。

图中数字1是后端使用jwt工具包生成的token,通常是由三部分组成,也就是token中间的两个“.”将其分为三部分,第一部分对应的是右边的数字2部分,然后依次对应。

这三部分分别是头部、有效载荷、签名。

头部:alg是指说用到的算法,type当然是令牌类型

有效荷载:sub所签发的用户,name是签发者的姓名,lat是这签发时间,exp是指到期时间,当然还有一些其他的,这些数据都是非必要数据,实则只有exp可能有用,因为有效数据实际都是在data里面,当然你也可以不这么做。

签名:前两者都是通过base64url编码过的,而非是算法加密的,所以几乎是透明的。但是签名是默认是通过hsa256算法加密的 ,加密的规则是:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

如何验证token呢?那更加简单了,就是用前端传回来的token前两部分和服务器存的秘钥再次加密一次,然后做base64url处理和第三部分比较,一样代表这个token是自己签发的,不一样代表是伪造的,完了过后再将有效载荷部分进行base64url反编码,就得到exp,然后和当前时间比较是否过期。

jwt未能解决的疑惑

经过测试,发现三个问题。

1、 node和java使用对应的jwt工具包生成的token不相同,这里是指在一系列参数完全相同的情况下。

2、 还发现用java生成的token在jwt官网解析时候输入秘钥结果的到的签名和自己的签名不一样,但是node是一样的,于是就产生了java工具包在输入签名的时候对密钥进行了其他处理的想法,但是他并没有找出做出来怎么样的处理,也就是说我们在生成签名的时候根本就连密钥都是不一样的。

3、 不管是node还是java生成的token,我们用原来一模一样的数据和一样的算法公式都产生不了和工具包生成一样的签名,也就是可以怀疑在进行hsa256算法加密前确实对秘钥进行处理了。

base64和base64url

base64就是一种二进制编码方式,原理很简单,先准备一个包含64个字符的数组:

['A','B','C',...'a','b','c',...'0','1','2',...'+','/']

然后对二进制数据进行处理,每三个字节一组,一共24bit,一个字节8bit嘛,然后再将24分为4组,每组正好6bit。6bit的话就刚好能表示64个字符。如果编码字符不是3的倍数,就会剩下一个字节或者两个字节,这个时候就在后面填充\x00,再在编码末尾加上一个或者两个=号。解码的时候制动力去掉就好了。

base64url编码就是将字符编码成url能传递的参数,因为base64编码会出现+号和/号,然后就会在url中出现问题,所以base64url其实就是将+和/分别变成-和_

posted @ 2019-11-13 17:36  __松子  阅读(802)  评论(2编辑  收藏  举报