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 相关文章推荐
JavaScript设置首页和收藏页面的小例子
Nov 11 Javascript
javascript列表框操作函数集合汇总
Nov 28 Javascript
一个简单的jquery的多选下拉框(自写)
May 05 Javascript
jQuery中:checkbox选择器用法实例
Jan 03 Javascript
AngularJS中监视Scope变量以及外部调用Scope方法
Jan 23 Javascript
Vue.js基础知识汇总
Apr 27 Javascript
html、css和jquery相结合实现简单的进度条效果实例代码
Oct 24 Javascript
js前端解决跨域问题的8种方案(最新最全)
Nov 18 Javascript
微信小程序开发之好友列表字母列表跳转对应位置
Sep 26 Javascript
vue 实现购物车总价计算
Nov 06 Javascript
JavaScript ECMA-262-3 深入解析(一):执行上下文实例分析
Apr 25 Javascript
JavaScript实现复选框全选功能
Apr 11 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
如何去掉文章里的 html 语法
2006/10/09 PHP
C# Assembly类访问程序集信息
2009/06/13 PHP
基于Snoopy的PHP近似完美获取网站编码的代码
2011/10/23 PHP
解析PHP提交后跳转
2013/06/23 PHP
php实现图形显示Ip地址的代码及注释
2014/01/20 PHP
php readfile()修改文件上传大小设置
2017/08/11 PHP
给moz-firefox下添加IE方法和属性
2007/04/10 Javascript
JavaScript小技巧 2.5 则
2010/09/12 Javascript
jQuery EasyUI API 中文文档 - Menu菜单
2011/10/03 Javascript
JavaScript实现GriwView单列全选(自写代码)
2013/05/13 Javascript
常见表单重复提交问题整理及解决方法
2013/11/13 Javascript
vue.js学习之vue-cli定制脚手架详解
2017/07/02 Javascript
微信小程序scroll-view横向滑动嵌套for循环的示例代码
2018/09/20 Javascript
微信小程序使用setData修改数组中单个对象的方法分析
2018/12/30 Javascript
VUE+Element环境搭建与安装的方法步骤
2019/01/24 Javascript
详解关于element级联选择器数据回显问题
2019/02/20 Javascript
python类型强制转换long to int的代码
2013/02/10 Python
Python中生成器和yield语句的用法详解
2015/04/17 Python
windows下python安装paramiko模块和pycrypto模块(简单三步)
2017/07/06 Python
pandas获取groupby分组里最大值所在的行方法
2018/04/20 Python
python 为什么说eval要慎用
2019/03/26 Python
python切片的步进、添加、连接简单操作示例
2019/07/11 Python
NumPy中的维度Axis详解
2019/11/26 Python
通过Python实现一个简单的html页面
2020/05/16 Python
HTML5之SVG 2D入门3—文本与图像及渲染文本介绍
2013/01/30 HTML / CSS
HTML5新特性之语义化标签
2017/10/31 HTML / CSS
印度尼西亚最大的电商平台:Tokopedia(印尼版淘宝)
2017/12/02 全球购物
加拿大约会网站:EliteSingles.ca
2018/01/12 全球购物
饿了么订餐官网:外卖、网上订餐
2019/06/28 全球购物
JACK & JONES荷兰官网:男士服装和鞋子
2021/03/07 全球购物
教师求职信范文
2014/05/24 职场文书
机械制造专业大学生自我鉴定
2014/09/19 职场文书
巾帼文明岗汇报材料
2014/12/24 职场文书
学校隐患排查制度
2015/08/05 职场文书
vue组件的路由高亮问题解决方法
2021/05/11 Vue.js
20180830晚上第一届KSL半决赛 雨神vs解冻(二龙 三炮解说)
2022/04/01 星际争霸