Node.JS如何实现JWT原理


Posted in Javascript onSeptember 18, 2020

1.为什么需要会话管理

我们用nodejs为前端或者其他服务提供resful接口时,http协议他是一个无状态的协议,有时候我们需要根据这个请求的上下获取具体的用户是否有权限,针对用户的上下文进行操作。所以出现了cookies session还有jwt这几种技术的出现, 都是对HTTP协议的一个补充。使得我们可以用HTTP协议+状态管理构建一个的面向用户的WEB应用。

2.session和cookies

session和cookies是有联系的,session就是服务端在客户端cookies种下的session_id, 服务端保存session_id所对应的当前用户所有的状态信息。每次客户端请求服务端都带上cookies中的session_id, 服务端判断是否有具体的用户信息,如果没有就去调整登录。

  • cookies安全性不好,攻击者可以通过获取本地cookies进行欺骗或者利用cookies进行CSRF攻击。
  • cookies在多个域名下,会存在跨域问题
  • session的信息是保存在服务端上面的,当我们node.js在stke部署多台机器的时候,需要解决共享session,所以引出来session持久化问题,所以session不支持分布式架构,无法支持横向扩展,只能通过数据库来保存会话数据实现共享。如果持久层失败会出现认证失败。

3.jwt的定义

jwt是json web token的全称,他解决了session以上的问题,优点是服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展,什么情况下使用jwt比较合适,我觉得就是授权这个场景,因为jwt使用起来轻便,开销小,后端无状态,所以使用比较广泛。

4.jwt的原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。

{
 "姓名": "张三",
 "角色": "管理员",
 "到期时间": "2018年7月1日0点0分"
}

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。

5.jwt的认证流程

流程说明:

  1. 浏览器发起请求登陆,携带用户名和密码;
  2. 服务端根据用户名和明码到数据库验证身份,根据算法,将用户标识符打包生成 token,
  3. 服务器返回JWT信息给浏览器,JWT不应该包含敏感信息,这是很重要的一点
  4. 浏览器发起请求获取用户资料,把刚刚拿到的 token一起发送给服务器,一般放在header里面,字段为authorization
  5. 服务器发现数据中有 token,decode token的信息,然后再次签名,验明正身;
  6. 服务器返回该用户的用户资料;
  7. 服务器可以在payload设置过期时间, 如果过期了,可以让客户端重新发起验证。

6.jwt的数据结构

jwt包含了使用.风格的三个部分

{ "alg": "HS256", "typ": "JWT"}  
// algorithm => HMAC SHA256
// type => JWT

这是固定的写法,alg表面要用的是HS256算法

Payload 负载、载荷,JWT 规定了7个官方字段

iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

除了这七个,可以自定义,比如过期时间

Signature 签名

对前两部分header和payload进行签名,防止数据篡改

HMACSHA256(
 base64UrlEncode(header) + "." +
 base64UrlEncode(payload),
 secret)

secret是一段字符串,后端保存,需要注意的是JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

7.jwt使用方式

HTTP 请求的头信息Authorization字段里面, Bearer也是规定好的

Authorization: Bearer <token>

通过url传输(不推荐)

http://www.xxx.com/pwa?token=xxxxx

如果是post请求也可以放在请求体中

8.在koa项目中使用

可以使用现成库,jwt-simple 或者 jsonwebtoken

let Koa = require('koa');
let Router = require('koa-router');
let bodyparser = require('koa-bodyparser');
let jwt = require('jwt-simple');
let router = new Router()
let app = new Koa();
app.use(bodyparser());
// 可以自己自定义
let secret = 'zhenglei';
// 验证是否登陆
router.post('/login',async(ctx)=>{ 
  let {username,password} = ctx.request.body;
  if(username === 'admin' && password === 'admin'){
    // 通常会查数据库,这里简单的演示
    let token = jwt.encode(username, secret);
    ctx.body = {
      code:200,
      username,
      token,
    }
  }
});
// 验证是否有权限
router.get('/validate',async(ctx)=>{ 
  let Authorization = ctx.get('authorization')
  let [,token] = Authorization.split(' ');
  if(token){
    try{
      let r = jwt.decode(token,secret);
      ctx.body = {
        code:200,
        username:r,
        token
      }
    }catch(e){
      ctx.body = {
        code:401,
        data:'没有登陆'
      }
    }
  }else{
    ctx.body = {
      code:401,
      data:'没有登陆'
    }
  }
 
});
app.use(router.routes());
app.listen(4000);
  1. 实现两个接口 一个是/login 验证是否登录,一个是 validate,验证是否有权限
  2. 请求login接口的时候,客户端带username和password, 后端一般会查数据库,验证是否存在当前用户,如果存在则为username进行签名,千万不要给password这些敏感信息也带进来签名
  3. 客户端接收后端给的token令牌,再请求其他接口,比如这个例子的/validate的时候,ajax请求的时候,可以在header指定authorization字段,后端拿到token进行decode,然后将header和payload进行再一次的签名,如果前后的签名一致,说明没有被篡改过,则权限验证通过。因为是同步的过程,所以可以用try catch来捕捉错误

9.原理的实现

  1. sha256哈希算法,可以用nodejs的内置加密模块crypto, 生成base64字符串,要注意的是生成base64需要为+ - = 做一下替换,=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。
  2. token令牌的组成是header, payload和sigin的通过.来组成
  3. base64urlUnescape的解码是固定写法,decode出base64的内容
