express框架中使用jwt实现验证的方法


Posted in Javascript onAugust 25, 2019

前言

接着上遍文章(使用session保存用户数据)来让使用jwt保存用户数据。

这里会用到passport-jwt/jsonwebtoken。

passport-jwt是passport的一个验证策略。它使用jwt(json web token)验证。

jsonwebtoken是一个编码、解码、验证jwt的模块。

使用jwt保存用户数据与使用session保存用户数据对比

session json web token
保存在server 保存在client

因session保存在server,所以服务器压力比较大。听说并发量达到1k时就能看到效果。

因jwt保存在client,所以需要加密。

使用jwt

1. 安装依赖。

npm i passport-jwt jsonwebtoken

2. 创建一个配置文件,引用配置是使用。

// ./config.js
module.exports = {
 secretKey: '12345-67890-9876-54321',
 mongoUrl: 'mongodb://localhost:27017/confusion'
}

3. 使用数据库链接配置

var config = require('./config')
...
const url = config.mongoUrl
const connet = mongoose.connect(url, {useNewUrlParse: true, useCreateIndex: true})

4. 创建验证文件

./authenticate.js
var passport = require('passport'),
 LocalStrategy = require('passport-local').Strategy,
 User = require('./models/user')

var JwtStrategy = require('passport-jwt').Strategy,
 ExtractJwt = require('passport-jwt').ExtractJwt,
 jwt = require('jsonwebtoken')

var config = require('./config.js')

passport.use(new LocalStrategy(User.authenticate()))
passport.serializeUser(User.serializeUser())
passport.deserializeUser(User.deserializeUser())

exports.getToken = function (user) {
 return jwt.sign(user, config.secretKey, {expiresIn: 3600}) // 签发token时设置超时时间是3600s
}

var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken() // 从验证头中提取,模型默认是`'bearer'`.
opts.secretOrKey = config.secretKey

exports.jwtPassport = passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
 console.log('JWT payload: ', jwt_payload)
 User.findOne({_id: jwt_payload._id}, (err, user) => {
 if (err) {
 return done(err, false)
 } else {
 if (user) {
 return done(null, user)
 } else {
 return done(null, false)
 }
 }
 })
}))

exports.verifyUser = passport.authenticate('jwt', {session: false}) // 使用jwt就不再需要session保存用户数据了。

5. 用户申请登录时把jwt给前端

// routes/users.js
...
var authenticate = require('../authticate')
router.post('/login', passport.authenticate('local'), (req, res) => { // 登录时还是使用passport-local
 var token = authenticate.getToken({_id: req.user._id}) // 得到签发后的jwt
 res.statusCode = 200
 res.setHeader('Content-Type', 'application/json')
 res.json({success: true, token: token, status: 'You are successful logged in!'})
})

6. 前端保存token

// use localStorage
$.ajax({
 type: 'post',
 dataType: 'json',
 url: 'users/login',
 data: {
 username: 'un',
 password: 'pw'
 },
 success: funciton (res) {
 localStorage.token = getToken(res)
 },
 error: funciton (err) {...}
})
// 还可以使用vux方法。
// 还可以使用封装axios方法。

7. 用户登录超时
jsonwebtoken验证jwt后,若结果不通过,会有3种错误类型。分别是

TokenExpiredError // 当token超时时抛出。

err = {
 name: 'TokenExpiredError',
 massage: 'jwt expired',
 expired: [ExpDate]
}
JsonWebTokenError

jwt错误

err = {
 name: 'JsonWebTokenError',
 message: 'jwt malformed' // 'jwt malformed', 'jwt signature in required', 'invalid signature', 'jwt audience invalid. expected: [OPTIONS AUDIENCE]', 'jwt issuer invalid. expected: [OPTIONS ISSUER]', 'jwt id invalid. expected:[OPTIONS JWT ID]', 'jwt subject invalid. expected: [OPTIONS SUBJECT]'
}

NotBeforeError

当当前时间超过nbf的值时抛出该错误。

err = {
 name: 'NotBeforeError',
 message: 'jwt not active',
 date: 2018-10-04T16:10:44.000Z
}

passport在验证jwt不通过时(token过期也是一种不通过)自动向前端发送“状态码为401,内容是Unauthorized”.
在使用passport/passport-jwt/jsonwebtoken时没有发现处理token过期的方法。所以在使用passport-jwt验证不通过时再写一个验证是否过期的方法。

// authenicate.js
...
export.verifyUser = passport.authenticate('jwt', {
 session: false,
 failureRedirect: '/error/auth' // 在这个路由里统一处理验证不通过的事情
 })
// routes/error.js
...
router.get('/auth', (req, res, next) => {
 let header = req.headers
 let rawToken = header.authorization
 if (!rawToken.split(' ').length) {
 res.json({ // 统一的数据结构方便前端使用
 code: 403,
 data: {},
 message: 'error for get token'
 })
 } else {
 let token = rawToken.split(' ')[1]
 jwt.verify(token, config.secretKey, err => { // 这里用到jsonwebtoken/config。注意引用
 switch (err.name) {
 case 'TokenExpiredError':
 case 'NotBeforeError':
  let payload = jwt.decode(token)
  token = authenticate.getToken({_id: payload._id})
  res.statusCode = 200
  res.setHeader('Content-Type', 'application/json')
  res.json({success: true, token: token, status: '已经刷新token'})
  break
 case 'JsonWebTokenError':
 default:
  res.statusCode = 401
  res.json({
  code: 401,
  data: {
  error: err
  },
  message: 'token错误'
  })
  break
 }
 })
 }
 })

