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 backgroundImage控制
May 19 Javascript
动态的绑定事件addEventListener方法的使用
Jan 24 Javascript
document.forms用法示例介绍
Jun 26 Javascript
javascript中的Base64、UTF8编码与解码详解
Mar 18 Javascript
AngularJS实现树形结构(ztree)菜单示例代码
Sep 18 Javascript
jQuery 表单序列化实例代码
Jun 11 jQuery
原生js实现拖拽功能基本思路详解
Apr 18 Javascript
JavaScript中七种流行的开源机器学习框架
Oct 11 Javascript
es6 symbol的实现方法示例
Apr 02 Javascript
使用vscode快速建立vue模板过程详解
Oct 10 Javascript
js new Date()实例测试
Oct 31 Javascript
JavaScript内置对象之Array的使用小结
May 12 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
与空气斗智斗勇的经典《Overlord》,传说中的“无稽之谈”
2020/04/09 日漫
PHP PDOStatement对象bindpram()、bindvalue()和bindcolumn之间的区别
2014/11/20 PHP
PHP数据库表操作的封装类及用法实例详解
2016/07/12 PHP
Laravel框架实现redis集群的方法分析
2017/09/14 PHP
Javascript类定义语法,私有成员、受保护成员、静态成员等介绍
2011/12/08 Javascript
创建js对象和js类的方法汇总
2014/12/24 Javascript
简介JavaScript中Math.cos()余弦方法的使用
2015/06/15 Javascript
基于BootStrap Metronic开发框架经验小结【一】框架总览及菜单模块的处理
2016/05/12 Javascript
jQuery中通过ajax调用webservice传递数组参数的问题实例详解
2016/05/20 Javascript
JavaScript组合模式学习要点
2016/08/26 Javascript
AngularJS实现动态编译添加到dom中的方法
2016/11/04 Javascript
js获取地址栏中传递的参数(两种方法)
2017/02/08 Javascript
js实现移动端吸顶效果
2020/01/08 Javascript
vue中的使用token的方法示例
2020/03/10 Javascript
[01:28]国服启动器接入蒸汽平台操作流程视频
2021/03/11 DOTA
[05:08]DOTA2-DPC中国联赛3月6日Recap集锦
2021/03/11 DOTA
Python的动态重新封装的教程
2015/04/11 Python
Python中死锁的形成示例及死锁情况的防止
2016/06/14 Python
Python实现模拟分割大文件及多线程处理的方法
2017/10/10 Python
Django教程笔记之中间件middleware详解
2018/08/01 Python
基于Python的Post请求数据爬取的方法详解
2019/06/14 Python
python买卖股票的最佳时机(基于贪心/蛮力算法)
2019/07/05 Python
从训练好的tensorflow模型中打印训练变量实例
2020/01/20 Python
python分别打包出32位和64位应用程序
2020/02/18 Python
Django通过json格式收集主机信息
2020/05/29 Python
浅谈Python中的继承
2020/06/19 Python
Python logging日志模块 配置文件方式
2020/07/12 Python
python中字符串的编码与解码详析
2020/12/03 Python
CSS3教程(4):网页边框和网页文字阴影
2009/04/02 HTML / CSS
“型”走纽约上东区:Sam Edelman
2017/04/02 全球购物
小班重阳节活动方案
2014/02/08 职场文书
《在家里》教后反思
2014/03/01 职场文书
原材料检验岗位职责
2014/03/15 职场文书
感恩老师主题班会
2015/08/12 职场文书
2016优秀教师先进个人事迹材料
2016/02/25 职场文书
分布式锁为什么要选择Zookeeper而不是Redis?看完这篇你就明白了
2021/05/21 Redis