let myJwt = {
  sign(content,secret){
    let r = crypto.createHmac('sha256',secret).update(content).digest('base64');
    return this.base64urlEscape(r)
  },
  base64urlEscape(str){
    return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
  },
  toBase64(content){
    return this.base64urlEscape(Buffer.from(JSON.stringify(content)).toString('base64'))
  },
  encode(username,secret){
    let header = this.toBase64({ typ: 'JWT', alg: 'HS256' });
    let content = this.toBase64(username);
    let sign = this.sign([header,content].join('.'),secret);
    return [header,content,sign].join('.')
  },
  base64urlUnescape(str) {
    str += new Array(5 - str.length % 4).join('=');
    return str.replace(/\-/g, '+').replace(/_/g, '/');
  },
  decode(token,secret){
    let [header,content,sign] = token.split('.');
    let newSign = this.sign([header,content].join('.'),secret);
    if(sign === newSign){
      return Buffer.from(this.base64urlUnescape(content),'base64').toString();
    }else{
      throw new Error('被篡改')
    }
  }
}

10.jwt的优缺点

  1. JWT默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密。
  2. 当JWT未加密方法是,一些私密数据无法通过JWT传输。
  3. JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。
  4. JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。
  5. JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。
  6. 为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。

以上就是Node.JS如何实现JWT原理的详细内容,更多关于Node.JS 实现JWT原理的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
asp.net刷新本页面的六种方法总结
Jan 07 Javascript
js判断上传文件类型判断FileUpload文件类型代码
May 20 Javascript
jQuery Raty 一款不错的星级评分插件
Aug 24 Javascript
详解Vue中过度动画效果应用
May 25 Javascript
小发现之浅谈location.search与location.hash的问题
Jun 23 Javascript
ReactNative Image组件使用详解
Aug 07 Javascript
JavaScript动态加载重复绑定问题
Apr 01 Javascript
vue采用EventBus实现跨组件通信及注意事项小结
Jun 14 Javascript
js限制input只能输入有效的数字(第一个不能是小数点)
Sep 28 Javascript
一个Java程序猿眼中的前后端分离以及Vue.js入门(推荐)
Apr 19 Javascript
npm qs模块使用详解
Feb 07 Javascript
html+vue.js 实现漂亮分页功能可兼容IE
Nov 07 Javascript
浏览器JavaScript调试功能无法使用解决方案
Sep 18 #Javascript
js将日期格式转换为YYYY-MM-DD HH:MM:SS
Sep 18 #Javascript
js实现手表表盘时钟与圆周运动
Sep 18 #Javascript
javascript实现智能手环时间显示
Sep 18 #Javascript
javascript实现打砖块小游戏(附完整源码)
Sep 18 #Javascript
js实现拖拽与碰撞检测
Sep 18 #Javascript
详解JavaScript 的执行机制
Sep 18 #Javascript
You might like
在PHP中执行系统外部命令
2006/10/09 PHP
php流量统计功能的实现代码
2012/09/29 PHP
php使用curl获取header检测开启GZip压缩的方法
2018/08/15 PHP
php反序列化长度变化尾部字符串逃逸(0CTF-2016-piapiapia)
2020/02/15 PHP
尽可能写&quot;友好&quot;的&quot;Javascript&quot;代码
2007/01/09 Javascript
JS判断客户端是手机还是PC的2个代码
2014/04/12 Javascript
c#+jquery实现获取radio和checkbox的值
2020/09/12 Javascript
jQuery中[attribute]选择器用法实例
2014/12/31 Javascript
基于JavaScript实现树形下拉框
2016/08/10 Javascript
打字效果动画的4种实现方法(超简单)
2017/10/18 Javascript
JavaScript自动生成 年月范围 选择功能完整示例【基于jQuery插件】
2019/09/03 jQuery
微信小程序云函数添加数据到数据库的方法
2020/03/04 Javascript
JavaScript 判断数据类型的4种方法
2020/09/11 Javascript
[09:43]DOTA2每周TOP10 精彩击杀集锦vol.5
2014/06/25 DOTA
[01:07:47]Secret vs Optic Supermajor 胜者组 BO3 第一场 6.4
2018/06/05 DOTA
Python脚本暴力破解栅栏密码
2015/10/19 Python
python动态加载包的方法小结
2016/04/18 Python
python搭建虚拟环境的步骤详解
2016/09/27 Python
Python跨文件全局变量的实现方法示例
2017/12/10 Python
Python sklearn KFold 生成交叉验证数据集的方法
2018/12/11 Python
Python 3.x基于Xml数据的Http请求方法
2018/12/28 Python
python 通过麦克风录音 生成wav文件的方法
2019/01/09 Python
详解Python3网络爬虫(二):利用urllib.urlopen向有道翻译发送数据获得翻译结果
2019/05/07 Python
如何更优雅地写python代码
2019/07/02 Python
安装docker-compose的两种最简方法
2019/07/30 Python
Python转换itertools.chain对象为数组的方法
2020/02/07 Python
基于python实现微信好友数据分析(简单)
2020/02/16 Python
Pycharm远程连接服务器并实现代码同步上传更新功能
2020/02/25 Python
jupyter notebook 重装教程
2020/04/16 Python
python 实现分组求和与分组累加求和代码
2020/05/18 Python
互动出版网:专业书籍
2017/03/21 全球购物
采购内勤岗位职责
2013/12/10 职场文书
马智宇婚礼主持词
2014/03/22 职场文书
2015个人半年总结范文
2015/03/09 职场文书
党员反四风学习心得体会
2016/01/22 职场文书
四则混合运算教学反思
2016/02/23 职场文书