8. 用户jwt验证不通过

passport在验证jwt不通过时(token过期也是一种不通过)自动向前端发送“状态码为401,内容是Unauthorized”.

9. 用户申请登出

在前端删除token.

10. 不要打断活动用户的操作

在no.7里若因为token过期造成验证不通过,则向前端返回了新的token。不是在不影响用户操作前提下更新用户的token的。下面在的总结的几种不影响用户操作的前提下更新用户的token的方法。

  1. 前端设置一个定时器。在小于过期时间时向后端请求新token并保存起来。
  2. 把token放在cookie时。后端从cookie里取出token,在过期前更新token。
  3. 将 token 存入 DB(如 Redis)中,失效则删除;但增加了一个每次校验时候都要先从 DB 中查询 token 是否存在的步骤,而且违背了 JWT 的无状态原则(这不就和 session 一样了么?)。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
发一个自己用JS写的实用看图工具实现代码
Jul 26 Javascript
js DOM的学习笔记
Dec 22 Javascript
JS实现拖动示例代码
Nov 01 Javascript
对于Form表单reset方法的新认识
Mar 05 Javascript
让人蛋疼的JavaScript语法特性
Sep 30 Javascript
在JavaScript中正确引用bind方法的应用
May 11 Javascript
JQuery中DOM实现事件移除的方法
Jun 13 Javascript
深入理解关于javascript中apply()和call()方法的区别
Apr 12 Javascript
老生常谈javascript的面向对象思想
Aug 22 Javascript
Vue render渲染时间戳转时间,时间转时间戳及渲染进度条效果
Jul 27 Javascript
在vue项目中,将juery设置为全局变量的方法
Sep 25 Javascript
vue 项目软键盘回车触发搜索事件
Sep 09 Javascript
JS异步处理的进化史深入讲解
Aug 25 #Javascript
Vue源码分析之Vue实例初始化详解
Aug 25 #Javascript
javascript导出csv文件(excel)的方法示例
Aug 25 #Javascript
JavaScript在web自动化测试中的作用示例详解
Aug 25 #Javascript
angularjs自定义过滤器demo示例
Aug 24 #Javascript
Jquery实现获取子元素的方法分析
Aug 24 #jQuery
微信小程序class封装http代码实例
Aug 24 #Javascript
You might like
php set_time_limit(0) 设置程序执行时间的函数
2010/05/26 PHP
用Laravel Sms实现laravel短信验证码的发送的实现
2018/11/29 PHP
浅谈laravel orm 中的一对多关系 hasMany
2019/10/21 PHP
CentOS7系统搭建LAMP及更新PHP版本操作详解
2020/03/26 PHP
js 省地市级联选择
2010/02/07 Javascript
jquery 使用点滴函数代码
2011/05/20 Javascript
node.js 一个简单的页面输出实现代码
2012/03/07 Javascript
html+css+js实现xp window界面及有关功能
2013/03/26 Javascript
可简单避免的三个JS发布错误的详细介绍
2013/08/02 Javascript
Js获取数组最大和最小值示例代码
2013/10/29 Javascript
js模拟点击以提交表单为例兼容主流浏览器
2013/11/29 Javascript
jquery批量设置属性readonly和disabled的方法
2014/01/24 Javascript
javascript中声明函数的方法及调用函数的返回值
2014/07/22 Javascript
不使用ajax实现无刷新提交表单
2014/12/21 Javascript
Javascript中的Prototype到底是什么
2016/02/16 Javascript
js实现导航跟随效果
2018/11/17 Javascript
从零撸一个pc端vue的ui组件库( 计数器组件 )
2019/08/08 Javascript
[02:54]DOTA2亚洲邀请赛 VG战队出场宣传片
2015/02/07 DOTA
进一步了解Python中的XML 工具
2015/04/13 Python
Python 3.x 新特性及10大变化
2015/06/12 Python
Python的包管理器pip更换软件源的方法详解
2016/06/20 Python
微信跳一跳辅助python代码实现
2018/01/05 Python
python try 异常处理(史上最全)
2019/03/07 Python
Python图像处理之图片文字识别功能(OCR)
2019/07/30 Python
Python自动化办公Excel模块openpyxl原理及用法解析
2020/11/05 Python
python爬虫看看虎牙女主播中谁最“顶”步骤详解
2020/12/01 Python
Python 实现劳拉游戏的实例代码(四连环、重力四子棋)
2021/03/03 Python
HTML5在IE10、火狐下中文乱码问题的解决方法
2013/11/18 HTML / CSS
Reebok俄罗斯官方网上商店:购买锐步运动服装和鞋子
2016/09/26 全球购物
英国婴儿产品专家:Samuel Johnston
2020/04/20 全球购物
Linux上比较文件的命令都有哪些
2012/02/24 面试题
行政管理毕业生自荐信
2014/02/24 职场文书
体育教学随笔感言
2014/02/24 职场文书
2014年教师德育工作总结
2014/11/10 职场文书
刑事法律意见书
2015/06/04 职场文书
Java 超详细讲解ThreadLocal类的使用
2022/04/07 Java/Android