经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Node.js » 查看文章
express框架中使用jwt实现验证的方法
来源:jb51  时间:2019/8/26 8:25:19  对本文有异议

前言

接着上遍文章(使用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. 安装依赖。

  1. npm i passport-jwt jsonwebtoken

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

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

3. 使用数据库链接配置

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

4. 创建验证文件

  1. ./authenticate.js
  2. var passport = require('passport'),
  3. LocalStrategy = require('passport-local').Strategy,
  4. User = require('./models/user')
  5.  
  6. var JwtStrategy = require('passport-jwt').Strategy,
  7. ExtractJwt = require('passport-jwt').ExtractJwt,
  8. jwt = require('jsonwebtoken')
  9.  
  10. var config = require('./config.js')
  11.  
  12. passport.use(new LocalStrategy(User.authenticate()))
  13. passport.serializeUser(User.serializeUser())
  14. passport.deserializeUser(User.deserializeUser())
  15.  
  16. exports.getToken = function (user) {
  17. return jwt.sign(user, config.secretKey, {expiresIn: 3600}) // 签发token时设置超时时间是3600s
  18. }
  19.  
  20. var opts = {}
  21. opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken() // 从验证头中提取,模型默认是`'bearer'`.
  22. opts.secretOrKey = config.secretKey
  23.  
  24. exports.jwtPassport = passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
  25. console.log('JWT payload: ', jwt_payload)
  26. User.findOne({_id: jwt_payload._id}, (err, user) => {
  27. if (err) {
  28. return done(err, false)
  29. } else {
  30. if (user) {
  31. return done(null, user)
  32. } else {
  33. return done(null, false)
  34. }
  35. }
  36. })
  37. }))
  38.  
  39. exports.verifyUser = passport.authenticate('jwt', {session: false}) // 使用jwt就不再需要session保存用户数据了。

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

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

6. 前端保存token

  1. // use localStorage
  2. $.ajax({
  3. type: 'post',
  4. dataType: 'json',
  5. url: 'users/login',
  6. data: {
  7. username: 'un',
  8. password: 'pw'
  9. },
  10. success: funciton (res) {
  11. localStorage.token = getToken(res)
  12. },
  13. error: funciton (err) {...}
  14. })
  15. // 还可以使用vux方法。
  16. // 还可以使用封装axios方法。

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

TokenExpiredError // 当token超时时抛出。

  1. err = {
  2. name: 'TokenExpiredError',
  3. massage: 'jwt expired',
  4. expired: [ExpDate]
  5. }
  6. JsonWebTokenError

jwt错误

  1. err = {
  2. name: 'JsonWebTokenError',
  3. 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]'
  4. }

NotBeforeError

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

  1. err = {
  2. name: 'NotBeforeError',
  3. message: 'jwt not active',
  4. date: 2018-10-04T16:10:44.000Z
  5. }

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

  1. // authenicate.js
  2. ...
  3. export.verifyUser = passport.authenticate('jwt', {
  4. session: false,
  5. failureRedirect: '/error/auth' // 在这个路由里统一处理验证不通过的事情
  6. })
  1. // routes/error.js
  2. ...
  3. router.get('/auth', (req, res, next) => {
  4. let header = req.headers
  5. let rawToken = header.authorization
  6. if (!rawToken.split(' ').length) {
  7. res.json({ // 统一的数据结构方便前端使用
  8. code: 403,
  9. data: {},
  10. message: 'error for get token'
  11. })
  12. } else {
  13. let token = rawToken.split(' ')[1]
  14. jwt.verify(token, config.secretKey, err => { // 这里用到jsonwebtoken/config。注意引用
  15. switch (err.name) {
  16. case 'TokenExpiredError':
  17. case 'NotBeforeError':
  18. let payload = jwt.decode(token)
  19. token = authenticate.getToken({_id: payload._id})
  20. res.statusCode = 200
  21. res.setHeader('Content-Type', 'application/json')
  22. res.json({success: true, token: token, status: '已经刷新token'})
  23. break
  24. case 'JsonWebTokenError':
  25. default:
  26. res.statusCode = 401
  27. res.json({
  28. code: 401,
  29. data: {
  30. error: err
  31. },
  32. message: 'token错误'
  33. })
  34. break
  35. }
  36. })
  37. }
  38. })

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 一样了么?)。

总结

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号